我尝试使用TFilestream获取文件内容:
procedure ShowFileCont(myfile : string);
var
tr : string;
fs : TFileStream;
Begin
Fs := TFileStream.Create(myfile, fmOpenRead or fmShareDenyNone);
SetLength(tr, Fs.Size);
Fs.Read(tr[1], Fs.Size);
Showmessage(tr);
Fs.Free;
end;
我做了一个只包含内容的小文本文件: aaaaaaaJ“њРЉTщЂ®8ЈЏVд“Ј|AИaaaaaaa
这些文件有不同的大小,但内容相同 - 我在记事本中打开它们,它们都有相同的内容
但是当我运行ShowFileCont proc时,它会向我显示不同的结果:
问题:
添加:对不起,我没有说我使用Lazarus FPC和string = utf8string
答案 0 :(得分:3)
为什么文件大小不同?
因为他们使用不同的编码。 1251编码将每个字符映射到单个字节。但是UTF-8为每个字符使用可变数量的字节。
如何获取真实的文件内容?
您需要使用与文件中使用的编码匹配的字符串类型。因此,例如,如果内容是UTF-8编码,这是最佳选择,那么您将内容加载到UTF-8字符串中。您在string
为UTF-8编码的模式下使用FPC。在这种情况下,问题中的代码就是您所需要的。
加载代码页为1251的MBCS编码文件比较棘手。您可以将其加载到AnsiString
变量中,只要系统的区域设置为1251,就可以正确执行任何转换。
但是,在具有不同语言环境的计算机上运行时,代码的行为会有所不同。如果您想使用不同的MBCS编码加载文本,例如1252,那么您就无法使用此方法。您需要加载到一个字节数组中,然后从1252转换为UTF-8,以便您可以将该UTF-8存储在string
变量中。
为此,您可以使用LCL的LConvEncoding
单位。例如,您可以使用CP1251ToUTF8
,CP1252ToUTF8
等将MBCS转换为UTF-8。
如何从文件中确定使用了哪种编码?
你做不到。你可以猜测在许多情况下都是准确的。但一般来说,根本不可能识别用于表示文本的字节数组的编码。
有时可以获取文件并排除某些编码。例如,并非所有字节流都是有效的UTF-8或UTF-16文本。所以你可以排除这些文件。但对于像1251,1252等编码,任何字节流都是有效的。除了1252个编码流之外,你根本没有办法告诉1251个编码流,准确率为100%。
LConvEncoding
单位有GuessEncoding
,听起来似乎有用。
答案 1 :(得分:1)
他们的内容显然不相等。您可以自己查看文件大小不同。不同大小的东西永远不会相等。
您的文件可能在记事本中显示相同,因为记事本知道如何识别某些字符编码。您以两种不同的方式保存了文件。一种方法是使用一种编码,为256个可能的值中的每一个分配一个字节。另一种方法是使用一个编码,为1到6个字节分配超过10,000个可能的值。您保存的某些字符需要多个字节,这就解释了为什么该文件的一个版本比另一个版本大。
TFileStream
没有注意到这些。它只处理字节。根据您的Delphi版本,您的string
变量可能会也可能不会注意编码。在Delphi 2009之前,string
每个字符存储一个字节。从Delphi 2009开始,string
每个字符使用两个字节,因此您的SetLength
调用是错误的,之后的所有内容都无法进一步调查。
每个字符只有一个字节,您的ShowMessage
调用不会将字符串解释为UTF-8编码。相反,它将使用您的系统代码页来解释您的字符串。如果您知道您阅读的字符串是使用UTF-8编码的,那么您需要在显示之前通过调用UTF8Decode
将其转换为UTF-16。这将返回WideString
,您可以使用任意数量的函数来显示它,例如MessageBoxW
。如果你有Delphi 2009或更高版本,那么如果你使用Utf8String
而不是string
,编译器会自动为你插入转换代码。