String 为什么将字符串保存到文件的方式会影响结果
我已经解决了将包含德语字母的字符串保存到txt文件的问题。 MCVE如下所示:String 为什么将字符串保存到文件的方式会影响结果,string,delphi,unicode,localization,String,Delphi,Unicode,Localization,我已经解决了将包含德语字母的字符串保存到txt文件的问题。 MCVE如下所示: procedure TForm1.Button1Click(Sender: TObject); var s: string; //alias for UnicodeString tf: textfile; ms: tmemorystream; begin s := 'ßüÜöÖäÄФфшШ'; assignfile(tf, 'b:\tmp.txt'); Rewrite(tf); writ
procedure TForm1.Button1Click(Sender: TObject);
var
s: string; //alias for UnicodeString
tf: textfile;
ms: tmemorystream;
begin
s := 'ßüÜöÖäÄФфшШ';
assignfile(tf, 'b:\tmp.txt');
Rewrite(tf);
write(tf, s);
closefile(tf);
ms := tmemorystream.Create;
try
ms.WriteBuffer(Pointer(s)^, Length(s) * SizeOf(s[Low(s)]));
ms.Position := 0;
ms.SaveToFile('b:\tmp2.txt');
finally
ms.Free;
end;
end;
如果字符串直接保存到文件中,我们会得到以下结果:tmp.txt
–?uUoOaAФфцⅢ
。德语字母已更改,但西里尔字母仍保留。如果字符串由TMemoryStream保存,则结果是正确的:tmp2.txt
–。这是什么原因
追加
我决定为以不同方式保存的给定字符串添加十六进制值:
对于Write
方法:
data: array[0..10] of byte = (
$3F, $75, $55, $6F, $4F, $61, $41, $D4, $F4, $F8, $D8
);
对于AssignFile(tf,'b:\tmp.txt',CP_UTF8)之后调用的Write
方法代码>:
对于TMemoryStream
:
data: array[0..21] of byte = (
$DF, $00, $FC, $00, $DC, $00, $F6, $00, $D6, $00, $E4, $00, $C4, $00, $24, $04,
$44, $04, $48, $04, $28, $04
);
对于TStringList
:
data: array[0..27] of byte = (
$FF, $FE, $DF, $00, $FC, $00, $DC, $00, $F6, $00, $D6, $00, $E4, $00, $C4, $00,
$24, $04, $44, $04, $48, $04, $28, $04, $0D, $00, $0A, $00
);
追加
根据@Remy Lebeau的宝贵建议:
此方法生成一个25字节长的文件。它类似于在AssignFile(tf,'b:\tmp.txt',CP_UTF8)之后调用Write方法生成的HEX代码>附加3个字节(BOM?)
要使用Write/WriteLn过程将unicode字符串存储在文本文件中,必须首先分配适当的代码页:
AssignFile(tf, 'b:\tmp.txt',CP_UTF8);
要针对不同的区域设置保存文件,也可以先在文件中放置BOM表:
Write(tf, #$FEFF); // An utf8 BOM
我相信如果他们能完成这项工作,他们会一直使用RTL中的函数。在本例中,TStringList以一种非常简单的方式为您实现了这一技巧:
在这个小示例中,我将一个stringlist保存到一个文本文件中,然后再次加载它。为了证明它有效,我在再次加载文本文件后添加了一个断言测试
因此,无需使用MemoryStream
和关于BOM
的内容。使用TStringList,因为它具有您需要的所有功能
procedure TForm1.Button1Click(Sender: TObject);
var
s: String;
begin
s := 'ßüÜöÖäÄФфшШ';
with TStringList.Create do
try
Text := s;
SaveToFile('C:\aa\tmp3.txt', TEncoding.Unicode);
finally
free;
end;
with TStringList.Create do
try
LoadFromFile('C:\aa\tmp3.txt');
Assert(Strings[0] = s, '');
finally
free;
end;
end;
在这种情况下,请尝试使用TStreamWriter
,例如:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string; //alias for UnicodeString
writer: TStreamWriter;
begin
s := 'ßüÜöÖäÄФфшШ';
writer := TStreamWriter.Create('b:\tmp.txt', False, TEncoding.UTF8);
try
writer.Write(s);
finally
writer.Free;
end;
ms := TMemoryStream.Create;
try
writer := TStreamWriter.Create(ms, TEncoding.UTF8);
try
writer.Write(s);
finally
writer.Free;
end;
ms.Position := 0;
ms.SaveToFile('b:\tmp2.txt');
finally
ms.Free;
end;
end;
瞧,谢谢,@LU-RD。我有德尔福XE5。你的评论帮助了我。代码使用以下行正常工作:assignfile(tf'b:\tmp.txt',CP\u UTF8)代码>请发布一个答案,这样我就可以接受了。我想你需要决定的是你希望如何编码你的文本。“你应该积极地做出决定。谢谢你,@DavidHeffernan。”。我是否正确理解将字符串变量写入文件需要明确的编码指示?但是,这是否意味着编写一个包含记录的文件,其中包含字符串(固定长度)的记录将导致数据损坏?一个简单的问题。您的文本要使用什么编码。谢谢。这是否意味着TMemoryStream.SaveToFile
的写入方法与Write
的写入方法不同?是的,TMemoryStream将文件存储在UTF-16中,而本例将文件存储在UTF-8中。这是一个相当糟糕的建议。由于UTF-8的数据单元适合单字节、字节顺序标记编码,Unicode Consortium不鼓励使用它。@FreeConsulting,请注意,我使用了“can”一词。在某些情况下,对于UTF-8文件,BOM是更好的选择。@LURD,当然可以。但我不同意你使用它的理由。UTF-8编码的文件已经与任何语言环境兼容,无论是否存在BOM。包含或排除BOM的要求仅仅是支持旧的和/或损坏的软件实现的问题。非常有必要提及您的示例编写的UTF-16 LE(和未指定的读取)编码。谢谢,@JensBorrisholt。对不起,我不能接受超过一个答案,我只能投赞成票。我注意到文件的大小不同。如果它是由TMemoryStream
或调用AssignFile(tf,'b:\tmp.txt',CP_UTF8)后由Write
编写的代码>大小为22字节。我认为一个11个字符长的字符串应该有22个字节的大小+2个字节,不是吗?在使用TStringList写入之后,它是28个字节。如果我用记事本打开这些文件,它们将显示相同的字符串。这是否意味着,如果文件是用TStringList
写入的,那么它也必须由TStringList
读取?@JensBorrisholt,OK,不是“未指定”,而是“使用前导码检测到的”。正如您所看到的,由于没有清楚地指出所使用的编码,您给OP带来了更多的混乱。@asd tm没关系。额外字节的原因是我使用Unicode而不是UTF8保存它。如果要将其保存在UTF8中,请将TEncoding.Unicode替换为TEncoding.UTF8加载时我不指定编码的原因是因为字符串列表自动检测编码谢谢,Remy Lebeau。我已经用你的代码生成的十六进制附加了我的帖子。所有的答案都非常有用,不幸的是我只能接受一个。我只能投你宝贵的一票。
procedure TForm1.Button1Click(Sender: TObject);
var
s: String;
begin
s := 'ßüÜöÖäÄФфшШ';
with TStringList.Create do
try
Text := s;
SaveToFile('C:\aa\tmp3.txt', TEncoding.Unicode);
finally
free;
end;
with TStringList.Create do
try
LoadFromFile('C:\aa\tmp3.txt');
Assert(Strings[0] = s, '');
finally
free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
s: string; //alias for UnicodeString
writer: TStreamWriter;
begin
s := 'ßüÜöÖäÄФфшШ';
writer := TStreamWriter.Create('b:\tmp.txt', False, TEncoding.UTF8);
try
writer.Write(s);
finally
writer.Free;
end;
ms := TMemoryStream.Create;
try
writer := TStreamWriter.Create(ms, TEncoding.UTF8);
try
writer.Write(s);
finally
writer.Free;
end;
ms.Position := 0;
ms.SaveToFile('b:\tmp2.txt');
finally
ms.Free;
end;
end;