有没有一种简单的方法来解决Delphi utf8的问题

我发现(困难的方式),如果文件具有有效的UTF-8 BOM但包含任何无效的UTF8编码,并且可以通过任何Delphi(2009+)编码启用的方法(如LoadFromFile )读取,则结果是一个完全空的文件,没有错误指示。 在我的几个应用程序中,我宁愿仅仅丢失一些不好的编码,即使在这种情况下我也没有收到错误报告。

调试显示MultiByteToWideChar被调用两次,首先得到输出缓冲区大小,然后进行转换。 但是TEncoding.UTF8包含这些调用的私有FMBToWCharFlags值,并且这是使用MB_ERR_INVALID_CHARS值初始化的。 所以获取charcount的调用返回0,并且加载的文件完全是空的。 在没有标志的情况下调用这个API会'悄然丢弃非法代码点'。

我的问题是如何最好地通过Encoding区域中的类嵌套来解决事实,即这是一个私有值(因为它是所有线程的类变量,所以需要这样做)。 我想我可以使用Marco Cantu的Delphi 2009书中的指导来添加自定义的UTF8编码。 如果MultiByteToWideChar在没有标志的情况下再次调用它时返回了编码错误,它可以选择引发异常。 但是这并不能解决如何使用自定义编码而不是Tencoding.UTF8

如果我可以在初始化时将它设置为应用程序的默认值,可能通过实际修改Tencoding.UFT8的类var,这可能就足够了。

当然,我需要一个解决方案,而无需等待提交质量控制报告,要求提供更强大的设计,让它接受,并看到它改变。

任何想法都会非常受欢迎。 有人可以证实,这仍然是XE4的问题,我还没有安装?


当我第一次更新Indy来支持TEncoding ,我遇到了MB_ERR_INVALID_CHARS问题,最后为UTF-8处理实现了一个自定义的TEncoding - MB_ERR_INVALID_CHARS类,以避免指定MB_ERR_INVALID_CHARS 。 我没想过要使用班级帮手。

但是,这个问题不仅限于UTF-8。 任何TEncoding类的任何解码失败都会导致空白结果,而不是引发异常。 为什么Embarcadero选择了这种路线,当大多数RTL / VCL使用异常时,这种情况超出了我的想象。 没有提出错误例外导致Indy中相当多的问题必须解决。


这可以很简单地完成,至少在Delphi XE5中(没有检查早期版本)。 只是实例化你自己的TUTF8Encoding

procedure LoadInvalidUTF8File(const Filename: string);
var
  FEncoding: TUTF8Encoding;
begin
  FEncoding := TUTF8Encoding.Create(CP_UTF8, 0, 0); 
                      // Instead of CP_UTF8, MB_ERR_INVALID_CHARS, 0
  try
    with TStringList.Create do
    try
      LoadFromFile(Filename, FEncoding);
      // ...
    finally
      Free;
    end;
  finally
    FEncoding.Free;
  end;
end;

这里唯一的问题是,新实例化的TUTF8EncodingIsSingleByte属性被错误地设置为False ,但是这个属性目前在Delphi源码的任何地方都没有使用。


部分解决方法是强制UTF8编码全局抑制MB_ERR_INVALID_CHARS 。 对于我来说,这避免了引发异常的需要,因为我发现它使MultiByteToWideChar不太“沉默”:它实际上插入了$fffd字符(Unicode'替换字符'),然后我可以在重要的情况下找到它。 以下代码执行此操作:

unit fixutf8;
interface
uses System.Sysutils;
type
  TUTF8fixer = class helper for Tmbcsencoding
  public
    procedure setflag0;
  end;

implementation
procedure TUTF8fixer.setflag0;
{$if CompilerVersion = 31}
asm
  XOR ECX,ECX
  MOV Self.FMBToWCharFlags,ECX
end;
{$else}
begin
  Self.FMBToWCharFlags := 0;
end;
{$endif}

procedure initencoding;
begin
  (Tencoding.UTF8 as TmbcsEncoding).setflag0;
end;

initialization
  initencoding;
end.

一个更有用的原则性修复需要将对MultiByteToWideChar的调用更改为不使用MB_ERR_INVALID_CHARS ,并使用此标志进行初始调用,以便在加载完成后引发异常,以指示字符将被替换。

在这个问题上有相关的质量控制报告,包括76571,79042和111980.第一个已经“按设计”解决。

(编辑与德尔福柏林合作)

链接地址: http://www.djcxy.com/p/16169.html

上一篇: Is there an easy way to work around a Delphi utf8

下一篇: External Exception EEFFACE in Delphi