3

次のようなライブラリ関数があります。

class function TFileUtils.ReadTextStream(const AStream: TStream): string;
var
  StringStream: TStringStream;
begin
  StringStream := TStringStream.Create('', TEncoding.Unicode);
  try
    // This is WRONG since CopyFrom might rewind the stream (see Remys comment)
    StringStream.CopyFrom(AStream, AStream.Size - AStream.Position);
    Result := StringStream.DataString;
  finally
    StringStream.Free;
  end;
end;

関数によって返される文字列を確認すると、最初の Char は (リトルエンディアン) BOM です。

TStringStream が BOM を無視しないのはなぜですか?

これを行うより良い方法はありますか?古いバージョンの Delphi との下位互換性は必要ありません。XE2 の実用的なソリューションで問題ありません。

4

2 に答える 2

9

は BOM を書き込まないため、BOM はTStreamソースから取得する必要があります。TStringStreamソースに BOM が存在する場合に BOM を無視する場合は、データをコピーする前に手動で行う必要があります。次に例を示します。

class function TFileUtils.ReadTextStream(const AStream: TStream): string;
var
  StreamPos, StreamSize: Int64;
  Buf: TBytes;
  NumBytes: Integer;
  Encoding: TEncoding;
begin
  Result := '';

  StreamPos := AStream.Position;
  StreamSize := AStream.Size - StreamPos;

  // Anything available to read?
  if StreamSize < 1 then Exit;

  // Read the first few bytes from the stream...
  SetLength(Buf, 4);
  NumBytes := AStream.Read(Buf[0], Length(Buf));
  if NumBytes < 1 then Exit;
  Inc(StreamPos, NumBytes);
  Dec(StreamSize, NumBytes);

  // Detect the BOM. If you know for a fact what the TStream data is encoded as, 
  // you can assign the Encoding variable to the appropriate TEncoding object and 
  // GetBufferEncoding() will check for that encoding's BOM only...
  SetLength(Buf, NumBytes);
  Encoding := nil;
  Dec(NumBytes, TEncoding.GetBufferEncoding(Buf, Encoding));

  // If any non-BOM bytes were read than rewind the stream back to that position...
  if NumBytes > 0 then
  begin
    AStream.Seek(-NumBytes, soCurrent);
    Dec(StreamPos, NumBytes);
    Inc(StreamSize, NumBytes);
  end else
  begin
    // Anything left to read after the BOM?
    if StreamSize < 1 then Exit;
  end;

  // Now read and decode whatever is left in the stream...
  StringStream := TStringStream.Create('', Encoding);
  try
    StringStream.CopyFrom(AStream, StreamSize);
    Result := StringStream.DataString;
  finally
    StringStream.Free;
  end;
end;
于 2013-01-11T01:48:31.623 に答える
3

明らかTStreamReaderに同じ問題に悩まされていません:

var
  StreamReader: TStreamReader;
begin
  StreamReader := TStreamReader.Create(AStream);
  try
    Result := StreamReader.ReadToEnd;
  finally
    StreamReader.Free;
  end;
end;

TStringListも動作します (whosrdaddy に感謝):

var
  Strings: TStringList;
begin
  Strings := TStringList.Create;
  try
    Strings.LoadFromStream(AStream);
    Result := Strings.Text;
  finally
    Strings.Free;
  end;
end;

また、両方のメソッドを測定しましたが、TStreamReader は約 2 倍高速のようです。

于 2013-01-10T18:27:31.340 に答える