Могу ли я экспортировать данные Excel с UTF-8 без спецификации?

Я экспортирую данные Microsoft Excel с помощью Excel Macro(VBScript). Поскольку файл представляет собой скрипт lua, я экспортирую его как UTF-8. Единственный способ сделать UTF-8 в Excel - это использовать adodb.stream, как это

set fileLua = CreateObject("adodb.stream")
fileLua.Type = 2
fileLua.Mode = 3
fileLua.Charset = "UTF-8"
fileLua.Open
fileLua.WriteText("test")
fileLua.SaveToFile("Test.lua")
fileLua.flush
fileLua.Close

Я хочу исключить спецификацию из Test.lua, но не знаю как. (Поскольку в Test.lua есть текст Unicode, я должен использовать формат UTF-8.)

Знаете ли вы, как сделать файл UTF-8 без спецификации в файле Excel? Заранее спасибо.

6 ответов

Решение

У меня также та же проблема: нужно экспортировать данные из Excel (Office 2003, VBA6.5) в файл в кодировке UTF-8. Нашел ответ по вашему вопросу! Ниже моего примера, где я также раздеваю спецификацию, используя трюк № 2 из ответа Boos (спасибо!). Я не работал #1 и никогда не пытался #3.

Sub WriteUTF8WithoutBOM()
    Dim UTFStream As Object
    Set UTFStream = CreateObject("adodb.stream")
    UTFStream.Type = adTypeText
    UTFStream.Mode = adModeReadWrite
    UTFStream.Charset = "UTF-8"
    UTFStream.LineSeparator = adLF
    UTFStream.Open
    UTFStream.WriteText "This is an unicode/UTF-8 test.", adWriteLine
    UTFStream.WriteText "First set of special characters: öäåñüûú€", adWriteLine
    UTFStream.WriteText "Second set of special characters: qwertzuiopõúasdfghjkléáûyxcvbnm\|Ä€Í÷×äðÐ[]í³£;?¤>#&@{}<;>*~¡^¢°²`ÿ´½¨¸0", adWriteLine

    UTFStream.Position = 3 'skip BOM

    Dim BinaryStream As Object
    Set BinaryStream = CreateObject("adodb.stream")
    BinaryStream.Type = adTypeBinary
    BinaryStream.Mode = adModeReadWrite
    BinaryStream.Open

    'Strips BOM (first 3 bytes)
    UTFStream.CopyTo BinaryStream

    'UTFStream.SaveToFile "d:\adodb-stream1.txt", adSaveCreateOverWrite
    UTFStream.Flush
    UTFStream.Close

    BinaryStream.SaveToFile "d:\adodb-stream2.txt", adSaveCreateOverWrite
    BinaryStream.Flush
    BinaryStream.Close
End Sub

Ссылка на объект потока ADO, которую я использовал.

Если кто-то еще борется с константой adTypeText, вам нужно включить "Библиотеку объектов Microsoft ActiveX Data Objects 2.5" в меню "Инструменты-> Ссылки".

Несколько возможностей:

  1. Поместите текст в буфер как UTF-8, Type=2, но затем установите Type=1 (как двоичный файл) и запишите его. Это может убедить ADODB.Stream пропустить добавление спецификации.

  2. Создайте другой буфер, как тип binary, и используйте CopyTo, чтобы скопировать данные в этот буфер из точки после спецификации.

  3. Снова прочитайте файл, используя Scripting.FileSystemObject, обрежьте спецификацию, запишите снова

редактировать

Комментарий от rellampec предупредил меня о том, что лучше отбросить обнаруженный мной LF был добавлен в конец файла методом user272735. Я добавил новую версию моей программы в конце.

Оригинальный пост

Я успешно использовал метод user272735 в течение года, когда обнаружил, что в конце файла добавлен LF. Я не замечал этот дополнительный LF, пока не провел очень подробное тестирование, так что это не важная ошибка. Тем не менее, моя последняя версия отказывается от LF на тот случай, если это когда-нибудь станет важным.

Public Sub PutTextFileUtf8(ByVal PathFileName As String, ByVal FileBody As String)

  ' Outputs FileBody as a text file (UTF-8 encoding without leading BOM)
  ' named PathFileName

  ' Needs reference to "Microsoft ActiveX Data Objects n.n Library"
  ' Addition to original code says version 2.5. Tested with version 6.1.

  '  1Nov16  Copied from http://stackru.com/a/4461250/973283
  '          but replaced literals with parameters.
  ' 15Aug17  Discovered routine was adding an LF to the end of the file.
  '          Added code to discard that LF.

  ' References: http://stackru.com/a/4461250/973283
  '             https://www.w3schools.com/asp/ado_ref_stream.asp

  Dim BinaryStream As Object
  Dim UTFStream As Object

  Set UTFStream = CreateObject("adodb.stream")

  UTFStream.Type = adTypeText
  UTFStream.Mode = adModeReadWrite
  UTFStream.Charset = "UTF-8"
  ' The LineSeparator will be added to the end of FileBody. It is possible
  ' to select a different value for LineSeparator but I can find nothing to
  ' suggest it is possible to not add anything to the end of FileBody
  UTFStream.LineSeparator = adLF
  UTFStream.Open
  UTFStream.WriteText FileBody, adWriteLine

  UTFStream.Position = 3 'skip BOM

  Set BinaryStream = CreateObject("adodb.stream")
  BinaryStream.Type = adTypeBinary
  BinaryStream.Mode = adModeReadWrite
  BinaryStream.Open

  UTFStream.CopyTo BinaryStream

  ' Oriinally I planned to use "CopyTo Dest, NumChars" to not copy the last
  ' byte.  However, NumChars is described as an integer whereas Position is
  ' described as Long. I was concerned by "integer" they mean 16 bits.
  'Debug.Print BinaryStream.Position
  BinaryStream.Position = BinaryStream.Position - 1
  BinaryStream.SetEOS
  'Debug.Print BinaryStream.Position

  UTFStream.Flush
  UTFStream.Close
  Set UTFStream = Nothing

  BinaryStream.SaveToFile PathFileName, adSaveCreateOverWrite
  BinaryStream.Flush
  BinaryStream.Close
  Set BinaryStream = Nothing

End Sub

Новая версия рутины

Эта версия пропускает код для удаления ненужного LF, добавленного в конце, потому что в первую очередь избегает добавления LF. Я сохранил оригинальную версию на тот случай, если кто-нибудь заинтересуется техникой удаления конечных символов.

Public Sub PutTextFileUtf8NoBOM(ByVal PathFileName As String, ByVal FileBody As String)

  ' Outputs FileBody as a text file named PathFileName using
  ' UTF-8 encoding without leading BOM

  ' Needs reference to "Microsoft ActiveX Data Objects n.n Library"
  ' Addition to original code says version 2.5. Tested with version 6.1.

  '  1Nov16  Copied from http://stackru.com/a/4461250/973283
  '          but replaced literals with parameters.
  ' 15Aug17  Discovered routine was adding an LF to the end of the file.
  '          Added code to discard that LF.
  ' 11Oct17  Posted to Stackru
  '  9Aug18  Comment from rellampec suggested removal of adWriteLine from
  '          WriteTest statement would avoid adding LF.
  ' 30Sep18  Amended routine to remove adWriteLine from WriteTest statement
  '          and code to remove LF from file. Successfully tested new version.

  ' References: http://stackru.com/a/4461250/973283
  '             https://www.w3schools.com/asp/ado_ref_stream.asp

  Dim BinaryStream As Object
  Dim UTFStream As Object

  Set UTFStream = CreateObject("adodb.stream")

  UTFStream.Type = adTypeText
  UTFStream.Mode = adModeReadWrite
  UTFStream.Charset = "UTF-8"
  UTFStream.Open
  UTFStream.WriteText FileBody

  UTFStream.Position = 3 'skip BOM

  Set BinaryStream = CreateObject("adodb.stream")
  BinaryStream.Type = adTypeBinary
  BinaryStream.Mode = adModeReadWrite
  BinaryStream.Open

  UTFStream.CopyTo BinaryStream

  UTFStream.Flush
  UTFStream.Close
  Set UTFStream = Nothing

  BinaryStream.SaveToFile PathFileName, adSaveCreateOverWrite
  BinaryStream.Flush
  BinaryStream.Close
  Set BinaryStream = Nothing

End Sub

Вот еще один взлом BOM, из ответа, который перекрывает ваш вопрос.

Извиняюсь за поздний ответ - это больше для других людей, которые сталкиваются с маркерами порядка байтов - и просмотры страниц по этому вопросу говорят мне, что ваш вопрос имеет отношение к нескольким связанным проблемам: на удивление трудно написать файл без спецификации в VBA - даже некоторые из общих потоковых библиотек вносят BOM в ваш вывод, независимо от того, просили вы об этом или нет.

Я говорю, что мой ответ "перекрывается", потому что приведенный ниже код решает немного другую проблему - основная цель - написание файла схемы для папки с разнородным набором файлов - но это рабочий пример удаления BOM и файла без BOM запись в использовании, и соответствующий сегмент четко обозначен.

Ключевая функциональность заключается в том, что мы перебираем все файлы.csv в папке и тестируем каждый файл с быстрым откусыванием первых четырех байтов: и мы только беремся за обременительную задачу удаления маркера, если мы увидеть один.

Мы работаем с низкоуровневым кодом для обработки файлов из изначального C. Мы должны, вплоть до использования байтовых массивов, потому что все остальное, что вы делаете в VBA, будет помещать маркеры порядка байтов, встроенные в структуру строковая переменная.

Итак, без дальнейшего adodb, вот код:

Код утилизации для текстовых файлов в файле schema.ini:

Public Sub SetSchema (strFolder As String) При ошибке Возобновить Далее 
'Запишите файл Schema.ini в папку данных.
"Это необходимо, если у нас нет привилегий реестра для установки правильного значения реестра ImportMixedTypes=Text", которое переопределяет IMEX=1
Код также проверяет наличие файлов ANSI или UTF-8 и UTF-16 и применяет "полезную настройку для CharacterSet ( UNICODE|ANSI) с ужасным взломом".
'Определенный кодовой страницей OEM текст не поддерживается: требуется дальнейшее кодирование
' ... И мы убираем маркеры порядка байтов, если мы их видим - поставщик текстовых файлов OLEDB SQL ' не может иметь дело с спецификацией в файле UTF-16 или UTF-8
'Не реализовано: обработка файлов с разделителями табуляции или других разделителей. "Код предполагает наличие строки заголовка со столбцами, определяет" сканировать все строки "и" налагает "чтение столбца как текста, если типы данных смешаны.
Dim strSchema As String Dim strFile As String Dim hndFile As Long Dim arrFile () As Byte Dim arrBytes (от 0 до 4) As Byte
Если верно (strFolder, 1) <> "\" Тогда strFolder = strFolder & "\"
'Dir () - это функция итератора, когда вы вызываете ее с подстановочным знаком:
strFile = VBA.FileSystem.Dir (strFolder & "*.csv")
Делай Пока Лен (strFile) > 0
hndFile = FreeFile Открыть strFolder & strFile Для двоичного файла как #hndFile Получить #hndFile,, arrBytes Закрыть #hndFile
strSchema = strSchema & "[" & strFile & "]" & vbCrLf strSchema = strSchema & "Format = CSVDelimited" & vbCrLf strSchema = strSchema & "ImportMixedTypes=Text" & vbCrLf strSanma = strSchma
Если arrBytes(2) = 0 или arrBytes(3) = 0, тогда 'это взломать strSchema = strSchema & "CharacterSet=UNICODE" & vbCrLf Остальное strSchema = strSchema & "CharacterSet = ANSI" & vbCrLf End If
strSchema = strSchema & "ColNameHeader = True" & vbCrLf strSchema = strSchema & vbCrLf
************************************************* **********
'Распределение спецификации - Порядок следования байтов нарушает поставщика текста OLEDB Access:
Если arrBytes(0) = &HFE И arrBytes(1) = &HFF _ Или arrBytes(0) = &HFF И arrBytes(1) = &HFE Тогда
hndFile = FreeFile Открыть strFolder и strFile Для двоичного файла #hndFile ReDim arrFile(от 0 до LOF(hndFile) - 1) Получить #hndFile,, arrFile Закрыть #hndFile
BigReplace arrFile, arrBytes (0) и arrBytes (1), ""
hndFile = FreeFile Открыть strFolder & strFile Для двоичного файла #hndFile Поместить #hndFile,, arrFile Закрыть #hndFile Удалить arrFile
ElseIf arrBytes (0) = & HEF И arrBytes(1) = &HBB И arrBytes(2) = &HBF Тогда
hndFile = FreeFile Открыть strFolder & strFile Для двоичного файла #hndFile ReDim arrFile(от 0 до LOF(hndFile) - 1) Получить #hndFile,, arrFile Закрыть #hndFile BigReplace arrFile, arrBytes(0) и arrBytes(1) & arb (, ""
hndFile = FreeFile Открыть strFolder & strFile Для двоичного файла #hndFile Поместить #hndFile,, arrFile Закрыть #hndFile Удалить arrFile
Конец, если
************************************************* **********

strFile = "" strFile = Dir
петля
Если лен (стрСхема) > 0 то
strFile = strFolder & "Schema.ini"
hndFile = FreeFile Открыть strFile для двоичного файла как #hndFile Поставить #hndFile,, strSchema Закрыть #hndFile
Конец, если

End Sub

Public Sub BigReplace (ByRef arrBytes () As Byte, _ ByRef SearchFor As String, _ ByRef ReplaceWith As String) При ошибке Возобновить Далее
Dim varSplit As Variant
varSplit = Split (arrBytes, SearchFor) arrBytes = Join $ (varSplit, ReplaceWith)
Стереть varSplit
End Sub

Код легче понять, если вы знаете, что байтовый массив может быть назначен для VBA.String, и наоборот. Функция BigReplace() - это хак, который обходит неэффективную обработку строк в VBA, особенно распределение: вы обнаружите, что большие файлы вызывают серьезные проблемы с памятью и производительностью, если вы делаете это каким-либо другим способом.

Если вы предпочитаете нативный T-SQL вместо внешнего кода

DECLARE @FILE_NAME              VARCHAR(255)    = 'd:\utils\test.xml'       --drive:\path\filename\
DECLARE @FILE_DATA              VARCHAR(MAX)    = '<?xml version="1.0" encoding="UTF-8"?>test</xml>'            --binary as varchar(max)

DECLARE @FILE_NAME_TO           VARCHAR(255)                        --Temp name for text stream
DECLARE @FSO_ID_TXTSTRM         INT                                 --Text Stream
DECLARE @FSO_ID_BINSTRM         INT                                 --Binary Stream
DECLARE @RC                     INT 

EXEC @RC = sp_OACreate 'ADODB.Stream',  @FSO_ID_TXTSTRM OUTPUT
EXEC @RC = sp_OASetProperty             @FSO_ID_TXTSTRM,    'Type',             2                           --1 = binary, 2 = text
EXEC @RC = sp_OASetProperty             @FSO_ID_TXTSTRM,    'Mode',             3                           --0 = not set, 1 read, 2 write, 3 read/write
EXEC @RC = sp_OASetProperty             @FSO_ID_TXTSTRM,    'Charset',          'UTF-8'                     --'ISO-8859-1'
EXEC @RC = sp_OASetProperty             @FSO_ID_TXTSTRM,    'LineSeparator',    'adLF'
EXEC @RC = sp_OAMethod                  @FSO_ID_TXTSTRM,    'Open'  
EXEC @RC = sp_OAMethod                  @FSO_ID_TXTSTRM,    'WriteText',        NULL,       @FILE_DATA      --text method

--Create binary stream
EXEC @RC = sp_OACreate 'ADODB.Stream',  @FSO_ID_BINSTRM OUTPUT
EXEC @RC = sp_OASetProperty             @FSO_ID_BINSTRM,    'Type',             1                           --1 = binary, 2 = text
EXEC @RC = sp_OAMethod                  @FSO_ID_BINSTRM,    'Open'
EXEC @RC = sp_OASetProperty             @FSO_ID_BINSTRM,    'Mode',             3                           --0 = not set, 1 read, 2 write, 3 read/write    

--Move 3 positions forward in text stream (BOM is first 3 positions)
EXEC @RC = sp_OASetProperty             @FSO_ID_TXTSTRM,    'Position',         3

--Copy text stream to binary stream
EXEC @RC = sp_OAMethod                  @FSO_ID_TXTSTRM,    'CopyTo',           NULL,       @FSO_ID_BINSTRM

--Commit data and close text stream
EXEC @RC = sp_OAMethod                  @FSO_ID_TXTSTRM,    'Flush'
EXEC @RC = sp_OAMethod                  @FSO_ID_TXTSTRM,    'Close'
EXEC @RC = sp_OADestroy                 @FSO_ID_TXTSTRM

--Save binary stream to file and close
EXEC @RC = sp_OAMethod                  @FSO_ID_BINSTRM,    'SaveToFile',       NULL,       @FILE_NAME, 2   --1 = notexist 2 = overwrite
EXEC @RC = sp_OAMethod                  @FSO_ID_BINSTRM,    'Close'
EXEC @RC = sp_OADestroy                 @FSO_ID_BINSTRM
Другие вопросы по тегам