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.
- Honnêtement, tl;dr.
- Qui tl;dr? Honnêtement
Vous devez vous connecter pour publier un commentaire.
Dans ce cas particulier, à l'aide de
RawByteString
est une solution appropriée:De cette façon, le
RawByteString
détient la page de codes, et de l'affectation de laRawByteString
pour tout autre type de chaîne, qu'il s'agisse deAnsiString
ouUTF8String
ou quoi que ce soit, de permettre la RTL pour convertir automatiquement lesRawByteString
données à partir de sa page de codes actuelle de la chaîne de destination de la page de codes (qui comprend les conversions àUnicodeString
).Si vous devez absolument retourner un
AnsiString
(que je ne recommande pas), vous pouvez toujours utiliserSetCodePage()
via un transtypage:L'inverse est beaucoup plus facile, il suffit d'utiliser la page de codes qui sont déjà stockées dans un
(Ansi|RawByte)String
(il suffit de s'assurer que ces pages de code sont toujours exactes), depuis le RTL sait déjà comment récupérer et utiliser la page de codes pour vous:Cela étant dit, je voudrais suggérer la suppression des fonctions d'assistance au total et il suffit d'utiliser tapé chaînes à la place. Laissez le RTL gérer les conversions pour vous:
RawByteString
entre en jeu. Il est préférable de ne pas salir avec la page de codes associés à une plaineAnsiString
. La conversion deUnicodeString
à unRawByteString
appropriée de la page de codes, et le stockage de données Ansi dans unRawByteString
appropriée de la page de codes avant de l'affecter àUnicodeString
, est la meilleure option dans cette situation.AnsiString
, je dois juste être très attention, changer tout le code existant partout (dans chaque projet, dans chaque bibliothèque partagée, dans tous les dll, etc). C'est particulièrement gênant dans le code qui a été construit dans les fichiers binaires (par exemple, dll/bpl).deprecated
, venir dans maniable. Marquez vos fonctions/classes/variables commedeprecated
et laisser le compilateur vous montrer partout où ils sont utilisés, et puis vous pouvez adapter ce code en fonction des besoins.WideStringToStr(UnicodeString, UINT): AnsiString
etWideStringToStr(UnicodeString, UINT): RawByteString
. Je suis sûr que Delphi refuser de reconnaître la différence entre eux. Je crois qu'il y aura cast Implicite de "RawByteString' à 'AnsiString' avec un potentiel de perte de données avertissements.AnsiString
au lieu deRawByteString
.SetAnsiString
qui semble résoudre beaucoup de ma douleur pour moi (dans un officiellement pris en charge et documentés).Je pense que le retour d'un
RawByteString
est probablement aussi bon que vous obtenez. Vous pourriez le faire à l'aide deAnsiString
comme vous l'avez indiqué, maisRawByteString
capture l'intention de mieux. Dans ce scénario, unRawByteString
moralement compte comme un paramètre dans le sens de l'officiel Embarcadero conseils. C'est juste une sortie et non une entrée. La vraie clé est de ne pas l'utiliser comme une variable.Vous pourriez code comme ceci:
Puis
sorties 1252, 1251, puis 65001 que vous attendez.
Et vous pouvez utiliser
LocaleCharsFromUnicode
si vous préférez. Bien sûr, vous avez besoin de prendre sa documentation avec une pincée de sel: LocaleCharsFromUnicode est un wrapper pour le WideCharToMultiByte fonction. Incroyable que le texte était déjà écrit depuisLocaleCharsFromUnicode
sûrement n'existe que pour être multi-plateforme.Cependant, je me demande si vous avez peut-être fait une erreur en essayant de le garder ANSI texte codé en
AnsiString
variables dans votre programme. Normalement, vous serait encodé en ANSI que le plus tard possible (à la limite d'interopérabilité), et de même de décoder le plus tôt possible.Si vous avez simplement à faire cela, alors peut-être il est une meilleure solution qui permet d'éviter le redoutable
AnsiString
complètement. Au lieu de stocker le texte dans unAnsiString
, de le stocker dansTBytes
. Vous avez déjà des structures de données permettant d'assurer un suivi de l'encodage, donc pourquoi ne pas les garder. Remplacer le dossier qui contient le code de la page etAnsiString
avec l'un contenant le code de la page etTBytes
. Ensuite, vous n'avez pas peur de tout recoder votre texte derrière votre dos. Et votre code sera prêt à l'emploi sur le mobile compilateurs.AnsiString
à l'époque était une commodité. Il a comporté la facilité de déclaration, facilité de manipulation, de copie sur écriture, facile à retourner. (cf. paramètres déclarésarray of Byte
, et les types de retour déclaréTByteDynArray
).TEncoding
utilisation est un peu plus propre que d'utiliserLocaleCharsFromUnicode()
(ouWideCharToMultiByte()
), il nécessite l'utilisation de deux fois plus de mémoire (un pour leTBytes
, puis de nouveau pour leResult
), même si temporairement. BTW, vous pouvez utiliserSetString()
au lieu deSetLength()
/Move()
.LocaleCharsFromUnicode()
de la documentation, il a probablement été écrit à une époque oùLocaleCharsFromUnicode()
était vraiment juste un wrapper pourWideCharToMultiByte()
par lui-même avant de nouvelles plates-formes ont été ajoutés, et pas mis à jour en conséquence.System
unité:"LocaleCharsFromUnicode is a cross-platform wrapper for WideCharToMultiByte with an emulated implemention on non-Windows platforms."
et"UnicodeFromLocaleChars is a cross-platform wrapper for MultiByteToWideChar with an emulated implemention on non-Windows platforms."
Les deux fonctions ont été ajoutées dans le XE, mais FireMonkey et OSX de soutien ont été introduites dans XE2.Aplatir par
System.pas
, j'ai trouvé la fonction intégréeSetAnsiString
qui fait ce que je veux:Il est également important de noter que cette fonction ne pousser la page de Codes dans l'intérieur de la StrRec structure pour moi:
Cela me permet d'écrire quelque chose comme:
Donc quand je l'appelle:
je récupérer la
AnsiString
:Un AnsiString avec le code approprié pages déjà en peluche dans le secret
codePage
membre.L'autre sens
MultiByteToWideChar
. Vous donner les octets d'un AnsiString et la page de code qu'il est actuellement, et Windows peut convertir en UTF-16 pour vous.UniStr := String( TEncoding.GetEncoding(SourceCP).GetChars( SourceBytes ) ) ;
Merci pour votre travail supplémentaire.