2016-02-29 44 views
8

Mam raport błędów EurekaLog pokazujący EEncodingError. Dziennik wskazuje na TFile.AppendAllText. Wzywam TFile.AppendAllText jest to procedura kopalni:Co może spowodować "Brak odwzorowania znaku Unicode na docelowej, wielobajtowej stronie kodowej"?

procedure WriteToFile(CONST FileName: string; CONST uString: string; CONST WriteOp: WriteOpperation; ForceFolder: Boolean= FALSE);  // Works with UNC paths 
begin 
if NOT ForceFolder 
OR (ForceFolder AND ForceDirectoriesMsg(ExtractFilePath(FileName))) then 
    if WriteOp= (woOverwrite) 
    then IOUtils.TFile.WriteAllText (FileName, uString) 
    else IOUtils.TFile.AppendAllText(FileName, uString); 
end; 

to informacje z EurekaLog.

enter image description here

enter image description here

Co może być przyczyną tak się stało?

Odpowiedz

11

Ten program odtwarza błąd, że raport:

{$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. 

Tutaj Pisałem plik oryginalny jak ANSI. A następnie o nazwie AppendAllText, który spróbuje napisać jako UTF-8. Co się dzieje, jest to, że kończy się w tej funkcji:

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; 

Błąd wynika z tej linii:

if TEncoding.UTF8.GetString(UTFStr) = TEncoding.UTF8.GetString(UTF8Str) then 

Problemem jest to, że UTFStr nie jest w istocie ważny UTF-8. I dlatego TEncoding.UTF8.GetString(UTFStr) zgłasza wyjątek.

To jest defekt w TFile.AppendAllBytes. Ponieważ doskonale wie, że kodowanie UTFStr jest ANSI, nie ma żadnego sensu, aby zadzwonić pod numer TEncoding.UTF8.GetString.

Powinieneś przesłać zgłoszenie błędu do Embarcadero za tę wadę, która nadal występuje w Delphi 10 Seattle. W międzyczasie nie powinieneś używać TFile.AppendAllBytes.

+0

Co z TStreamReaderem? Wygląda na przyzwoitą alternatywę i nie opiera się na IOUtils. – Ampere

+0

Perf jest nieco podejrzany. Nie chcę doradzać bez znajomości czasu istnienia pliku i kto go modyfikuje. –