La conversion de UnicodeString à AnsiString

Dans les temps anciens, j'avais une fonction qui permettrait de convertir un WideString à un AnsiString de la page de code:

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;

Et tout a fonctionné. J'ai passé la fonction d'un unicode chaîne (c'est à dire codé en UTF-16 données) et l'a converti en un AnsiString, avec la compréhension que les octets dans le AnsiString représentés des personnages de la spécifiées à la page de code.

Par exemple:

TUnicodeHelper.WideStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', 1252);

serait de retour le Windows-1252 chaîne codée:

The qùíçk brown fôx jumped ovêr the lázÿ dog

Remarque: les Informations de cours a été perdue lors de la conversion de la totalité jeu de caractères Unicode pour l'limité confins du Windows-1252 du code de la page:

  • Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ (avant)
  • The qùíçk brown fôx jumped ovêr the lázÿ dog (après)

Mais les Fenêtres WideChartoMultiByte fait un assez bon travail de meilleur ajustement de la cartographie; car il est conçu pour faire.

Maintenant le après une fois

Maintenant, nous sommes dans l'après fois. WideString est désormais un paria, avec UnicodeString être la bonté. C'est une conséquence du changement; comme la fonction Windows seulement besoin d'une pointeur à une série de WideChar de toute façon (dont un UnicodeString est aussi). Nous avons donc modifier la déclaration à l'utilisation UnicodeString à la place:

funtion WideStringToString(const Source: UnicodeString; CodePage: UINT): AnsiString;
begin
   ...
end;

Nous arrivons maintenant à la valeur de retour. j'ai un AnsiString qui contient les octets:

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

Dans les temps anciens, c'était bon. J'ai gardé la trace de ce que la page de code de la AnsiString contenaient en fait; j'ai eu à rappelez-vous que le retour de l' AnsiString n'a pas été codée à l'aide des paramètres régionaux de l'ordinateur (par exemple, Windows 1258), mais, au contraire, est codé à l'aide d'une autre page de code (le CodePage la page de code).

Mais en Delphi XE6 un AnsiString aussi secrètement contient la page de codes:

  • page de codes: 1258
  • longueur: 44
  • valeur: The qùíçk brown fôx jumped ovêr the lázÿ dog

Cette page de code est faux. Delphi est de spécifier la page de code de mon ordinateur, plutôt que la page de code que la chaîne est. Techniquement, ce n'est pas un problème, j'ai toujours compris que la AnsiString était dans une page de code, j'ai juste eu à être sûr de passer cette information.

Donc quand j'ai voulu décoder la chaîne, j'ai dû passer le long de la page de code avec elle:

s := TUnicodeHeper.StringToWideString(s, 1252);

avec

function StringToWideString(s: AnsiString; CodePage: UINT): UnicodeString;
begin
   ...
   MultiByteToWideChar(...);
   ...
end;

D'une personne vis de tout ce

Le problème était que, dans les temps anciens, j'ai déclaré un type appelé Utf8String:

type
   Utf8String = type AnsiString;

Parce qu'il est assez commun d'avoir:

function TUnicodeHelper.WideStringToUtf8(const s: UnicodeString): Utf8String;
begin
   Result := WideStringToString(s, CP_UTF8);
end;

et l'inverse:

function TUnicodeHelper.Utf8ToWideString(const s: Utf8String): UnicodeString;
begin
   Result := StringToWideString(s, CP_UTF8);
end;

Maintenant dans XE6 j'ai une fonction qui prend un Utf8String. Si le code existant quelque part ont été prendre une codé en UTF-8 AnsiString, et d'essayer de le convertir en UnicodeString à l'aide de Utf8ToWideString il ne pourrait pas:

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 pour le pire, l'ampleur de code existant qui n':

s: Utf8String;
s := UnicodeStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', CP_UTF8);

La chaîne de retour seront totalement déformés:

  • la fonction renvoie AnsiString(1252) (AnsiString tagged as codé à l'aide de la page de codes actuelle)
  • le résultat de retour est stockée dans un AnsiString(65001) chaîne (Utf8String)
  • Delphi convertit en UTF-8 chaîne en UTF-8, comme si c'était 1252.

Comment aller de l'avant

Idéalement mon UnicodeStringToString(string, codePage) fonction (qui renvoie un AnsiString) pourrait définir la CodePage à l'intérieur de la chaîne afin de correspondre à la page de code en utilisant quelque chose comme SetCodePage:

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;

Sauf que manuellement le nettoyage autour de la structure interne d'un AnsiString est horriblement dangereux.

Ainsi que sur le retour RawByteString?

Il a été dit, sur un plus de, par beaucoup de gens qui ne sont pas moi qui RawByteString est censé être le universelle destinataire; il n'était pas censé être comme un paramètre de retour:

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;

Cela a le mérite d'être en mesure d'utiliser le étayé et documenté SetCodePage.

Mais si nous allons traverser une ligne, et commencent à retourner RawByteString, sûrement Delphi a déjà une fonction qui permet de convertir un UnicodeString à un RawByteString chaîne et 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;

Mais qu'en est-il?

Ou quoi d'autre dois-je faire?

C'est un travail de longue haleine ensemble de documents de référence pour une question banale. Le réel question est, bien sûr, que dois-je faire à la place? Il y a beaucoup de code qui dépend de la UnicodeStringToString et l'inverse.

tl;dr:

Je peux convertir un UnicodeString UTF par:

Utf8Encode('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');

et je peux convertir un UnicodeString à l'actuel code de la page en utilisant:

AnsiString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');

Mais comment puis-je convertir un UnicodeString arbitraire (non spécifiés) à la page de code?

Mon sentiment est que, puisque tout ce qui est vraiment un AnsiString:

Utf8String = AnsiString(65001);
RawByteString = AnsiString(65535);

je devrais mordre la balle, buste ouvert la AnsiString de la structure, et de mettre le bon code-page en:

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;

Puis le reste de la VCL de l'automne en ligne.

InformationsquelleAutor Ian Boyd | 2014-11-12