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?
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);
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With