Вывод VBA в файл с использованием UTF-16

У меня очень сложная проблема, которую трудно объяснить должным образом. В интернете много дискуссий по этому поводу, но ничего конкретного. Любая помощь или лучшее объяснение, чем мое, очень приветствуется.

По сути, я просто пытаюсь написать файл XML, используя UTF-16 с VBA.

Если я сделаю это:

sXML = "<?xml version='1.0' encoding='utf-8'?>"
sXML = sXML & rest_of_xml_document
Print #iFile, sXML

затем я получаю файл, который является допустимым XML. Однако, если я изменю "encoding=" на "utf-16", я получу эту ошибку от моего валидатора XML:

Switch from current encoding to specified encoding not supported.

Поиск в Google говорит мне, что это означает, что атрибут кодировки xml отличается от АКТУАЛЬНОЙ кодировки, используемой файлом, поэтому я должен создавать документ utf-8 с помощью команд Open и Print.

Если я сделаю что-то вроде:

With CreateObject("ADODB.Stream")
  .Type = 2
  .Charset = "utf-16"
  .Open
  .WriteText sXML
  .SaveToFile sFilename, 2
  .Close
End With

затем я получаю несколько странных символов (BOM) в начале моего файла, что приводит к сбою проверки XML.

Если я открою файл в Notepad++, удалю спецификацию и изменю кодировку на «UCS-2», то файл будет проверен со значением кодировки «utf-16» (это означает, что UCS-2 достаточно близок к UTF-16, что неважно, или что XML умеет Switch from current encoding между этими двумя типами.

Мне нужно использовать UTF-16, потому что UTF-8 не охватывает все символы, используемые в презентациях, которые я экспортирую.

Вопрос:

Как заставить VBA вести себя как Notepad++, создавая текстовый файл в кодировке UTF-16 без спецификации, который можно заполнить данными XML? ЛЮБАЯ помощь высоко ценится!


person Alex McMillan    schedule 01.02.2012    source источник
comment
Вы можете использовать .Charset = "utf-16le"?   -  person martin clayton    schedule 01.02.2012
comment
Никакой разницы :(   -  person Alex McMillan    schedule 01.02.2012
comment
Мне кажется, вы путаете некоторые понятия. Общие сведения см. на странице joelonsoftware.com/articles/Unicode.html.   -  person GSerg    schedule 01.02.2012
comment
Спасибо за этот GSerg - отличное чтение :)   -  person Alex McMillan    schedule 01.02.2012


Ответы (1)


Ваше замечание о том, что UTF-8 не может хранить все необходимые вам символы, неверно.
UTF-8 может хранить все символы, определенные в стандарте Unicode.
Единственное отличие состоит в том, что для текста на определенных языках , UTF-8 может занимать больше места для хранения своих кодовых точек, чем, скажем, UTF-16. Верно и обратное: для некоторых других языков, таких как английский, использование UTF-8 экономит пространство.

VB6 и VBA, хотя и хранят строки в памяти в Unicode, неявно переключаются на ANSI (используя текущую системную кодовую страницу) при выполнении файлового ввода-вывода. В результате вы получите файл НЕ в UTF-8. Это кодовая страница вашей текущей системы, которая, как вы можете узнать из этой полезной статьи, выглядит так же, как UTF-8, если вы из США.

Пытаться:

Dim s As String
s = "<?xml version='1.0' encoding='utf-16'?>"
s = s & ChrW$(&H43F&) & ChrW$(&H440&) & ChrW$(&H43E&) & ChrW$(&H432&) & ChrW$(&H435&) & ChrW$(&H440&) & ChrW$(&H43A&) & ChrW$(&H430&)

Dim b() As Byte
b = s

Open "Unicode.txt" For Binary Access Write As #1
Put #1, , b
Close #1

И если вам абсолютно необходимо иметь UTF-8, вы можете сделать это самостоятельно:

Option Explicit

Private Declare Function WideCharToMultiByte Lib "kernel32.dll" (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long, ByRef lpMultiByteStr As Byte, ByVal cchMultiByte As Long, ByVal lpDefaultChar As String, ByRef lpUsedDefaultChar As Long) As Long

Private Const CP_UTF8 As Long = 65001
Private Const ERROR_INSUFFICIENT_BUFFER As Long = 122&


Public Function ToUTF8(s As String) As Byte()

  If Len(s) = 0 Then Exit Function


  Dim ccb As Long
  ccb = WideCharToMultiByte(CP_UTF8, 0, StrPtr(s), Len(s), ByVal 0&, 0, vbNullString, ByVal 0&)

  If ccb = 0 Then
    Err.Raise 5, , "Internal error."
  End If

  Dim b() As Byte
  ReDim b(1 To ccb)

  If WideCharToMultiByte(CP_UTF8, 0, StrPtr(s), Len(s), b(LBound(b)), ccb, vbNullString, ByVal 0&) = 0 Then
    Err.Raise 5, , "Internal error."
  Else
    ToUTF8 = b
  End If

End Function
Sub Test()
  Dim s As String
  s = "<?xml version='1.0' encoding='utf-8'?>"
  s = s & ChrW$(&H43F&) & ChrW$(&H440&) & ChrW$(&H43E&) & ChrW$(&H432&) & ChrW$(&H435&) & ChrW$(&H440&) & ChrW$(&H43A&) & ChrW$(&H430&)

  Dim b() As Byte
  b = ToUTF8(s)

  Open "utf-8.txt" For Binary Access Write As #1
  Put #1, , b
  Close #1
End Sub
person GSerg    schedule 01.02.2012
comment
Если это так, то почему, когда я использую utf-8, XML не проходит проверку из-за недопустимых символов? Тот же XML проверяется при использовании utf-16... - person Alex McMillan; 02.02.2012
comment
@AlexMcMillan Как вы используете utf-8? Изменив «utf-16» на «utf-8» в приведенном выше фрагменте кода? - person GSerg; 02.02.2012
comment
Да, и я знаю, что это не меняет фактическую кодировку файла, но меняет то, как валидатор смотрит на символы. Например, я нахожу символ «» в каком-то тексте, который я помещаю в элемент XML. Если я устанавливаю кодировку utf-8, это не работает. Если я ставлю utf-16, все работает нормально. - person Alex McMillan; 02.02.2012
comment
@AlexMcMillan Дело не в валидаторе. Этот код записывает UTF-16, то есть не менее двух байтов на символ. Если вы затем скажете, что это UTF-8, то другой байт каждого символа интерпретируется как отдельный символ. Поскольку вы используете английский язык, этот другой байт обычно равен нулю, что приводит к нулевому символу, который не разрешен в XML. - person GSerg; 02.02.2012