Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

unicode (?), regexp, SQL... le bordel

21 réponses
Avatar
Olivier Masson
Bonjour,

J'ai du mal à comprendre et surtout à résoudre un problème tout bête.

Je cherche à parser un texte à la recherche d'expressions présentes dans
une base. Ce que font les tonnes des trucs à la autolink.

Mais, si tout va bien dans mes tests hors SQL, si tout va bien sur la
pluparts des mots et expressions, ça foire avec les quotes.

Ceci place des % entre les expressions ($exp) trouvées dans la base MySQL :
preg_replace ( "#(\W)(" . preg_quote ( $exp ) . ")(\W)#Ui" ,
'${1}%${2}%${3}' , $texte , $max );

Si, au sein du script php, je cherche "aujourd'hui", ça ne fonctionne pas.
Normal, le problème est que le texte original transforme les ' en ' (je
ne le contrôle pas).
Donc, toujours au sein du script, je cherche "aujourd'hui" et là, ça
fonctionne.

Par contre, quand j'utilise la sortie MySQL, ça ne fonctionne plus. J'ai
beau faire un str_replace("'","'",$texte) ou un
str_replace("'","'",$exp), ça ne fonctionne pas du moment que $exp vient
de la base de données.

Pourtant, quand je fais un echo du $exp sorti de la base et que je fais
un copier/coller dans le script, ça fonctionne.

Tout est censé être en utf-8 et j'ai même insisté avec un
set_charset('utf8') pour la connexion MySQL.

Alors bon, j'en ai marre :(

10 réponses

1 2 3
Avatar
Olivier Miakinen
[Publication en UTF-8]

Bonjour,

Le 16/10/2009 15:40, Olivier Masson a écrit :

[...] je cherche "aujourd'hui", ça ne fonctionne pas.

[...] transforme les ' en ' [...]

[...] je cherche "aujourd'hui" et là, ça fonctionne.

[...] str_replace("'","'",$texte) [...]



Tu devrais republier ton article en UTF-8 plutôt qu'en Windows-1252, il
y a moins de risques que les ’ se transforment en ', rendant ta question
incompréhensible.

Cordialement,
--
Olivier Miakinen
Avatar
Olivier Masson
Olivier Miakinen a écrit :

Tu devrais republier ton article en UTF-8 plutôt qu'en Windows-1252, il
y a moins de risques que les ’ se transforment en ', rendant ta question
incompréhensible.

Cordialement,



/ Merci Olivier, je pensais que Tb faisait ça comme un grand (d'ailleurs
c'est iso-8859-1 qui est choisi dans ma conf, pas windows-1252 : pas
bien Tb !)
Je reprends donc : /

Bonjour,

J'ai du mal à comprendre et surtout à résoudre un problème tout bête.

Je cherche à parser un texte à la recherche d'expressions présentes dans
une base. Ce que font les tonnes des trucs à la autolink.

Mais, si tout va bien dans mes tests hors SQL, si tout va bien sur la
pluparts des mots et expressions, ça foire avec les quotes.

Ceci place des % entre les expressions ($exp) trouvées dans la base MySQL :
preg_replace ( "#(W)(" . preg_quote ( $exp ) . ")(W)#Ui" ,
'${1}%${2}%${3}' , $texte , $max );

Si, au sein du script php, je cherche "aujourd'hui", ça ne fonctionne pas.
Normal, le problème est que le texte original transforme les ' en ’ (je
ne le contrôle pas).
Donc, toujours au sein du script, je cherche "aujourd’hui" et là, ça
fonctionne.

Par contre, quand j'utilise la sortie MySQL, ça ne fonctionne plus. J'ai
beau faire un str_replace("’","'",$texte) ou un
str_replace("'","’",$exp), ça ne fonctionne pas du moment que $exp vient
de la base de données.

Pourtant, quand je fais un echo du $exp sorti de la base et que je fais
un copier/coller dans le script, ça fonctionne.

Tout est censé être en utf-8 et j'ai même insisté avec un
set_charset('utf8') pour la connexion MySQL (j'ai aussi forcé avec un
iconv sans résultat).


Alors bon, j'en ai + que marre :(


PS : pourquoi les mb_ utilisent les regex POSIX (y'a pas de mb_ereg)
alors qu'elles sont dépréciées ?
Avatar
Olivier Miakinen
En attendant ta republication, quelques remarques quand même :

Le 16/10/2009 15:40, Olivier Masson a écrit :

Ceci place des % entre les expressions ($exp) trouvées dans la base MySQL :
preg_replace ( "#(W)(" . preg_quote ( $exp ) . ")(W)#Ui" ,
'${1}%${2}%${3}' , $texte , $max );



Tout d'abord, si les expressions $exp sont censées toujours commencer et
finir par un caractère « de mot », alors une assertion « limite de mot »
est plus facile à lire qu'un (W) repris dans un ${1}. Qui plus est,
cela fonctionnera même en début de chaîne, ce qui n'est pas le cas avec
(W).

preg_replace ( "#b(" . preg_quote ( $exp ) . ")b#Ui" , '%${1}%' ,
$texte , $max );

Personnellement j'ai l'habitude de respecter à peu près la typographie
en ce qui concerne les espaces à l'intérieur des parenthèses et avant
les virgules, d'autant que c'est la même en anglais et en français, mais
tu n'es bien sûr pas obligé de penser comme moi. Malgré tout je le fais
ici pour être sûr de ne rien oublier d'autre. Je supprime en général
aussi l'espace entre le nom d'une fonction et sa parenthèse ouvrante, et
je préfère utiliser $1 plutôt que ${1} (sauf bien sûr s'il est suivi
d'un chiffre, ce qui ne m'est jamais arrivé).

preg_replace("#b(" . preg_quote($exp) . ")b#Ui", '%$1%',
$texte, $max);

Tiens, je m'aperçois que tous les caractères servant au « matching »
sont utilisés, on peut donc encore économiser deux parenthèses et
(selon moi, mais c'est très subjectif) améliorer encore la lisibilité.

preg_replace("#b" . preg_quote($exp) . "b#Ui", '%$0%',
$texte, $max);

Maintenant, puisque tu dis que tout est en UTF-8, je pense que l'option
« u » (PCRE8) s'impose, d'autant que tu n'utilises aucune fonction
spécifique à PCRE. Si ça se trouve, c'est la seule source de tes soucis,
même si je n'en ai aucune idée parce que je n'ai pas encore vraiment
compris le problème.

preg_replace("#b" . preg_quote($exp) . "b#Uiu", '%$0%',
$texte, $max);

Voilà. Pour le reste on verra plus tard...
Avatar
Olivier Miakinen
Le 16/10/2009 16:34, Olivier Masson a écrit :



<HS>
Tu devrais republier ton article en UTF-8 plutôt qu'en Windows-1252, il
y a moins de risques que les ’ se transforment en ', rendant ta question
incompréhensible.

Cordialement,



/ Merci Olivier, je pensais que Tb faisait ça comme un grand (d'ailleurs
c'est iso-8859-1 qui est choisi dans ma conf, pas windows-1252 : pas
bien Tb !)



Je pense que c'est intl.fallbackCharsetList.ISO-8859-1 qui vaut
windows-1252 par défaut et qu'il faut changer pour autre chose.

Tu peux y mettre UTF-8, mais personnellement j'y ai mis ISO-8859-15 et
ça marche très bien : Thunderbird choisit tout seul entre ISO-8859-1 et
ISO-8859-15 quand il le peut, et sinon il me demande de confirmer le
passage à UTF-8.
</HS>

Je reprends donc : /

[...]

Ceci place des % entre les expressions ($exp) trouvées dans la base MySQL :
preg_replace ( "#(W)(" . preg_quote ( $exp ) . ")(W)#Ui" ,
'${1}%${2}%${3}' , $texte , $max );



Tiens, j'ai oublié de te signaler un truc dans mon article précédent :
remplacer preg_quote($exp) par preg_quote($exp, '#') au cas où tu
pourrais trouver un '#' dans $exp !

Si, au sein du script php, je cherche "aujourd'hui", ça ne fonctionne pas.
Normal, le problème est que le texte original transforme les ' en ’ (je
ne le contrôle pas).
Donc, toujours au sein du script, je cherche "aujourd’hui" et là, ça
fonctionne.

Par contre, quand j'utilise la sortie MySQL, ça ne fonctionne plus. J'ai
beau faire un str_replace("’","'",$texte) ou un
str_replace("'","’",$exp), ça ne fonctionne pas du moment que $exp vient
de la base de données.



Pour comprendre ce qui se passe réellement, il serait intéressant
d'avoir un « dump » hexadécimal du $exp et de $texte. Est-ce que
des caractères Unicode ne seraient pas ajoutés automatiquement quand
on rencontre un « ’ » ?

Pourtant, quand je fais un echo du $exp sorti de la base et que je fais
un copier/coller dans le script, ça fonctionne.



Idem. Si le copier/coller ne recopie pas un caractère invisible du style
d'un BOM, cela pourrait peut-être expliquer le comportement.

PS : pourquoi les mb_ utilisent les regex POSIX (y'a pas de mb_[p]reg)
alors qu'elles sont dépréciées ?



Problablement parce que tout le monde se fout des fonctions mb_*, vu que
le vrai standard est UTF-8 et que les fonctions preg_* savent très bien
les gérer avec l'option u ?
Avatar
Olivier Masson
Olivier Miakinen a écrit :


preg_replace("#b" . preg_quote($exp) . "b#Ui", '%$0%',
$texte, $max);




Pour les espaces, je fais comme toi mais là, j'éclaircis à force de
modifications.
Oui, pour le b ça doit fonctionner mais j'ai déjà rencontré des
problèmes avec son utilisation (surement de ma faute).
Et pour # dans preg_quote, il y est à l'origine, mais merci.

Maintenant, puisque tu dis que tout est en UTF-8, je pense que l'option
« u » (PCRE8) s'impose, d'autant que tu n'utilises aucune fonction
spécifique à PCRE. Si ça se trouve, c'est la seule source de tes soucis,
même si je n'en ai aucune idée parce que je n'ai pas encore vraiment
compris le problème.

preg_replace("#b" . preg_quote($exp) . "b#Uiu", '%$0%',
$texte, $max);




Ben écoute, au moins j'aurais appris un truc car je n'avais jamais prêté
attention à l'option u ! Ca va m'alléger quelques scripts.

Pour mon problème, ça ne change rien. Ce que j'ai du mal à comprendre,
c'est qu'en fait, le quote, ce serait un /x92, typique de CP1252 alors
que c'est de l'utf-8 partout.
Lorsque je fais str_replace("’","'",$texte), le texte n'est pas modifié.
Par contre l'opération inverse sur $exp fonctionne (' se transforme bien
en ’).
Pourtant, à l'écran, ces deux ’ sont les mêmes. Y'a-t-il
translittération automatique qq part ?

Démonstration (en espérant que tout passe tel quel) :
iconv('Windows-1252' ,'UTF-8', $exp) : aujourd’hui
iconv('Windows-1252' ,'UTF-8', $texte) : aujourd’hui
iconv('UTF-8' ,'Windows-1252', $exp) : aujourd�hui
iconv('UTF-8' ,'Windows-1252', $texte) : aujourd’hui
iconv('iso-8859-1' ,'UTF-8', $exp) : aujourd’hui€™ (là, le cc ne
fonctionne pas)
iconv('iso-8859-1' ,'UTF-8', $texte) : aujourd’hui

J'en déduis bêtement que mon $exp est bien en utf-8 et mon $texte non.
Alors que la base et le champ sont en utf-8 et que mb_detect_encoding me
dit qu'il s'agit d'utf-8.

Où je deviens fou, c'est qu'en faisant :
str_replace("'",chr(146),$exp)
pour remplacer le ' de ma base par un x92 bien moche qui est censé être
dans le texte, bien évidemment, ça ne fonctionne pas et ne s'affiche
même pas puisque chr(146) en utf-8, ça plait pas.

Pour finir, j'ai essayé un
str_replace("'","&#8217;",$exp), ce qui a bien remplacé le ' par un ’...
mais toujours pas considéré comme identique à celui du $texte.
Avatar
Olivier Miakinen
Le 16/10/2009 18:37, Olivier Masson a écrit :

Pour mon problème, ça ne change rien. Ce que j'ai du mal à comprendre,
c'est qu'en fait, le quote, ce serait un /x92, typique de CP1252 alors
que c'est de l'utf-8 partout.



Ah, on avance.

Lorsque je fais str_replace("’","'",$texte), le texte n'est pas modifié.
Par contre l'opération inverse sur $exp fonctionne (' se transforme bien
en ’).
Pourtant, à l'écran, ces deux ’ sont les mêmes. Y'a-t-il
translittération automatique qq part ?

Démonstration (en espérant que tout passe tel quel) :
iconv('Windows-1252' ,'UTF-8', $exp) : aujourd’hui



’ : E2 80 99, si le charset était Windows-1252. Puisque c'est le
résultat de iconv('Windows-1252', 'UTF-8'), on peut supposer que
$exp contient bien une apostrophe UTF-8 à l'origine.

iconv('Windows-1252' ,'UTF-8', $texte) : aujourd’hui



Du coup, on peut supposer que $texte contient une apostrophe
Windows-1252 à l'origine.

iconv('UTF-8' ,'Windows-1252', $exp) : aujourd�hui



Logique : il transforme une apostrophe UTF-8 en Windows-1252, et le
résultat n'est pas de l'UTF-8.

iconv('UTF-8' ,'Windows-1252', $texte) : aujourd’hui



Ah zut, là je ne comprends pas comment c'est possible.

iconv('iso-8859-1' ,'UTF-8', $exp) : aujourd’hui€™ (là, le cc ne
fonctionne pas)



Ça, cela reste logique : des trois codes xE2, x80 et x99 d'origine,
seul le xE2 existe en Latin1 ; les autres sont virés.

iconv('iso-8859-1' ,'UTF-8', $texte) : aujourd’hui



Hum...

J'en déduis bêtement que mon $exp est bien en utf-8 et mon $texte non.



Je ne te demande que deux tests :
bin2hex($exp)
bin2hex($texte)
Là on saura enfin ce qu'ils ont dans le ventre.

Alors que la base et le champ sont en utf-8 et que mb_detect_encoding me
dit qu'il s'agit d'utf-8.



Une possibilité serait que l'apostrophe Windows-1252 ait été encodée en
UTF-8 *comme si* c'était du Latin1, ce qui donnerait C2 92 au lieu de
92 (Windows-1252 vrai) ou de E2 80 99 (UTF-8 vrai). Je pense que la
fonction utf8_encode doit faire ce genre de blague.

Où je deviens fou, c'est qu'en faisant :
str_replace("'",chr(146),$exp)
pour remplacer le ' de ma base par un x92 bien moche qui est censé être
dans le texte, bien évidemment, ça ne fonctionne pas et ne s'affiche
même pas puisque chr(146) en utf-8, ça plait pas.



et str_replace("'", "xC2x99", $exp) ?

Pour finir, j'ai essayé un
str_replace("'","&#8217;",$exp), ce qui a bien remplacé le ' par un ’...
mais toujours pas considéré comme identique à celui du $texte.



Oui, ça c'est normal. Ce sont deux représentations complètement différentes.

Cordialement,
--
Olivier Miakinen
Avatar
Olivier Masson
Olivier Miakinen a écrit :

Je ne te demande que deux tests :
bin2hex($exp)
bin2hex($texte)
Là on saura enfin ce qu'ils ont dans le ventre.




Ah ben c'est sûr...
version base : 7465737420696369
version texte : 3c703e61756a6f7572642623383231373b6875693c2f703e0a
Au cas où, je signale que les accents, que ce soit dans la base ou le
texte passe très bien sur la page résultante encodée en utf-8.

et str_replace("'", "xC2x99", $exp) ?




Gros caca à la place du '
Avatar
Olivier Miakinen
Le 16/10/2009 20:23, Olivier Masson a écrit :

Je ne te demande que deux tests :
bin2hex($exp)
bin2hex($texte)
Là on saura enfin ce qu'ils ont dans le ventre.



Ah ben c'est sûr...
version base : 7465737420696369



"test ici" ;-)

version texte : 3c703e61756a6f7572642623383231373b6875693c2f703e0a



"<p>aujourd&#8217;hui</p>"

Ah, je comprends mieux pourquoi toutes les versions marchaient, quels
que soient les iconv() effectués ! Il n'y a que de l'ASCII ici, et c'est
juste à l'affichage que tu montres une apostrophe...
Avatar
Olivier Miakinen
Le 16/10/2009 20:23, Olivier Masson a écrit :

bin2hex($exp)
bin2hex($texte)



version base : 7465737420696369
version texte : 3c703e61756a6f7572642623383231373b6875693c2f703e0a




Pour la prochaine fois, plutôt qu'un simple bin2hex($exp) dont le
résultat est un peu trop compact, je te suggère :
chunk_split(bin2hex($exp), 2, " ");
Avatar
Olivier Masson
Olivier Miakinen a écrit :
Le 16/10/2009 20:23, Olivier Masson a écrit :
Je ne te demande que deux tests :
bin2hex($exp)
bin2hex($texte)
Là on saura enfin ce qu'ils ont dans le ventre.


Ah ben c'est sûr...
version base : 7465737420696369



"test ici" ;-)




:) je ne parle pas hexadecimal couramment, me suis planté de ligne.

61 75 6a 6f 75 72 64 27 68 75 69
61 75 6a 6f 75 72 64 26 23 38 32 31 37 3b 68 75 69

version texte : 3c703e61756a6f7572642623383231373b6875693c2f703e0a



"<p>aujourd&#8217;hui</p>"

Ah, je comprends mieux pourquoi toutes les versions marchaient, quels
que soient les iconv() effectués ! Il n'y a que de l'ASCII ici, et c'est
juste à l'affichage que tu montres une apostrophe...



Mais pourquoi cette entité n'apparait nulle part (dans le code) ?
1 2 3