Delphi 如何防止TStrings.SaveToStream写入BOM?

Delphi 如何防止TStrings.SaveToStream写入BOM?,delphi,delphi-xe3,tstringlist,Delphi,Delphi Xe3,Tstringlist,我正在使用delphixe3 在我的代码中,我需要创建一个文件流,然后将我自己的一些数据以及几个TStringList的内容写入其中。该文件为UTF-16LE格式 因此,我的代码: FileStream := TFileStream.Create('D:\MyFile.dat', fmCreate or fmOpenWrite or fmShareExclusive); try // Write some data to FileStream // Write contents of

我正在使用delphixe3

在我的代码中,我需要创建一个文件流,然后将我自己的一些数据以及几个TStringList的内容写入其中。该文件为UTF-16LE格式

因此,我的代码:

FileStream := TFileStream.Create('D:\MyFile.dat', fmCreate or fmOpenWrite or fmShareExclusive);

try
  // Write some data to FileStream

  // Write contents of StringList1 into FileStream
  StringList1.SaveToStream(FileStream, TEncoding.Unicode);

  // Write some more data to FileStream

  // Write contents of StringList2 into FileSteram
  StringList2.SaveToStream(FileStream, TEncoding.Unicode);
finally
  FileStream.Free;
end;
执行代码后,每次调用StringList1.SaveToStream(FileStream,TEncoding.Unicode)时,我都会发现一个问题;它将写入BOM(0xFFFE),然后在字符串列表中后跟实际字符串

因此,我得到一个Unicode文件,如下所示:

0xFFFE(第一个是我自己写的)

(一些数据)

0xFFFE(StringList1内容)

(一些数据)

0xFFFE(StringList2内容)


但这不是我所期望的,因为在文件的开头应该只有一个0xFFFE。因此,我只是想知道如何防止StringList1.SaveToStream在写入实际字符串列表之前写入0xFFFE BOM?

您可以先使用
SaveToStream()
保存到
TMemoryStream
,然后将该流的
位置设置为跳过其中的BOM表,并将其其余数据保存到
TFileStream

procedure-WriteUnicodeStrings(AStream:TStream;AStrings:TStrings);
变量
MS:TMemoryStream;
开始
MS:=TMemoryStream.Create;
尝试
AStrings.SaveToStream(MS,TEncoding.Unicode);
如果MS.Size>2,则
开始
女士职位:=2;
AStream.CopyFrom(MS,MS.Size-2);
结束;
最后
弗里女士;
结束;
结束;
...
FileStream:=TFileStream.Create('D:\MyFile.dat',fmCreate或fmOpenWrite或fmShareExclusive);
尝试
//将一些数据写入FileStream
WriteUnicodeStrings(文件流,StringList1);
//将更多数据写入FileStream
WriteUnicodeStrings(文件流,StringList2);
最后
免费文件流;
结束;
或者,您可以简单地从
SysUtils.TUnicodeEncoding
派生一个类,并重写其
GetPreamble()
方法以不返回任何BOM表,然后使用该类而不是使用
TEncoding.Unicode

类型
TMyUnicodeEncoding=类(TUnicodeEncoding)
公众的
功能:t字节;推翻
结束;
函数tmyunicodeincode.GetPreamble:TBytes;
开始
结果:=无;
结束;
过程写入(AStream:TStream;AStrings:TStrings);
变量
附件:TMYUNICODENCODE;
开始
Enc:=tmyunicodeincode.Create;
尝试
AStrings.SaveToStream(AStream,Enc);
最后
免费附件;
结束;
结束;
...
FileStream:=TFileStream.Create('D:\MyFile.dat',fmCreate或fmOpenWrite或fmShareExclusive);
尝试
//将一些数据写入FileStream
WriteUnicodeStrings(文件流,StringList1);
//将更多数据写入FileStream
WriteUnicodeStrings(文件流,StringList2);
最后
免费文件流;
结束;

我为我的问题找到了另一个解决方案

TStrings有一个WriteBOM属性,它将控制在使用SaveToStream或SaveToFile时是否写出BOM表

因此,使用以下代码将禁用BOM表:

  StringList1.WriteBOM := False;
  StringList1.SaveToStream(FileStream, TEncoding.Unicode);

停止每次使用SaveToStream写入。使用一个TStringList,调用AddStrings来添加其他内容,然后使用SaveToStream保存原始内容一次。@KenWhite仍然会向流中写入BOM,OP根本不希望这样,因为他在写入TStringList之前编写了自己的BOMdata@RemyLebeau:但是posetr不需要写入BOM,因为它和SL写的是同一个。这是为了完成SL将为您做的事情而跳过不必要的障碍,这样做毫无意义。@KenWhite如果您再仔细阅读OP的问题,be会编写自己的BOM,然后是一些数据,然后是TStringList,然后是一些数据,然后是另一个TStringList。所以你所描述的不会work@KenWhite,雷米尔博是对的。我需要将我自己的数据和TStringList的内容混合写入流中。因此,把所有东西都放进TStringList是不可行的。而且我的内容很大(20-30MB+),如果全部放在TStringList中,会消耗大量内存。我还修改了我的帖子以使其更清晰。我阅读了GetPremission上的文档,其中说“如果不需要序言,则返回一个零长度数组。”。但是你返回零。我认为它并不意味着长度为0的数组,它表示一个nil指针。但是,您的代码可以在Delphi下编译。为什么?@alancc
TBytes
是一个动态的
字节数组
。长度为0的动态数组由nil指针表示(与
字符串
类似)。您可以使用
SetLength(结果为0)而不是
结果:=nil