为什么当非unicode页面是韩语时,ReadLn会误解UTF8文本(949)?

时间:2015-03-21 17:54:12

标签: delphi unicode utf-8 codepages

在Delphi XE2中,当系统区域设置为英语时,我只能使用AssignFileReadLn()例程读取和显示unicode字符(来自UTF8编码文件)。

失败的地方
如果我将非unicode应用程序的系统区域设置设置为朝鲜语(代码页949,我认为)并重复相同的读取,我的一些UTF8多字节对将被$3F替换。这仅适用于使用ReadLn,而不适用于使用TFile.ReadAllText(aFilename, TEncoding.UTF8)TFileStream.Read()

测试
1.我创建了一个文本文件,UTF8没有BOM(Notepad ++),其中包含以下字符(第二行显示的十六进制等效值):

테스트
ed 85 8c ec 8a a4 ed 8a b8
  1. 使用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;
    
  2. 我在执行gLine对话之前检查了UTF8ToWideString的内容,并在英语/美国语言环境Windows下进行了检查:

    $ED $85 $8C $EC $8A $A4 $ED $8A $B8

  3. 顺便说一句,如果我用BOM读取相同的文件,我会得到正确的3字节前同步码,并且执行UTF8解码时的输出是相同的。一切都好!到目前为止!

    1. 将Windows 7(x64)切换为使用韩语作为没有Unicode支持的应用程序的代码页(区域和语言 - >管理选项卡 - >更改系统区域设置 - >韩语(韩国)。重新启动计算机。

    2. 使用上述应用程序读取同一文件(没有BOM的UTF8),gLine现在有十六进制值:

      $3F $8C $EC $8A $A4 $3F $3F

      TMemo中的输出:?�스??

    3. 假设ReadLn()(和Read()就此而言)试图将UTF8序列映射为韩语多字节序列(即尝试解释$ ED $ 85,可以' t等等问题标记$ 3F)。

    4. 使用TFileStream准确读取预期的字节数(9 w / o BOM),内存中的十六进制现在完全正确:

      $ED $85 $8C $EC $8A $A4 $ED $8A $B8

      TMemo中的输出:테스트(完美!)

    5. 问题:懒惰 - 我有很多遗留的例程,可以逐行解析潜在的大型文件,我想确保我不需要编写例程来手动读取每个文件的新行。

      问题:

      1. 为什么Read()没有返回文件中找到的确切字节字符串?是因为我使用TextFile类型,因此Delphi使用非unicode代码页进行一定程度的解释?

      2. 是否有内置方法逐行读取UTF8编码文件?

      3. 更新

        刚刚遇到Rob Kennedy的this post解决方案,它将我重新介绍给了TStreamReader,它回答了有关逐行优雅读取UTF8文件的问题。

1 个答案:

答案 0 :(得分:6)

  

是否有逐行读取UTF8编码文件的内置方式?

使用TStreamReader。它有一个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;