Сохраните текстовый файл UTF-8 в кодировке VBA

Как я могу записать строки в кодировке UTF-8 в текстовый файл из VBA, как

Dim fnum As Integer
fnum = FreeFile
Open "myfile.txt" For Output As fnum
Print #fnum, "special characters: äöüß" 'latin-1 or something by default
Close fnum

Есть ли какие-то настройки на уровне приложения?

7 ответов

Решение

Я нашел ответ в Интернете:

Dim fsT As Object
Set fsT = CreateObject("ADODB.Stream")
fsT.Type = 2 'Specify stream type - we want To save text/string data.
fsT.Charset = "utf-8" 'Specify charset For the source text data.
fsT.Open 'Open the stream And write binary data To the object
fsT.WriteText "special characters: äöüß"
fsT.SaveToFile sFileName, 2 'Save binary data To disk

Конечно, не так, как я ожидал...

Вы можете использовать метод CreateTextFile или OpenTextFile, оба имеют атрибут "unicode", полезный для настроек кодирования.

object.CreateTextFile(filename[, overwrite[, unicode]])        
object.OpenTextFile(filename[, iomode[, create[, format]]])

Пример: перезаписать:

CreateTextFile:
 fileName = "filename"
 Set fso = CreateObject("Scripting.FileSystemObject")
 Set out = fso.CreateTextFile(fileName, True, True)
 out.WriteLine ("Hello world!")
 ...
 out.close

Пример: Добавить:

 OpenTextFile Set fso = CreateObject("Scripting.FileSystemObject")
 Set out = fso.OpenTextFile("filename", ForAppending, True, 1)
 out.Write "Hello world!"
 ...
 out.Close

Подробнее о документах MSDN

Это записывает метку порядка байтов в начале файла, которая не нужна в файле UTF-8, и некоторым приложениям (в моем случае, SAP) это не нравится. Решение здесь: Могу ли я экспортировать данные Excel с UTF-8 без спецификации?

Вот еще один способ сделать это - использовать функцию API WideCharToMultiByte:

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, _
  ByVal lpMultiByteStr As Long, _
  ByVal cbMultiByte As Long, _
  ByVal lpDefaultChar As Long, _
  ByVal lpUsedDefaultChar As Long) As Long

Private Sub getUtf8(ByRef s As String, ByRef b() As Byte)
Const CP_UTF8 As Long = 65001
Dim len_s As Long
Dim ptr_s As Long
Dim size As Long
  Erase b
  len_s = Len(s)
  If len_s = 0 Then _
    Err.Raise 30030, , "Len(WideChars) = 0"
  ptr_s = StrPtr(s)
  size = WideCharToMultiByte(CP_UTF8, 0, ptr_s, len_s, 0, 0, 0, 0)
  If size = 0 Then _
    Err.Raise 30030, , "WideCharToMultiByte() = 0"
  ReDim b(0 To size - 1)
  If WideCharToMultiByte(CP_UTF8, 0, ptr_s, len_s, VarPtr(b(0)), size, 0, 0) = 0 Then _
    Err.Raise 30030, , "WideCharToMultiByte(" & Format$(size) & ") = 0"
End Sub

Public Sub writeUtf()
Dim file As Integer
Dim s As String
Dim b() As Byte
  s = "äöüßµ@€|~{}[]²³\ .." & _
    " OMEGA" & ChrW$(937) & ", SIGMA" & ChrW$(931) & _
    ", alpha" & ChrW$(945) & ", beta" & ChrW$(946) & ", pi" & ChrW$(960) & vbCrLf
  file = FreeFile
  Open "C:\Temp\TestUtf8.txt" For Binary Access Write Lock Read Write As #file
  getUtf8 s, b
  Put #file, , b
  Close #file
End Sub

Я посмотрел на ответ от Маны, чье имя намекает на квалификацию и опыт кодирования. Документы VBA говорят CreateTextFile(filename, [overwrite [, unicode]]) создает файл "как файл Unicode или ASCII. Значение True, если файл создан как файл Unicode; False, если он создан как файл ASCII. Если не указан, предполагается, что файл ASCII". Хорошо, что файл хранит символы Юникода, но в какой кодировке? Не закодированный Unicode не может быть представлен в файле.

Страница документа VBA для OpenTextFile(filename[, iomode[, create[, format]]]) предлагает третий вариант для формата:

  • TriStateDefault 2 "открывает файл, используя систему по умолчанию".
  • TriStateTrue 1 "открывает файл как Unicode."
  • TriStateFalse 0 "открывает файл как ASCII."

Маня передает -1 за этот аргумент.

Судя по документации VB.NET (не VBA, но я думаю, что отражает реальность того, как базовая ОС Windows представляет строки Unicode и отражается в MS Office, я не знаю), система по умолчанию представляет собой кодировку с использованием символа 1 байта / Unicode с использованием ANSI кодовая страница для локали. UnicodeEncoding это UTF-16. Документы также описывают, что UTF-8 также является "кодировкой Unicode", что имеет смысл для меня. Но я пока не знаю, как указать UTF-8 для вывода VBA, и не уверен, что данные, которые я записываю на диск с помощью OpenTextFile(,,,1), имеют кодировку UTF-16. Пост Тамалека полезен.

Я не хотел менять весь свой код только для поддержки нескольких строк UTF8, поэтому я позволил своему коду делать свое дело, и после того, как файл был сохранен (в коде ANSI, так как это значение по умолчанию для excel), я затем преобразовал файл в UTF-8, используя этот код:

      Sub convertTxttoUTF(sInFilePath As String, sOutFilePath As String)
    Dim objFS  As Object
    Dim iFile       As Double
    Dim sFileData   As String
    
    'Init
    iFile = FreeFile
    Open sInFilePath For Input As #iFile
        sFileData = Input$(LOF(iFile), iFile)
        sFileData = sFileData & vbCrLf
    Close iFile
    
    'Open & Write
    Set objFS = CreateObject("ADODB.Stream")
    objFS.Charset = "utf-8"
    objFS.Open
    objFS.WriteText sFileData
    
    'Save & Close
    objFS.SaveToFile sOutFilePath, 2   '2: Create Or Update
    objFS.Close
    
    'Completed
    Application.StatusBar = "Completed"
End Sub

и я использую этот сабвуфер вот так (это пример):

      Call convertTxttoUTF("c:\my.json", "c:\my-UTF8.json")

я нашел этот код здесь: VBA для изменения кодировки файлов ANSI на UTF8 - текст в Unicode

и поскольку это написано маркером BOM, чтобы удалить bom, я изменил Sub на это:

      Sub convertTxttoUTF(sInFilePath As String, sOutFilePath As String)
    Dim objStreamUTF8  As Object
    Dim objStreamUTF8NoBOM  As Object
    Dim iFile       As Double
    Dim sFileData   As String
    
    Const adSaveCreateOverWrite = 2
    Const adTypeBinary = 1
    Const adTypeText = 2
    
    'Init
    iFile = FreeFile
    Open sInFilePath For Input As #iFile
        sFileData = Input(LOF(iFile), iFile)
    Close iFile
    
    'Open files
    Set objStreamUTF8 = CreateObject("ADODB.Stream")
    Set objStreamUTF8NoBOM = CreateObject("ADODB.Stream")
           
    ' wrute the fules       
    With objStreamUTF8
      .Charset = "UTF-8"
      .Open
      .WriteText sFileData
      .Position = 0
      .SaveToFile sOutFilePath, adSaveCreateOverWrite
      .Type = adTypeText
      .Position = 3
    End With
    
    With objStreamUTF8NoBOM
      .Type = adTypeBinary
      .Open
      objStreamUTF8.CopyTo objStreamUTF8NoBOM
      .SaveToFile sOutFilePath, 2
    End With
    
    ' close the files
    objStreamUTF8.Close
    objStreamUTF8NoBOM.Close
End Sub

я использовал этот ответ , чтобы решить неизвестный символ спецификации в начале файла

Традиционный способ преобразования строки в строку UTF-8 следующий:

StrConv("hello world",vbFromUnicode)

Проще говоря:

Dim fnum As Integer
fnum = FreeFile
Open "myfile.txt" For Output As fnum
Print #fnum, StrConv("special characters: äöüß", vbFromUnicode)
Close fnum

Никаких специальных COM-объектов не требуется

Другие вопросы по тегам