Convertir UnicodeString a AnsiString
En los viejos tiempos, tenía una función que convertiría unWideString
a unaAnsiString
de la página de códigos especificada:
function WideStringToString(const Source: WideString; CodePage: UINT): AnsiString;
...
begin
...
// Convert source UTF-16 string (WideString) to the destination using the code-page
strLen := WideCharToMultiByte(CodePage, 0,
PWideChar(Source), Length(Source), //Source
PAnsiChar(cpStr), strLen, //Destination
nil, nil);
...
end;
Y todo funcionó. Pasé la función aUnicode cadena (es decir, datos codificados UTF-16) y lo convirtió en unAnsiString
, con el entendimiento de que los bytes en elAnsiString
caracteres representados de la página de códigos especificada.
Por ejemplo:
TUnicodeHelper.WideStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', 1252);
devolvería elWindows-1252
cadena codificada:
The qùíçk brown fôx jumped ovêr the lázÿ dog
Nota: Por supuesto, la información se perdió durante la conversión del juego de caracteres Unicode completo a los límites limitados de la página de códigos de Windows-1252:
Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ
(antes de)The qùíçk brown fôx jumped ovêr the lázÿ dog
(después)Pero las ventanasWideChartoMultiByte
hace un muy buen trabajo de mapeo de mejor ajuste; como está diseñado para hacer.
Ahora estamos en los tiempos posteriores.WideString
ahora es un paria, conUnicodeString
siendo la bondad Es un cambio intrascendente; ya que la función de Windows solo necesitaba unpuntero a una serie deWideChar
de todos modos (que unUnicodeString
también es). Entonces cambiamos la declaración para usarUnicodeString
en lugar:
funtion WideStringToString(const Source: UnicodeString; CodePage: UINT): AnsiString;
begin
...
end;
Ahora llegamos al valor de retorno. Yo tengo unAnsiString
que contiene los bytes:
54 68 65 20 71 F9 ED E7 The qùíç
6B 20 62 72 6F 77 6E 20 k brown
66 F4 78 20 6A 75 6D 70 fôx jump
65 64 20 6F 76 EA 72 20 ed ovêr
74 68 65 20 6C E1 7A FF the lázÿ
20 64 6F 67 dog
En los viejos tiempos eso estaba bien. Seguí el rastro de qué página de códigosAnsiString
realmente contenido; tuve querecuerda que el regresóAnsiString
no se codificó utilizando la configuración regional de la computadora (por ejemplo, Windows 1258), sino que se codificó utilizando otra página de códigos (elCodePage
página de código).
Pero en Delphi XE6 unAnsiString
también contiene secretamente la página de códigos:
The qùíçk brown fôx jumped ovêr the lázÿ dog
Esta página de códigos está mal. Delphi está especificando la página de códigos de mi computadora, en lugar de la página de códigos que es la cadena. Técnicamente esto no es un problema, siempre entendí que elAnsiString
estaba en una página de códigos particular, solo tenía que asegurarme de transmitir esa información.
Entonces, cuando quería decodificar la cadena, tenía que pasarle la página de códigos:
s := TUnicodeHeper.StringToWideString(s, 1252);
con
function StringToWideString(s: AnsiString; CodePage: UINT): UnicodeString;
begin
...
MultiByteToWideChar(...);
...
end;
Entonces una persona arruina todoEl problema era que en los viejos tiempos declaraba un tipo llamadoUtf8String
:
type
Utf8String = type AnsiString;
Porque era lo suficientemente común como para tener:
function TUnicodeHelper.WideStringToUtf8(const s: UnicodeString): Utf8String;
begin
Result := WideStringToString(s, CP_UTF8);
end;
y al revés:
function TUnicodeHelper.Utf8ToWideString(const s: Utf8String): UnicodeString;
begin
Result := StringToWideString(s, CP_UTF8);
end;
Ahora en XE6 tengo una función quetoma a Utf8String
. Si algún código existente en algún lugar fuera un UTF-8 codificadoAnsiString
e intente convertirlo a UnicodeString usandoUtf8ToWideString
fallaría:
s: AnsiString;
s := UnicodeStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', CP_UTF8);
...
ws: UnicodeString;
ws := Utf8ToWideString(s); //Delphi will treat s an CP1252, and convert it to UTF8
O peor, es la amplitud del código existente que hace:
s: Utf8String;
s := UnicodeStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', CP_UTF8);
La cadena devuelta quedará totalmente destrozada:
la función vuelveAnsiString(1252)
(AnsiString
etiquetado como codificado usando la página de códigos actual)el resultado devuelto se almacena en unAnsiString(65001)
cuerda (Utf8String
)Delphi convierte la cadena codificada UTF-8 en UTF-8 como si fuera 1252.Cómo avanzarIdealmente miUnicodeStringToString(string, codePage)
función (que devuelve unAnsiString
) podría establecer elCodePage
dentro de la cadena para que coincida con la página de códigos real usando algo comoSetCodePage
:
function UnicodeStringToString(s: UnicodeString; CodePage: UINT): AnsiString;
begin
...
WideCharToMultiByte(...);
...
//Adjust the codepage contained in the AnsiString to match reality
//SetCodePage(Result, CodePage, False); SetCodePage only works on RawByteString
if Length(Result) > 0 then
PStrRec(PByte(Result) - SizeOf(StrRec)).codePage := CodePage;
end;
Excepto que manualmente andar con la estructura interna de unAnsiString
Es horriblemente peligroso.
RawByteString
?Se ha dicho, más de una vez, por muchas personas que no soy yo queRawByteString
está destinado a ser elrecipiente universal; no estaba destinado a ser como un parámetro de retorno:
function UnicodeStringToString(s: UnicodeString; CodePage: UINT): RawByteString;
begin
...
WideCharToMultiByte(...);
...
//Adjust the codepage contained in the AnsiString to match reality
SetCodePage(Result, CodePage, False); SetCodePage only works on RawByteString
end;
Esto tiene la virtud de poder utilizar el soporte y el documento documentado.SetCodePage
.
Pero si vamos a cruzar una línea y comenzar a regresarRawByteString
, seguramente Delphi ya tiene una función que puede convertir unUnicodeString
a unRawByteString
cadena y viceversa:
function WideStringToString(const s: UnicodeString; CodePage: UINT): RawByteString;
begin
Result := SysUtils.Something(s, CodePage);
end;
function StringToWideString(const s: RawByteString; CodePage: UINT): UnicodeString;
begin
Result := SysUtils.SomethingElse(s, CodePage);
end;
¿Pero, qué es esto?
¿O qué más debo hacer?Este era un conjunto de antecedentes de largo aliento para una pregunta trivial. losreal la pregunta es, por supuesto, ¿qué debería hacer en su lugar? Hay mucho código por ahí que depende deUnicodeStringToString
y al revés.
Puedo convertir unUnicodeString
a UTF haciendo:
Utf8Encode('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');
y puedo convertir unUnicodeString
a la página de códigos actual usando:
AnsiString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');
¿Pero cómo convierto unUnicodeString
a una página de códigos arbitraria (no especificada)?
Mi sensación es que todo es realmente unAnsiString
:
Utf8String = AnsiString(65001);
RawByteString = AnsiString(65535);
Debería morder la bala, abrir el bustoAnsiString
estructura e inserte la página de códigos correcta en ella:
function StringToAnsi(const s: UnicodeString; CodePage: UINT): AnsiString;
begin
LocaleCharsFromUnicode(CodePage, ..., s, ...);
...
if Length(Result) > 0 then
PStrRec(PByte(Result) - SizeOf(StrRec)).codePage := CodePage;
end;
Entonces el resto del VCL se alineará.