Reputation: 799
I am using Delphi XE3.
In my code, I will need to create a file stream, and then write some my own data, as well as the contents of several TStringList into it. The file is in UTF-16LE format.
Therefore, my code:
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;
After executing the codes, I find a problem, each time I invoke StringList1.SaveToStream(FileStream, TEncoding.Unicode); it will write BOM (0xFFFE) then followed by the actual strings in the string list.
Therefore, I get a Unicode file like this:
0xFFFE(The first one is written by myself)
(some data)
0xFFFE (StringList1 contents)
(some data)
0xFFFE (StringList2 contents)
But this is not I expect since there should be only one 0xFFFE at the beginning of the file. Therefore, I just wonder how to prevent StringList1.SaveToStream to write the 0xFFFE BOM before writing the actual string lists?
Upvotes: 2
Views: 708
Reputation: 799
I find another solution for my question.
TStrings has a WriteBOM property, which will control whether to write out the BOM when using SaveToStream or SaveToFile.
Therefore, using the following codes will disable the BOM:
StringList1.WriteBOM := False;
StringList1.SaveToStream(FileStream, TEncoding.Unicode);
Upvotes: 6
Reputation: 596256
You can use SaveToStream()
to save to a TMemoryStream
first, then set that stream's Position
to skip past the BOM in it and save the rest of its data to the TFileStream
.
procedure WriteUnicodeStrings(AStream: TStream; AStrings: TStrings);
var
MS: TMemoryStream;
begin
MS := TMemoryStream.Create;
try
AStrings.SaveToStream(MS, TEncoding.Unicode);
if MS.Size > 2 then
begin
MS.Position := 2;
AStream.CopyFrom(MS, MS.Size-2);
end;
finally
MS.Free;
end;
end;
...
FileStream := TFileStream.Create('D:\MyFile.dat', fmCreate or fmOpenWrite or fmShareExclusive);
try
// Write some data to FileStream
WriteUnicodeStrings(FileStream, StringList1);
// Write some more data to FileStream
WriteUnicodeStrings(FileStream, StringList2);
finally
FileStream.Free;
end;
Or, you can simply derive a class from SysUtils.TUnicodeEncoding
and override its GetPreamble()
method to not return any BOM, then use that class instead of using TEncoding.Unicode
.
type
TMyUnicodeEncoding = class(TUnicodeEncoding)
public
function GetPreamble: TBytes; override;
end;
function TMyUnicodeEncoding.GetPreamble: TBytes;
begin
Result := nil;
end;
procedure WriteUnicodeStrings(AStream: TStream; AStrings: TStrings);
var
Enc: TMyUnicodeEncoding;
begin
Enc := TMyUnicodeEncoding.Create;
try
AStrings.SaveToStream(AStream, Enc);
finally
Enc.Free;
end;
end;
...
FileStream := TFileStream.Create('D:\MyFile.dat', fmCreate or fmOpenWrite or fmShareExclusive);
try
// Write some data to FileStream
WriteUnicodeStrings(FileStream, StringList1);
// Write some more data to FileStream
WriteUnicodeStrings(FileStream, StringList2);
finally
FileStream.Free;
end;
Upvotes: 2