1 (изменено: yuriy2000, 2012-11-17 19:12:54)

Тема: VBS: Глюк ADODB.Stream?

Случайно столкнулся с неприятным глюком при использовании ADODB.Stream:
для конвертации из кодировки UTF-8 в Windows-1251 использую функцию из копилки идей

При чтении из файла  в кодировке UTF-8, содержащего символы определяющие денежные единицы, например символ Йены (код в таблице Unicode U+00A5) или анг.фунта (U+00A3), при условии что не установлена опция On Error Resume Next получаем ошибку

Ошибка выполнения Microsoft VBScript: Недопустимый вызов или аргумент процедуры

Точнее ошибка возникает не при чтении, а при дальнейшем использовании строки считанной из файла.

Я написал тестовый пример (см. ниже), который создает строки в формате UTF-8, содержащие символы C2A0 ... C2BF (символы Unicode  U+00A0...U+00BF) и потом их конвертирует в Win-1251 - в результате  получается,  что создаются не пустыми лишь некоторые файлы.
U+00A0 U+00A4 U+00A6 U+00A7 U+00A9 U+00AB U+00AC U+00AD U+00AE U+00B0 U+00B1 U+00B5 U+00B6 U+00B7 U+00BB.
Остальные создаются пустыми.
Парадокс: Если конвертировать UTF-8 в кодовую таблицу 866, то таких проблем не возникает. Некоторые символы просто не перекодируются  и всё - т.е. файлы в 866-кодировке в тестовом примере получаются размером 2 либо 3 байта.

Кто-нибудь из гуру может прокомментировать такое странное поведение ADODB.Stream?

PS: при использовании аналогичной функции из копилки с перекодировкой файла таких проблем не возникает


Option Explicit

Dim i

On Error Resume Next

for i=&HA0 to &HBF
    test i
next 'i
On Error GoTo 0
WScript.Quit


sub test(sAsc)
    Dim sBuf, oFileOut
    Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")  

    sBuf=chr(&HC2) & chr(sAsc) '

    set oFileOut = oFSO.CreateTextFile(hex(sAsc) & ".txt",True)
    'oFileOut.WriteLine StrConv(sBuf,"cp866","UTF-8")
    oFileOut.WriteLine StrConv(sBuf,"windows-1251","UTF-8")
    oFileOut.Close

end sub

Function StrConv(Text,SourceCharset,DestCharset)
    Dim Stream
    Set Stream = CreateObject("ADODB.Stream")
    Stream.Type = 2
    Stream.Mode = 3
    Stream.Open
    Stream.Charset = SourceCharset
    Stream.WriteText Text
    Stream.Position = 0
    Stream.Charset = DestCharset
    StrConv = Stream.ReadText
End Function

2

Re: VBS: Глюк ADODB.Stream?

Проблема не в ADO.DB, проблема в FSO - ошибка возникает не при работе со строкой, а при попытке её записать в файл методом WriteLine(достаточно запустить скрипт в отладчике).
Соответственно файл уже создан(пустой), записать не удалось. А так как на ошибки забиваем - идём дальше и получаем толпу пустых файлов. Возможно какой-то баг FSO при попытке писать UTF-8 в открытый как ASCII файл.

yuriy2000 пишет:

Я написал тестовый пример (см. ниже), который создает строки в формате UTF-8, содержащие символы C2A0 ... C2BF (символы Unicode  U+00A0...U+00BF) и потом их конвертирует в Win-1251

Вообще-то, пример делает наоборот
Чтобы конвертировать из UTF в cp1251 нужно аргументы поменять местами

oFileOut.WriteLine StrConv(sBuf,"UTF-8","windows-1251")

3

Re: VBS: Глюк ADODB.Stream?

касательно использования StrConv(sBuf,"UTF-8","windows-1251") - дискуссия была здесь:

http://forum.script-coding.com/viewtopic.php?id=1179

4

Re: VBS: Глюк ADODB.Stream?

Действительно...

Правда в свете этого:

Charset Property (ADO):Sets or returns a String value that specifies the character set into which the contents of the Stream will be translated. The default value is Unicode.

а также приведённого в MSDN примера, всё выглядит не очень логично(для меня), но на тестовом примере(чтение строки из файла в UTF-8, StrConv, запись в ASCII файл) это подтвердилось(похоже я в корне неверно понимаю работу с ado.stream ).

Однако, в Вашем случае если ADO и виноват, то лишь косвенно - ошибка возникает всё-таки после того как ADO своё отработал.

5

Re: VBS: Глюк ADODB.Stream?

BeS Yara пишет:

Действительно...

.....

Однако, в Вашем случае если ADO и виноват, то лишь косвенно - ошибка возникает всё-таки после того как ADO своё отработал.

Возможно, что проблема и  не ado.stream , но всё-таки что-то с ним не так, ведь если перекодировать в cp866, то проблем не возникает...

6

Re: VBS: Глюк ADODB.Stream?

Если посмотреть ASCII-код символов на выходе из потока, то можно обратить внимание на характерный факт: "ненулевые" файлы возникают для символов для которых vbscript выдаёт одинаковый код и через Asc и через AscW. Для части символов коды совпадают, для остальных Asc=63(вопросительный знак), и ошибка метода .WriteLine.

Такое впечатление, что проблемы нет только для символов имеющих "синоним" или дубль в US-ASCII. Навеяно вики(ASCII-Национальные варианты ASCII, первый абзац), но обосновать не могу - не по всем работающим символам в статье есть упоминание.

Полагаю, что пример(по работе напрямую с файлом) работает без проблем по той причине, что ADO.Stream читает и пишет файлы не посимвольно, а побайтово. А потому вся работа с кодировками выполняется в нём без лишних "прокладок".

В Вашем случае прибавляется vbscript со своими тараканами.

Всё ниже сказанное - великое колдунство, которое я сам до конца не понял(какая-то идея под коркой шевелиться, но сформулировать не получается).:)
Если заменить:


oFileOut.WriteLine StrConv(sBuf,"windows-1251","UTF-8")

на


oFileOut.WriteLine Chr(AscW(StrConv(sBuf,"windows-1251","UTF-8")))

то ошибка не возникает, файлы пишутся все.

P.S. буду премного благодарен, если найдётся голова, которая мне популярно пояснит что же я сделал и почему запись 161-го(например) символа в рассматриваемом скрипте(изначальном) даёт ошибку, а этот код нет:


option explicit
Dim f : set f = CreateObject("Scripting.FileSystemObject").CreateTextFile("ascii.txt",True)
f.Write Chr(161)

P.P.S. возможно таки ADO тут "при чём"...

7

Re: VBS: Глюк ADODB.Stream?

yuriy2000 пишет:

PS: при использовании аналогичной функции из копилки с перекодировкой файла таких проблем не возникает

Но эта функция вам не подходит? Тогда поясните, в чём конкретно ваша задача заключается, что вы пытаетесь сделать?

8

Re: VBS: Глюк ADODB.Stream?

YMP пишет:
yuriy2000 пишет:

PS: при использовании аналогичной функции из копилки с перекодировкой файла таких проблем не возникает

Но эта функция вам не подходит? Тогда поясните, в чём конкретно ваша задача заключается, что вы пытаетесь сделать?

Да, эта функция из копилки (с конвертацией всего файла за один проход) решила мои проблемы, в конкретном случае..., но дело в том, что хотелось бы конвертировать строки в памяти. В будущем...

Может быть совместными усилиями найдем решение проблемы с этой  StrConv.

9

Re: VBS: Глюк ADODB.Stream?

yuriy2000 пишет:

Я написал тестовый пример (см. ниже), который создает строки в формате UTF-8

Тогда вам нужно использовать функцию ChrB. У вас в примере создаётся строка из двух юникодных двухбайтных символов (UTF-16), а не один двухбайтный символ UTF-8.

Если читать файл UTF-8 через FSO как ANSI, то во время чтения происходит конвертация в UTF-16, поскольку таков формат строк в VBScript. Конвертация пойдёт по таблице 1251 и в переменной окажется нечитаемый набор символов.

Восстановить строку можно записью из переменной в Stream, кодировка которого 1251. При этом произойдёт конвертация уже из Юникода по той же 1251 таблице, т.е. процесс, обратный тому, который привёл к порче строки. После этого меняем кодировку Stream на UTF-8, чтобы она соответствовала содержимому и читаем снова в переменную, при этом происходит конвертация в Юникод, но уже по правильной таблице UTF-8. В итоге имеем в переменной читаемый текст в UTF-16.

При попытке сохранить его через FSO в файл в кодировке 1251 проблема возникает, судя по всему, из-за символов, отсутствующих в целевой кодовой странице, т.е. тех, которые были бы заменены на знак вопроса. Может, забота о том, чтобы у пользователя информация не терялась.

10

Re: VBS: Глюк ADODB.Stream?

Если всё же нужно записать такой текст через FSO в файл в 1251, пусть даже с заменой отсутствующих в ней символов на "?", то можно предварительно сделать так:


sBuf = StrConv(sBuf,"windows-1251","windows-1251")

Получим ту же строку UTF-16, но где неродные для 1251 символы уже заменены на знаки вопроса, так что FSO не будет протестовать.

11

Re: VBS: Глюк ADODB.Stream?

YMP пишет:



sBuf = StrConv(sBuf,"windows-1251","windows-1251")

Хм. Без комментариев похоже на шаманство.  Но ведь это действительно работает!
Кто бы мог подумать...

Огромное Спасибо!!!