2016-02-29 28 views
8

Ho un bug report EurekaLog che mostra un EEncodingError. Il registro punta a TFile.AppendAllText. Io chiamo TFile.AppendAllText è questa procedura di mine:Cosa potrebbe causare "Non esiste alcuna mappatura per il carattere Unicode nella codepage multibyte di destinazione"?

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; 

Queste sono le informazioni da EurekaLog.

enter image description here

enter image description here

Che cosa può causare questo accada?

risposta

11

Questo programma riproduce l'errore che si segnala:

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

Qui ho scritto il file originale come ANSI. E quindi chiamato AppendAllText che proverà a scrivere come UTF-8. Ciò che accade è che si finisce in questa funzione:

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; 

L'errore deriva da questa linea:

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

Il problema è che UTFStr non è infatti valida UTF-8. E quindi TEncoding.UTF8.GetString(UTFStr) genera un'eccezione.

Questo è un difetto in TFile.AppendAllBytes. Dato che sa perfettamente che UTFStr è codificato ANSI, non ha assolutamente senso chiamare il numero TEncoding.UTF8.GetString.

È necessario inviare un bug report a Embarcadero per questo difetto che esiste ancora in Delphi 10 Seattle. Nel frattempo non dovresti usare TFile.AppendAllBytes.

+0

Che dire di TStreamReader? Sembra un'alternativa decente e non si basa su IOUtils. – Ampere

+0

Perf è un po 'dubbia. Non voglio consigliarlo senza conoscere la durata del file e chi lo modifica. –