Voici un article que j'ai écrit après quelques heures perdues sur un transtypage un peu rapidement fait
http://lookinside.free.fr/delphi.php...ns+les+strings
Voici un article que j'ai écrit après quelques heures perdues sur un transtypage un peu rapidement fait
http://lookinside.free.fr/delphi.php...ns+les+strings
prendez c'est fait exprès ? D'autant plus que "là-bas" c'est bien "prenez"...
Et, à propos de "là-bas", il ne manque pas un « d' », là dans le 2e § ? :(plus un "s" en trop et une espace en moins, au final : Les caractères de l'époque étaient toujours codés sur 8 bits, pas d'ambiguïté possible.)Les caractères de l'époques étaient toujours codés sur 8bits, pas ambiguïté possible.
Bonjour ! Article très éclairant en effet. Excellent titre, qui plus est.
Il y a une petite correction à faire dans le code : StringCodePage au lieu de StringPageCode.
Autrement, par simple curiosité, quand tu dis qu'à l'offset tant il y a telle donnée, y a-t-il un moyen de le vérifier (par code) ?
Quand tu dis qu'à l'offset -12 on trouve la page de code, par exemple 1200, je pense que j'ai mal compris la phrase, vu qu'il y a autre chose à l'offset -10 et que deux bits ne peuvent pas suffire à représenter la page de code.
Ton article est un tissu d'erreurs:
Par exemple:
- L'ASCII c'est 7 bits (128 valeurs), donc 1 octet
- Les "code pages" (ou MBCS sous Windows) c'est soit 1 octet (latin-1, latin-9) soit 2 octets (Shift-JIS)
- Les "code pages" sont compatibles ASCII, parce qu'il ne touchent pas aux 128 valeurs de l'ASCII (ils remplissent le trou 7bits <-> 8bits )
- Le "code page" ANSI c'est un truc bâtard: c'est le code page par défaut de Windows. Mais il change en fonction de la langue de Windows: latin-1 en France, Shift-JIS au Japon, ...
- L’Unicode c'est 3 encodages (mais il y en a eu des dizaines) UTF-8, UTF-16 (*) et UTF-32 (*) (*: big et little endian)
- L'UTF-8, c'est soit 1, 2, 3 ou 4 octets. Il est compatible ASCII
- L'UTF-16 c'est soit 2 octets (Windows) soit 4 octets (Linux). Il n'est pas compatible ASCII (<- en gros, il rajoute des '0' entre chaque caractères )
- L'UTF-16 c'est l'encodage de Windows (depuis W2K)
- L'API Windows W (wide) a un petit nom, et elle permet de rendre compatible le code "legacy" avant W2K
tu peux le faire comme ceci par exemple
mais l'unité System comprend aussi des fonctions pour cela: StringElementSize, StringCodePage, StringRefCount et Length() évidemment
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 type TStringInfo = record CodePage: Word; CharSize: Word; RefCount: Integer; Length : Integer; end; procedure StringInfo(p: Pointer); var i: ^TStringInfo; begin i := p; // l'adresse du premier caractère Dec(i); // remonter de SizeOf(TStringInfo) en mémoire WriteLn(' CodePage = ', i.CodePage); WriteLn(' CharSize = ', i.CharSize); WriteLn(' RefCount = ', i.RefCount); WriteLn(' Length = ', i.Length); end; begin StringInfo(pointer(Str)); end;
pas deux bits, deux octets
- "le type string correspond donc à une chaîne Unicode dont les caractères sont sur 16bits" Oui et non, UTF-16 mais ni Unicode, ni UTF-8, ni UTF-32 (une chaîne Unicode n'existe pas). Et oui, c'est 2 octets parce qu'on est sous Windows (sous Linux l'UTF-16 c'est 4 octets)
- "alors que l'Ansi est limité à 256 d'entre eux". Non, le "code page" Shift-JS par exemple, c'est plus de 256 valeurs (c'est 2 octets, mais pas 65536 caractères)
- "format 8bits qui permet de s'en sortir, c'est l'UTF8". Non, l'UTF8 c'est 1, 2, 3 ou 4 octets
- "format 8bits" Un format 1 octet n'existe pas. ASCII c'est 7 bits, MBCS c'est 1 ou 2 octets et UTF-8 voir ci-dessus.
- "car il n'y a aucune perte potentielle d'information" Non la conversion implicite MBCS ("code page") <-> UTF8 est sans perte si et seulement si on n'utilise que l'ASCII.
Je ne veux pas faire pédant , mais si tu as plein d'avertissements et de merdouilles avec les chaînes de caractères c'est que tu ne comprends que dalle à l'Unicode/ ASCII/ MBCS
Alors, ce que tu n'as sans doute pas compris, avant de critiquer mon article, c'est que mon article ne parle pas des encodages de caractères en général, mais de son implémentation sous Delphi.
UTF-16 c'est de l'unicode, même si l'inverse n'est pas forcément vrai, et le type String de Delphi est donc bien "une chaîne Unicode dont les caractères sont sur 16bits" soit de l'UTF-16, mais ma formulation me semble plus simple.
Shift-JS est codé sur 8bits, le premier caractère est systématiquement sur 7 bits (+ un bit à 0, donc 8bits), le second est quelconque.
on peut d'ailleurs s'en assurer comme ceci:
NB: sous Delphi j'ai directement tapé "分かりません" mais Developpez.com l'a encodé dans le source ci-dessus.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 type TStringInfo = record CodePage: Word; CharSize: Word; RefCount: Integer; Length : Integer; end; procedure dump(p: Pointer); var i: ^TStringInfo; begin i := p; Dec(i); WriteLn('CodePage = ', i.CodePage); WriteLn('CharSize = ', i.CharSize); WriteLn('Length = ', i.Length); end; type ShiftJS = type AnsiString(932); var su: string; sj: ShiftJS; sa: AnsiString; u8: UTF8String; begin su := '分かりません'; sj := su; sa := sj; u8 := sj; dump(Pointer(su)); dump(Pointer(sj)); dump(Pointer(sa)); dump(Pointer(u8)); MessageBox(0, PChar( 'su = ' + su + #13 + 'sj = ' + sj + #13 + 'sa = ' + sa + #13 + 'u8 = ' + u8 ), 'Test', 0 ); end;
3 des 4 chaînes affichent correctement "分かりません", alors que sa= '??????'
et on a
su: CodePage 1200, CharSize = 2, Length = 6, la chaîne Unicode (de Delphi) encode en 6 caractères UTF-16
sj: CodePage 932, CharSize = 1, Length = 12, la chaîne ShiftJS encode en 12 caractères 8bits
sa: CodePage 1252, CharSize = 1, Length = 6, la version Ansi contient 6 fois le caractère "?" = conversion impossible
u8: CodePage 65001, CharSize = 1, Length = 18, UTF8 encode en 18 caractères 8bits
et mon article reste totalement pertinent car j'ai les warning suivants
et si je ne prend pas garde, mes variables sa et u8 resteront en ShiftJS au lieu d'être converties dans le type attendu.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 [dcc32 Avertissement] Project1.dpr(66): W1058 Transtypage de chaîne implicite avec perte de données potentielle de 'string' en 'ShiftJS' [dcc32 Avertissement] Project1.dpr(67): W1057 Transtypage de chaîne implicite de 'ShiftJS' en 'string' [dcc32 Avertissement] Project1.dpr(67): W1058 Transtypage de chaîne implicite avec perte de données potentielle de 'string' en 'AnsiString' [dcc32 Avertissement] Project1.dpr(68): W1057 Transtypage de chaîne implicite de 'ShiftJS' en 'string' [dcc32 Avertissement] Project1.dpr(68): W1057 Transtypage de chaîne implicite de 'string' en 'UTF8String'
et cela reste du 8bits - d'où son nom - c'est d'ailleurs bien pour cela que le transtypage UTF8/Ansi ne provoque pas de conversion, le stockage est en 8bits, c'est l'interprétation des caractères qui peut se faire sur plusieurs octets.
tant qu'on aura pas d'ordinateur 7 bits, l'ASCII sera toujours représenté sur 8bits...et l'ANSI est un format 8bits complet.
je dis que la conversion UnicodeString => UTF8String est sans perte car ce sont deux formats Unicode, et c'est vrai. sinon démontre moi le contraire.
tu es pédant, mais les avertissements ont une raison d'être, et quand on code une requête SOAP en UTF8 à partir de données Unicode et Latin1, on a une petite idée de ce que l'on fait. Ce que je pointe dans l'article c'est que Delphi fait remonter des warning dès que l'on utilise des conversions implicites (mais cependant nécessaires) et qu'il est facile de se prendre les pieds dans le tapis du fait d'une certaine inconsistance (à mon sens) dans le traitement des transtypages qui sont parfois dynamiques et parfois non. selon qu'ils impliquent ou non le type "string".
Bonjour à tous
@Roland et ceux qui voudrait voir une manière différente pour accéder aux informations de taille codepage ...
l'utilisation est évidente et se passe donc démo
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 function StrSizeEx(const Str: string): Cardinal; var P: PByte; begin P := @Str[1]; Dec(P, 4); Result := PLongint(P)^ ; end; function StrRefCountEx(const Str: string): Integer; var P: PByte; begin P := @Str[1]; Dec(P, 8); Result := PLongint(P)^ ; end; function StrCharSizeEx(const Str: string): Cardinal; var P: PByte; begin P := @Str[1]; Dec(P, 10); Result := PWord(P)^ ; end; function StrCodePageEx(const Str: string): Cardinal; var P: PByte; begin P := @Str[1]; Dec(P, 12); Result := PWord(P)^ ; end;
ps Merci à Paul pour cet article
Cordialement,
@+
@Cirec
je préfère cette écriture qui revient cependant exactement au même
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 function StrSizeEx(const Str: string): Cardinal; var P: PLongint; // PByte; begin P := Pointer(Str); // @Str[1]; Dec(P{, 4}); Result := P^{PLongint(P)^ }; end;
@Paul TOTH
oui je comprend bien et ça fait un "transtypage" en moins
mais d'un autre coté, outre le fait d'avoir pris l'habitude(mauvaise certainement) d'utiliser le "PByte" ,
sur un plan plus didactique je préfère ce code parce qu'à la lecture on y voit de suite le lien avec les offsets citées dans ton article
Sinon pour respecter les deux on pourrait aussi faire comme ceci ... :... mais peut-être que ça rend le code un peu plus complexe à comprendre pour les débutants !
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 function StrSizeEx(const Str: string): Cardinal; begin Result := Cardinal(@Str[1]); Dec(Result, 4); Result := PLongint(Result)^ ; end; function StrRefCountEx(const Str: string): Integer; begin Result := Cardinal(@Str[1]); Dec(Result, 8); Result := PLongint(Result)^ ; end; function StrCharSizeEx(const Str: string): Cardinal; begin Result := Cardinal(@Str[1]); Dec(Result, 10); Result := PWord(Result)^ ; end; function StrCodePageEx(const Str: string): Cardinal; begin Result := Cardinal(@Str[1]); Dec(Result, 12); Result := PWord(Result)^ ; end;
Cordialement,
@+
@Cirec
oui on est d'accord, on pourrait même s'amuser à tout faire en une ligne
explications (par pour toi, mais les lecteurs perdus)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 function StrSizeEx(const Str: string): Cardinal; begin Result := PLongInt(@(PByte(Str)[-4]))^; end;
mais bon ça devient illisible à mon sens, on fait du Pascal pas du C
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 PByte(str) // fait de str un Pointeur sur des Byte PByte(str)[-4] // le byte à l'offset -4 @(PByte(str)[-4]) // prend l'adresse de ce byte PLongInt(@(PByte(str)[-4])) // transforme cette adresse en pointeur sur un LongInt PLongInt(@(PByte(str)[-4]))^ // récupère l'entier long à cette adresse
Ah ben crotte alors .... Je me coucherai moins bête ce soir
Attention, méfie-toi ! Il y a très peu d'espace dans un string pour les crottes... D'ici qu'on finisse vraiment par se prendre les pieds dedans...Ah ben crotte alors
à défaut de couper les cheveux en quatre, on coupera les me...s en deux
Partager