VBA:保存UTF-8无BOM文件

21

可能是一些简单的问题,这是我尝试过的:

 Set objStream = CreateObject("ADODB.Stream")
 Set objStreamNoBOM = CreateObject("ADODB.Stream")

 With objStream
        .Open
        .Charset = "UTF-8"
        .WriteText "aaaaaa"
        .Position = 0
    End With

    With objStreamNoBOM
      '.Charset = "Windows-1252"   ' WORK
       .Charset = "UTF-8"          ' DOESN'T WORK!!
       .Open
       .Type = 2
       .WriteText objStream.ReadText
       .SaveToFile "toto.php", 2
       .Close
    End With
    objStream.Close

如果字符集是UTF-8,则文件开头会有ï»。有没有办法以UTF-8格式保存文件而不带BOM呢?
2个回答

38
在最理想的情况下,相关列表应该包含对此问题的引用,我在搜索“vbscript adodb.stream bom vbscript site:stackoverflow.com”时发现它是第一篇结果。
基于boost的答案的第二个策略:
Option Explicit

Const adSaveCreateNotExist = 1
Const adSaveCreateOverWrite = 2
Const adTypeBinary = 1
Const adTypeText   = 2

Dim objStreamUTF8      : Set objStreamUTF8      = CreateObject("ADODB.Stream")
Dim objStreamUTF8NoBOM : Set objStreamUTF8NoBOM = CreateObject("ADODB.Stream")

With objStreamUTF8
  .Charset = "UTF-8"
  .Open
  .WriteText "aÄö"
  .Position = 0
  .SaveToFile "toto.php", adSaveCreateOverWrite
  .Type     = adTypeText
  .Position = 3
End With

With objStreamUTF8NoBOM
  .Type    = adTypeBinary
  .Open
  objStreamUTF8.CopyTo objStreamUTF8NoBOM
  .SaveToFile "toto-nobom.php", adSaveCreateOverWrite
End With

objStreamUTF8.Close
objStreamUTF8NoBOM.Close

证据:

chcp
Active code page: 65001

dir
 ...
15.07.2015  18:48                 5 toto-nobom.php
15.07.2015  18:48                 8 toto.php

type toto-nobom.php
aÄö

adSaveCreateNotExist 是一个未使用的常量。 - wqw
太好了!第一个SaveToFile实际上并不需要,使用.Flush就可以完成任务。为了测试代码,请在记事本中将其保存为UTF16-LE格式,否则当vbs读取源代码时,测试字符串将会出现乱码。 - Antoni Gual Via

4
我知道脚本文件系统对象的流插入了字节顺序标记,但我还没有在ADODB Stream中看到过这一点。或者至少目前还没有:我很少使用ADODB流对象...但我确实记得几年前把这个备注放进了一些代码里:
'   ****   WHY THIS IS COMMENTED OUT   **** **** **** **** **** **** **** ****
'
'   Microsoft ODBC and OLEDB database drivers cannot read the field names from
'   the header when a unicode byte order mark (&HFF & &HFE) is inserted at the
'   start of the text by Scripting.FileSystemObject 'Write' methods. Trying to
'   work around this by writing byte arrays will fail; FSO 'Write' detects the
'   string encoding automatically, and won't let you hack around it by writing
'   the header as UTF-8 (or 'Narrow' string) and appending the rest as unicode
'
'   (Yes, I tried some revolting hacks to get around it: don't *ever* do that)
'
'   **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
'
'    With FSO.OpenTextFile(FilePath, ForWriting, True, TristateTrue)
'        .Write Join(arrTemp1, EOROW)
'        .Close
'    End With ' textstream object from objFSO.OpenTextFile
'
'   **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****

你可以看出我当天心情很糟糕。 接下来,使用史前时期还未从原始的C语言中崛起的PUT命令进行文件处理:
'   **** WHY WE 'PUT' A BYTE ARRAY INSTEAD OF A VBA STRING VARIABLE  **** ****
'
'       Put #hndFile, , StrConv(Join(arrTemp1, EOROW), vbUnicode)
'       Put #hndFile, , Join(arrTemp1, EOROW)
'
'   If you pass unicode, Wide or UTF-16 string variables to PUT, it prepends a
'   Unicode Byte Order Mark to the data which, when written to your file, will
'   render the field names illegible to Microsoft's JET ODBC and ACE-OLEDB SQL
'   drivers (which can actually read unicode field names, if the helpful label
'   isn't in the way). However, the 'PUT' statements writes a Byte array as-is
'
'   **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****

所以这是实际执行它的代码:
Dim arrByte() As Byte
Dim strText   As String
Dim hndFile   As String


    strText = "Y'all knew that strings are actually byte arrays?"
    arrByte = strText 

    hndFile = FreeFile
    Open FilePath For Binary As #hndFile

    Put #hndFile, , arrByte
    Close #hndFile

    Erase arrByte

我假设strText实际上是UTF-8格式。我的意思是,我们在VBA中,在Microsoft Office中, 绝对 知道它始终是UTF-8格式的,即使我们在国外使用它...

... 对吗?


谢谢你的回答Nile,不过最终还是采用了Ekkehard.Horner的解决方案。 - Julien
据我所知,这将导致UTF-16编码(在所有当前版本的Windows中都受支持)。 - T.M.
@T.M - 最好分别检查:在VBA IDE导出的文本中,以及在工作表本身显示的文本中。 - Nigel Heffernan
@NigelHeffernan,感谢您对可能的版本问题提供反馈(?); 实际上,我新安装的Office 2019 64位中的记事本编辑器显示UTF-16LE作为导出文本的当前保存选项,因此我猜测它是UTF-16编码。 - T.M.
谢谢您提供这个简洁明了的答案。从“Put”命令的描述中,我永远无法想到“Put #hndFile, , arrByte”会起作用。请注意:最好添加“On Error Resume Next”,“Kill FilePath”,“On Error GoTo 0”,因为按照现有代码的方式将覆盖FilePath的开头内容,并且不会触及FilePath其余部分的内容。 - Rocky Scott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接