当非 unicode 页面是韩文 (949) 时,为什么 ReadLn 会错误解释 UTF8 文本?
Why does ReadLn mis-interpret UTF8 text when non-unicode page is Korean (949)?
在 Delphi XE2 中,当系统区域设置为英语时,我只能使用 AssignFile
和 ReadLn()
例程读取和显示 unicode 字符(来自 UTF8 编码文件)。
失败的地方
如果我将非 unicode 应用程序的系统区域设置为韩语(我认为是代码页 949)并重复相同的读取,我的一些 UTF8 多字节对将被替换为 F
。这仅适用于使用 ReadLn
而不是使用 TFile.ReadAllText(aFilename, TEncoding.UTF8)
或 TFileStream.Read()
.
测试
1. 我创建了一个文本文件,UTF8 w/o BOM (Notepad++),其中包含以下字符(第二行显示的十六进制等价物):
테스트
ed 85 8c ec 8a a4 ed 8a b8
用TMemo控件写一个Delphi XE 2 Windows表单应用程序:
procedure TForm1.ReadFile(aFilename:string);
var
gFile : TextFile;
gLine : RawByteString;
gWideLine : string;
begin
AssignFile(gFile, aFilename);
try
Reset(gFile);
Memo1.Clear;
while not EOF(gFile) do
begin
ReadLn(gFile, gLine);
gWideLine := UTF8ToWideString(gLine);
Memo1.Lines.Add(gWideLine);
end;
finally
CloseFile(gFile);
end;
end;
我在执行 UTF8ToWideString
对话之前检查了 gLine
的内容,在英语/美国语言环境下 Windows 它是:
$ED C $EC A $A4 $ED A $B8
顺便说一句,如果我读取带有 BOM 的同一个文件,我会得到正确的 3 字节前导码,并且执行 UTF8 解码时的输出是相同的。目前一切正常!
切换 Windows 7 (x64) 以使用韩语作为不支持 Unicode 的应用程序的代码页(区域和语言 --> 管理选项卡 --> 更改系统区域设置 --> 韩语(韩国).重启电脑。
使用上述应用程序读取相同的文件(UTF8 w/o BOM)并且 gLine
现在具有十六进制值:
F C $EC A $A4 F F
TMemo 中的输出:?????
假设 ReadLn()
(和 Read()
正试图将 UTF8 序列映射为韩语多字节序列(即试图解释 $ED $85,不能所以潜艇在问号 $3F).
使用TFileStream
准确读取预期的字节数(9 w/o BOM),内存中的十六进制现在正好是:
$ED C $EC A $A4 $ED A $B8
TMemo 中的输出:테스트(完美!)
问题:懒惰 - 我有很多遗留例程逐行解析可能很大的文件,我想确保我不需要编写例程手动读取每个文件的新行。
问题:
为什么 Read()
没有返回我在文件中找到的确切字节字符串?是不是因为我使用的是 TextFile
类型,所以 Delphi 正在使用非 unicode 代码页进行一定程度的解释?
是否有逐行读取 UTF8 编码文件的内置方法?
更新:
刚刚看到 Rob Kennedy 对 this post 的解决方案,它让我重新认识了 TStreamReader,它逐行回答了关于优雅地读取 UTF8 文件的问题。
Is there a built in way to read a UTF8 encoded file line by line?
使用TStreamReader
. It has a ReadLine()
方法。
procedure TForm1.ReadFile(aFilename:string);
var
gFile : TStreamReader;
gLine : string;
begin
Memo1.Clear;
gFile := TStreamReader.Create(aFilename, TEncoding.UTF8, True);
try
while not gFile.EndOfStream do
begin
gLine := gFile.ReadLine;
Memo1.Lines.Add(gLine);
end;
finally
gFile.Free;
end;
end;
话虽如此,这个特定示例可以大大简化:
procedure TForm1.ReadFile(aFilename:string);
begin
Memo1.Lines.LoadFromFile(aFilename, TEncoding.UTF8);
end;
在 Delphi XE2 中,当系统区域设置为英语时,我只能使用 AssignFile
和 ReadLn()
例程读取和显示 unicode 字符(来自 UTF8 编码文件)。
失败的地方
如果我将非 unicode 应用程序的系统区域设置为韩语(我认为是代码页 949)并重复相同的读取,我的一些 UTF8 多字节对将被替换为 F
。这仅适用于使用 ReadLn
而不是使用 TFile.ReadAllText(aFilename, TEncoding.UTF8)
或 TFileStream.Read()
.
测试
1. 我创建了一个文本文件,UTF8 w/o BOM (Notepad++),其中包含以下字符(第二行显示的十六进制等价物):
테스트
ed 85 8c ec 8a a4 ed 8a b8
用TMemo控件写一个Delphi XE 2 Windows表单应用程序:
procedure TForm1.ReadFile(aFilename:string); var gFile : TextFile; gLine : RawByteString; gWideLine : string; begin AssignFile(gFile, aFilename); try Reset(gFile); Memo1.Clear; while not EOF(gFile) do begin ReadLn(gFile, gLine); gWideLine := UTF8ToWideString(gLine); Memo1.Lines.Add(gWideLine); end; finally CloseFile(gFile); end; end;
我在执行
UTF8ToWideString
对话之前检查了gLine
的内容,在英语/美国语言环境下 Windows 它是:$ED C $EC A $A4 $ED A $B8
顺便说一句,如果我读取带有 BOM 的同一个文件,我会得到正确的 3 字节前导码,并且执行 UTF8 解码时的输出是相同的。目前一切正常!
切换 Windows 7 (x64) 以使用韩语作为不支持 Unicode 的应用程序的代码页(区域和语言 --> 管理选项卡 --> 更改系统区域设置 --> 韩语(韩国).重启电脑。
使用上述应用程序读取相同的文件(UTF8 w/o BOM)并且
gLine
现在具有十六进制值:F C $EC A $A4 F F
TMemo 中的输出:?????
假设
ReadLn()
(和Read()
正试图将 UTF8 序列映射为韩语多字节序列(即试图解释 $ED $85,不能所以潜艇在问号 $3F).使用
TFileStream
准确读取预期的字节数(9 w/o BOM),内存中的十六进制现在正好是:$ED C $EC A $A4 $ED A $B8
TMemo 中的输出:테스트(完美!)
问题:懒惰 - 我有很多遗留例程逐行解析可能很大的文件,我想确保我不需要编写例程手动读取每个文件的新行。
问题:
为什么
Read()
没有返回我在文件中找到的确切字节字符串?是不是因为我使用的是TextFile
类型,所以 Delphi 正在使用非 unicode 代码页进行一定程度的解释?是否有逐行读取 UTF8 编码文件的内置方法?
更新:
刚刚看到 Rob Kennedy 对 this post 的解决方案,它让我重新认识了 TStreamReader,它逐行回答了关于优雅地读取 UTF8 文件的问题。
Is there a built in way to read a UTF8 encoded file line by line?
使用TStreamReader
. It has a ReadLine()
方法。
procedure TForm1.ReadFile(aFilename:string);
var
gFile : TStreamReader;
gLine : string;
begin
Memo1.Clear;
gFile := TStreamReader.Create(aFilename, TEncoding.UTF8, True);
try
while not gFile.EndOfStream do
begin
gLine := gFile.ReadLine;
Memo1.Lines.Add(gLine);
end;
finally
gFile.Free;
end;
end;
话虽如此,这个特定示例可以大大简化:
procedure TForm1.ReadFile(aFilename:string);
begin
Memo1.Lines.LoadFromFile(aFilename, TEncoding.UTF8);
end;