Convertendo UnicodeString em AnsiString
Antigamente, eu tinha uma função que convertia umWideString
para umAnsiString
da página de código 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;
E tudo funcionou. Passei a função aunicode (ou seja, dados codificados em UTF-16) e os converteu em umAnsiString
, com o entendimento de que os bytes noAnsiString
caracteres representados da página de códigos especificada.
Por exemplo:
TUnicodeHelper.WideStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', 1252);
retornaria oWindows-1252
sequência codificada:
The qùíçk brown fôx jumped ovêr the lázÿ dog
Nota: Obviamente, as informações foram perdidas durante a conversão do conjunto completo de caracteres Unicode para os limites limitados da página de código do Windows-1252:
Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ
(antes)The qùíçk brown fôx jumped ovêr the lázÿ dog
(depois de)Mas o WindowsWideChartoMultiByte
faz um bom trabalho de mapeamento da melhor forma; como foi projetado para fazer.
Agora estamos nos tempos posteriores.WideString
agora é uma pária, comUnicodeString
sendo a bondade. É uma mudança inconseqüente; como a função Windows precisava apenas de umponteiro para uma série deWideChar
de qualquer maneira (que umUnicodeString
também é). Então, alteramos a declaração para usarUnicodeString
em vez de:
funtion WideStringToString(const Source: UnicodeString; CodePage: UINT): AnsiString;
begin
...
end;
Agora chegamos ao valor de retorno. eu tenho umAnsiString
que contém os 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
Nos velhos tempos, tudo bem. Acompanhei qual página de código oAnsiString
realmente contido; Eu preciseilembrar que o retornadoAnsiString
não foi codificado usando a localidade do computador (por exemplo, Windows 1258), mas é codificado usando outra página de código (aCodePage
página de código).
Mas no Delphi XE6 umAnsiString
também secretamente contém a página de códigos:
The qùíçk brown fôx jumped ovêr the lázÿ dog
Esta página de código está errada. Delphi está especificando a página de código do meu computador, em vez da página de código que a string é. Tecnicamente, isso não é um problema, eu sempre entendi que oAnsiString
estava em uma página de código específica, eu só precisava transmitir essas informações.
Então, quando eu quis decodificar a string, tive que passar a página de código com ela:
s := TUnicodeHeper.StringToWideString(s, 1252);
com
function StringToWideString(s: AnsiString; CodePage: UINT): UnicodeString;
begin
...
MultiByteToWideChar(...);
...
end;
Então uma pessoa estraga tudoO problema era que antigamente eu declarava um tipo chamadoUtf8String
:
type
Utf8String = type AnsiString;
Porque era comum o suficiente ter:
function TUnicodeHelper.WideStringToUtf8(const s: UnicodeString): Utf8String;
begin
Result := WideStringToString(s, CP_UTF8);
end;
e o inverso:
function TUnicodeHelper.Utf8ToWideString(const s: Utf8String): UnicodeString;
begin
Result := StringToWideString(s, CP_UTF8);
end;
Agora no XE6 eu tenho uma função queleva a Utf8String
. Se algum código existente em algum lugar estivesse usando um código UTF-8AnsiString
e tente convertê-lo em UnicodeString usandoUtf8ToWideString
falharia:
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
Ou pior, é a amplitude do código existente que faz:
s: Utf8String;
s := UnicodeStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', CP_UTF8);
A string retornada ficará totalmente desconfigurada:
a função retornaAnsiString(1252)
(AnsiString
marcado como codificado usando a página de código atual)o resultado do retorno está sendo armazenado em umAnsiString(65001)
corda (Utf8String
)Delphi converte a string codificada em UTF-8 em UTF-8 como se fosse 1252.Como avançarIdealmente meuUnicodeStringToString(string, codePage)
função (que retorna umAnsiString
) pode definir oCodePage
dentro da string para corresponder à página de código 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;
Exceto que mexer manualmente com a estrutura interna de umAnsiString
é terrivelmente perigoso.
RawByteString
?Já foi dito, repetidamente, por muitas pessoas que não sou eu queRawByteString
é para ser odestinatário universal; não era para ser como um 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;
Isso tem a virtude de poder usar os recursos suportados e documentadosSetCodePage
.
Mas se vamos cruzar uma linha e começar a retornarRawByteString
, certamente o Delphi já tem uma função que pode converter umUnicodeString
para umRawByteString
string e vice-versa:
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;
Mas o que é isso?
Ou o que mais devo fazer?Era um conjunto de antecedentes prolongado para uma pergunta trivial. oreal A questão é, é claro, o que devo fazer em vez disso? Existe muito código por aí que depende daUnicodeStringToString
e o contrário.
Eu posso converter umUnicodeString
para UTF fazendo:
Utf8Encode('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');
e eu posso converter umUnicodeString
para a página de código atual usando:
AnsiString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');
Mas como faço para converter umUnicodeString
para uma página de código arbitrária (não especificada)?
Meu sentimento é que, já que tudo é realmente umAnsiString
:
Utf8String = AnsiString(65001);
RawByteString = AnsiString(65535);
eu deveria morder a bala, abrir oAnsiString
estrutura e coloque a página de código correta nela:
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;
Em seguida, o restante da VCL ficará alinhado.