7

我有一个以这一行开头的 UTF-8 文本文件:

<HEAD><META name=GENERATOR content="MSHTML 10.00.9200.16521"><body>

当我TFile.ReadAllText使用 TEncoding.UTF8 读取此文件时:

MyStr := TFile.ReadAllText(ThisFileNamePath, TEncoding.UTF8);

那么文本文件的前 3 个字符被省略,所以 MyStr 的结果是:

'AD><META name=GENERATOR content="MSHTML 10.00.9200.16521"><body>...'

但是,当我在TFile.ReadAllText没有 TEncoding.UTF8 的情况下读取此文件时:

MyStr := TFile.ReadAllText(ThisFileNamePath);

然后文件被完全正确地读取:

<HEAD><META name=GENERATOR content="MSHTML 10.00.9200.16521"><body>...

TFile.ReadAllText有错误吗?

4

2 回答 2

9

前三个字节被跳过,因为 RTL 代码假定文件包含 UTF-8 BOM。显然你的文件没有。

该类TUTF8Encoding实现了一个GetPreamble指定UTF-8BOM 的方法。并ReadAllBytes跳过您传递的编码指定的前导码。

一种简单的解决方案是将文件读入字节数组,然后将TEncoding.UTF8.GetString其解码为字符串。

var
  Bytes: TBytes;
  Str: string;
....
Bytes := TFile.ReadAllBytes(FileName);
Str := TEncoding.UTF8.GetString(Bytes);

更全面的替代方法是创建一个TEncoding忽略 UTF-8 BOM 的实例。

type
  TUTF8EncodingWithoutBOM = class(TUTF8Encoding)
  public
    function Clone: TEncoding; override;
    function GetPreamble: TBytes; override;
  end;

function TUTF8EncodingWithoutBOM.Clone: TEncoding;
begin
  Result := TUTF8EncodingWithoutBOM.Create;
end;

function TUTF8EncodingWithoutBOM.GetPreamble: TBytes;
begin
  Result := nil;
end;

实例化其中之一(每个进程只需要一个实例)并将其传递给TFile.ReadAllText.

使用的单例实例的优点TUTF8EncodingWithoutBOM是您可以在任何需要TEncoding.

于 2013-06-16T12:57:26.030 回答
0

实际上,您传递给 ReadAllText 的值不是默认编码(当您检查其实现方式时),如果您传递 Nil 以外的其他内容,则它是强制编码。在内部,它调用其他具有额外默认编码参数的 TEncoding 方法(当 foundEncoding var 参数设置为 nil 并且无法检测到编码时)

我试图根据 TFile.ReadAllText 代码制作与 Stream 一起使用的方法,并最终得到了这个,注意我使用的“DefaultEncoding=nil”(=nil,因此不需要额外的重载方法)和“ForceDefaultEncoding=false " (说我们总是想使用那个编码)

function ReadAllBytes(const Stream: TStream): TBytes;
begin
  var LFileSize := Stream.Size;
  {$IFDEF CPU32BITS}
  if LFileSize > MaxInt then
    raise EInOutError.CreateRes(@SFileTooLong);
  {$ENDIF}
  SetLength(Result, LFileSize);
  Stream.ReadBuffer(result, Length(result));
end;

function ReadAllText(const Stream: TStream; const DefaultEncoding: TEncoding = nil; const ForceDefaultEncoding: Boolean = false): string;
var FoundEncoding: TEncoding;
begin
  if ForceDefaultEncoding then
    FoundEncoding := DefaultEncoding;
  var Buff := ReadAllBytes(Stream);
  var BOMLength := TEncoding.GetBufferEncoding(Buff, FoundEncoding, DefaultEncoding);
  result := FoundEncoding.GetString(Buff, BOMLength, Length(Buff) - BOMLength);
end;
于 2021-11-25T23:08:01.030 回答