2010-10-11 21 views
7

Delphi 2009 y posteriores usan cadenas Unicode para su tipo de cadena predeterminado. A mi entender unicode char es en realidad el valor de 16 bits o 2 bytes (nota: entiendo que hay posibilidad de 3 o 4 bytes de char, pero consideremos el caso más habitual). Sin embargo, descubrí que TStringStream no es muy confiable para manipular estas cadenas. Por ejemplo, la propiedad TStringStream.Size devuelve la longitud de la cadena, mientras que creo que debería devolver el conteo de bytes de la cadena contenida. De acuerdo, puedes ajustarlo por tu cuenta, pero lo que realmente me ha confundido más es: TStringStream no lee ni escribe en un búfer confiablemente.Cadena Unicode y TStringStream

Compruebe el siguiente código (es una prueba DUnit y siempre falla). Por favor, díganme dónde está el problema (estaba usando D2010 al probar el código).

procedure TestTCPackage.TestStringStream; 
const 
    cCount = 10; 
    cOrdMaxChar = Ord(High(Char)); 
var 
    B: Pointer; 
    SW, SR: TStringStream; 
    T: string; 
    i, j, k : Integer; 
    vStrings: array [0..cCount-1] of string; 
begin 
    RandSeed := GetTickCount; 
    for i := 0 to cCount - 1 do 
    begin 
    j := Random(100) + 1; 
    SetLength(vStrings[i], j); 
    for k := 1 to j do 
     // fill string with random char (but no #0) 
     vStrings[i][k] := Char(Random(cOrdMaxChar-1) + 1); 
    end; 

    for i := 0 to cCount - 1 do 
    begin 
    SW := TStringStream.Create(vStrings[i]); 
    try 
     GetMem(B, SW.Size * SizeOf(Char)); 
     try 
     SW.Read(B^, SW.Size * SizeOf(Char)); 

     SR := TStringStream.Create; 
     try 
      SR.Write(B^, SW.Size * SizeOf(Char)); 
      SR.Position := 0; 

      // check the string in the TStringStream with original value 
      Check(SR.DataString = vStrings[i]); 
     finally 
      SR.Free; 
     end; 
     finally 
     FreeMem(B); 
     end; 
    finally 
     SW.Free; 
    end; 
    end; 
end; 

Nota: Ya he intentado utilizar una instancia de TMemoryStream como intermediario de la lectura/escritura de la memoria intermedia y el uso de la CopyFrom TStringStream para leer el contenido de ese TMemoryStream con mismo efecto defecto.

Respuesta

5

Después de leer this post (y gracias a Serg que proporcionó la respuesta a esa pregunta) y la respuesta de Barry Kelly, he encontrado el problema. TStringStream realmente está usando la codificación ASCII/ansistring por defecto. Entonces, incluso si el tipo de cadena predeterminado es Unicode, a menos que específicamente se lo indique, no usará la codificación Unicode. Personalmente, creo que es extraño. Tal vez para facilitar la conversión de códigos antiguos.

Así que hay que establecer específicamente la codificación del TStringStream a TEncoding.Unicode para manipular cadena Unicode correctamente.

Aquí está mi código modificado que pasa la prueba DUnit es:

procedure TestTCPackage.TestStringStream; 
const 
    cCount = 10; 
    cOrdMaxChar = Ord(High(Char)); 
var 
    B: Pointer; 
    SW, SR: TStringStream; 
    i, j, k : Integer; 
    vStrings: array [0..cCount-1] of string; 
begin 
    RandSeed := GetTickCount; 
    for i := 0 to cCount - 1 do 
    begin 
    j := Random(100) + 1; 
    SetLength(vStrings[i], j); 
    for k := 1 to j do 
     // fill string with random char (but no #0) 
     vStrings[i][k] := Char(Random(cOrdMaxChar-1) + 1); 
    end; 

    for i := 0 to cCount - 1 do 
    begin 
    SW := TStringStream.Create(vStrings[i], ***TEncoding.Unicode***); 
    try 
     GetMem(B, SW.Size); 
     try 
     SW.ReadBuffer(B^, SW.Size); 

     SR := TStringStream.Create('', ***TEncoding.Unicode***); 
     try 
      SR.WriteBuffer(B^, SW.Size); 
      SR.Position := 0; 

      // check the string in the TStringStream with original value 
      Check(SR.DataString = vStrings[i]); 
     finally 
      SR.Free; 
     end; 
     finally 
     FreeMem(B); 
     end; 
    finally 
     SW.Free; 
    end; 
    end; 
end; 

Última nota: Unicode muerde! : D

5

Las cadenas Unicode no son para almacenamiento de datos; use TBytes para eso. TStringStream usa su codificación asociada (la propiedad Encoding) para codificar cadenas pasadas con WriteString, y cadenas de decodificación leídas con ReadString o la propiedad DataString.

+0

En realidad, el código no trata de almacenamiento de datos, pero después de leer su respuesta y otra publicación, he encontrado el problema. Es que TStringStream usa de manera predeterminada ASCII/ansistring. ¡Gracias! – Luthfi