我写了一个合并不同文本文件的工具(文件很小)。文件可以是 ANSI (Latin1)、UTF-8(带或不带 BOM)。对于带有 BOM 的文件,Delphi 可以正确检测文件的字符集,但对于没有 BOM 的文件,我必须做一些黑客操作来检测字符集(参见
GetFileCharset
)。
在以下 Delphi 代码(我使用 Delphi 11)中,我收到 2 个警告(请参阅相关行末尾的注释):
uses
WideStrUtils;
function GetFileCharset(const Filename: String): TEncoding;
var
StreamReader: TStreamReader;
FallbackEncoding: TEncoding;
CurrLine: String;
begin
FallbackEncoding := TEncoding.ANSI;
try
StreamReader := TStreamReader.Create(Filename, FallbackEncoding, True);
try
Result := StreamReader.CurrentEncoding;
if StreamReader.CurrentEncoding = FallbackEncoding then
begin
while not StreamReader.EndOfStream do
begin
CurrLine := StreamReader.ReadLine;
if IsUTF8String(CurrLine) then //[dcc32 Warning]: W1058 Implicit string cast with potential data loss from 'string' to 'RawByteString'
begin
Result := TEncoding.UTF8;
break;
end;
end;
end;
finally
StreamReader.Close;
StreamReader.Free;
end;
except on E : Exception do
Result := FallbackEncoding;
end;
end;
StreamWriter := TStreamWriter.Create(OutputFile, False, TEncoding.UTF8);
try
StreamReader := TStreamReader.Create(InputFile, GetFileCharset(CurrFile), True);
try
while not StreamReader.EndOfStream do
StreamWriter.WriteLine(UTF8Encode(StreamReader.ReadLine)); //[dcc32 Warning]: W1057 Implicit string cast from 'RawByteString' to 'string'
finally
StreamReader.Close;
StreamReader.Free;
end;
finally
StreamWriter.Close;
StreamWriter.Free;
end;
#1 对于
Implicit string cast
警告,我可以轻松做到:
StreamWriter.WriteLine(String(UTF8Encode(StreamReader.ReadLine)));
但是我想知道是否有更好的方法或者这里是否有潜在的危险?
#2 对于
Implicit string cast with potential data loss
,我不知道如何安全地解决这个问题。
#3 有没有比我所做的更好的方法来检测文件字符集?
对于第一个警告:
StreamReader.ReadLine()
返回一个UTF-16编码的UnicodeString
,它已经使用读者指定的Encoding
进行了字符集解码(在您的情况下,always将是TEncoding.ANSI
)。
IsUTF8String()
接受 RawByteString
并返回其原始字节是否以 UTF-8 编码。
当使用 16 位字符串而不是 8 位字符串调用
IsUTF8String()
时,您将获得隐式转换(此外,将 UnicodeString
转换为 RawByteString
是非 ASCII 字符的 lossy 转换,因此您一开始就不应该这样做)。
在您的情况下,IsUTF8String()
将never返回True
,因为UnicodeString
到RawByteString
的转换将never产生UTF-8字符串。所以,你可以完全摆脱这个测试。
对于您正在尝试的内容,您需要分析文件的原始字节,而不是来自ReadLine()
的
解码字符。此外,ASCII 是 ANSI 和 UTF-8 的子集,两者的编码完全相同,因此您需要分析整个文件(或至少直到遇到非 ASCII 字符)才能确定字符集正确。
对于第二次警告:
StreamWriter.WriteLine()
接受 UTF-16 UnicodeString
作为输入,但您改为向其传递 UTF-8 RawByteString
。您的解决方法只是使转换变得明确,但不会改变结果。虽然这是一种无损转换,但您实际上根本不需要 UTF8Encode()
。让 StreamWriter
在内部为您处理 UTF-8。