Chương trình này tái tạo các lỗi mà bạn báo cáo:
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.IOUtils;
var
FileName: string;
begin
try
FileName := TPath.GetTempFileName;
TFile.WriteAllText(FileName, 'é', TEncoding.ANSI);
TFile.AppendAllText(FileName, 'é');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Ở đây tôi đã viết các tập tin ban đầu như ANSI. Và sau đó được gọi là AppendAllText
sẽ cố gắng viết dưới dạng UTF-8. Chuyện gì xảy ra là chúng ta kết thúc trong chức năng này:
class procedure TFile.AppendAllText(const Path, Contents: string);
var
LFileStream: TFileStream;
LFileEncoding: TEncoding; // encoding of the file
Buff: TBytes;
Preamble: TBytes;
UTFStr: TBytes;
UTF8Str: TBytes;
begin
CheckAppendAllTextParameters(Path, nil, False);
LFileStream := nil;
try
try
LFileStream := DoCreateOpenFile(Path);
// detect the file encoding
LFileEncoding := GetEncoding(LFileStream);
// file is written is ASCII (default ANSI code page)
if LFileEncoding = TEncoding.ANSI then
begin
// Contents can be represented as ASCII;
// append the contents in ASCII
UTFStr := TEncoding.ANSI.GetBytes(Contents);
UTF8Str := TEncoding.UTF8.GetBytes(Contents);
if TEncoding.UTF8.GetString(UTFStr) = TEncoding.UTF8.GetString(UTF8Str) then
begin
LFileStream.Seek(0, TSeekOrigin.soEnd);
Buff := TEncoding.ANSI.GetBytes(Contents);
end
// Contents can be represented only in UTF-8;
// convert file and Contents encodings to UTF-8
else
begin
// convert file contents to UTF-8
LFileStream.Seek(0, TSeekOrigin.soBeginning);
SetLength(Buff, LFileStream.Size);
LFileStream.ReadBuffer(Buff, Length(Buff));
Buff := TEncoding.Convert(LFileEncoding, TEncoding.UTF8, Buff);
// prepare the stream to rewrite the converted file contents
LFileStream.Size := Length(Buff);
LFileStream.Seek(0, TSeekOrigin.soBeginning);
Preamble := TEncoding.UTF8.GetPreamble;
LFileStream.WriteBuffer(Preamble, Length(Preamble));
LFileStream.WriteBuffer(Buff, Length(Buff));
// convert Contents in UTF-8
Buff := TEncoding.UTF8.GetBytes(Contents);
end;
end
// file is written either in UTF-8 or Unicode (BE or LE);
// append Contents encoded in UTF-8 to the file
else
begin
LFileStream.Seek(0, TSeekOrigin.soEnd);
Buff := TEncoding.UTF8.GetBytes(Contents);
end;
// write Contents to the stream
LFileStream.WriteBuffer(Buff, Length(Buff));
except
on E: EFileStreamError do
raise EInOutError.Create(E.Message);
end;
finally
LFileStream.Free;
end;
end;
Các lỗi bắt nguồn từ dòng này:
if TEncoding.UTF8.GetString(UTFStr) = TEncoding.UTF8.GetString(UTF8Str) then
Vấn đề là UTFStr
không có trong thực tế có giá trị UTF-8
. Và do đó TEncoding.UTF8.GetString(UTFStr)
ném một ngoại lệ.
Đây là lỗi trong TFile.AppendAllBytes
. Cho rằng nó biết hoàn toàn rõ ràng rằng UTFStr
được mã hóa ANSI
, nó không có ý nghĩa gì cả cho nó để gọi TEncoding.UTF8.GetString
.
Bạn nên gửi báo cáo lỗi cho Embarcadero vì lỗi này vẫn tồn tại ở Delphi 10 Seattle. Trong thời gian chờ đợi, bạn không nên sử dụng TFile.AppendAllBytes
.
Còn TStreamReader thì sao? Có vẻ là một thay thế khá và nó không dựa trên IOUtils. – Ampere
Perf là một chút tinh ranh. Tôi không muốn tư vấn mà không có kiến thức về tuổi thọ của tập tin và những người khác sửa đổi nó. –