【问题标题】:TStringList behavior with non ANSI files非 ANSI 文件的 TStringList 行为
【发布时间】:2013-04-26 15:44:29
【问题描述】:

在我的应用程序中,当我想要导入文件时,我使用 TStringList。

但是,当有人从Excel中导出数据时,文件编码为UCS-2 Little Endian,TStringList无法读取数据。

有什么方法可以验证这种情况,识别文本编码并向用户发送警告,指出提供的文本不兼容?

为了清楚起见,用户将只提供纯文本..字母和数字,否则,我必须发送警告。

没有 BOM 的 Unicode 文件很好。 (TStringList 可以读取!)
ANSI 文件太。 (TStringList 可以读取!)
如果有办法删除它,即使是带有 BOM 的 Unicode 也会很好。 (TStringList 可以读取!但带有“i”“>>”和“reverse ?”字符,属于 BOM 字节)

【问题讨论】:

标签: file delphi encoding delphi-6 tstringlist


【解决方案1】:

我在 Delphi 6 中使用了以下函数来检测 Unicode BOM。

const
  //standard byte order marks (BOMs)
  UTF8BOM:              array [0..2] of AnsiChar = #$EF#$BB#$BF;
  UTF16LittleEndianBOM: array [0..1] of AnsiChar = #$FF#$FE;
  UTF16BigEndianBOM:    array [0..1] of AnsiChar = #$FE#$FF;
  UTF32LittleEndianBOM: array [0..3] of AnsiChar = #$FF#$FE#$00#$00;
  UTF32BigEndianBOM:    array [0..3] of AnsiChar = #$00#$00#$FE#$FF;

function FileHasUnicodeBOM(const FileName: string): Boolean;
var
  Buffer: array [0..3] of AnsiChar;
  Stream: TFileStream;
begin
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); // Allow other programs read access at the same time.
  Try
    FillChar(Buffer, SizeOf(Buffer), $AA);//fill with characters that we are not expecting then...
    Stream.Read(Buffer, SizeOf(Buffer));  //...read up to SizeOf(Buffer) bytes - there may not be enough
    //use Read rather than ReadBuffer so the no exception is raised if we can't fill Buffer
  Finally
    FreeAndNil(Stream);
  End;
  Result := CompareMem(@UTF8BOM,              @Buffer, SizeOf(UTF8BOM))              or
            CompareMem(@UTF16LittleEndianBOM, @Buffer, SizeOf(UTF16LittleEndianBOM)) or
            CompareMem(@UTF16BigEndianBOM,    @Buffer, SizeOf(UTF16BigEndianBOM))    or
            CompareMem(@UTF32LittleEndianBOM, @Buffer, SizeOf(UTF32LittleEndianBOM)) or
            CompareMem(@UTF32BigEndianBOM,    @Buffer, SizeOf(UTF32BigEndianBOM));
end;

这将检测所有标准 BOM。如果这是您想要的行为,您可以使用它来阻止此类文件。

您声明 Delphi 6 TStringList 可以加载没有 BOM 的 16 位编码文件。虽然可能是这种情况,但您会发现,对于 ASCII 范围内的字符,每隔一个字符都是#0。我猜这不是你想要的。

如果您想检测没有 BOM 的文件的文本是 Unicode,那么您可以使用 IsTextUnicode。但是,它可能会产生误报。在这种情况下,我怀疑请求宽恕比请求许可更好。

现在,如果我是你,我实际上不会尝试阻止 Unicode 文件。我会读它们。使用 TNT Unicode 库。你想要的课程叫做TWideStringList

【讨论】:

  • USC2 是早期 Unicode 的旧格式。这是一个固定长度的 16 位编码。我相信 Windows NT 的第一个版本是基于 UCS2 的,早期的 Java 也是如此。但是 UTF-16 是一种可变长度编码。一些代码点需要多个字符元素。那些使用代理对。但是,据我了解,UCS2 几乎是 UTF-16 的一个子集。如果您像读取 UTF-16 一样读取 UCS2 文件,您将获得合理的数据。
  • 是的,这将阻止任何包含已知 Unicode BOM 的文件
  • "detect Unicode" 是相当大胆的声明,你的代码只检测 BOM 的存在并且打开芝麻任何没有 BOM 的文件。
  • @user539484 是的,把名字改成TextFileHasKnownUnicodeBOM。当然不可能完全准确地检测 Unicode。
  • 是的,您应该更改名称,不带 BOM 的 UTF-8 文件很常见。诸如IsTextUnicodeDetectInputCodepage 之类的 MS 启发式实现更加可靠。
猜你喜欢
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多