164 votes

PHP: Convertir une chaîne en UTF-8 sans connaître l'original jeu de caractères, ou au moins essayer

J'ai une application qui traite avec des clients de partout dans le monde, et, naturellement, je veux tout ce qui se passe dans mes bases de données pour être codé en UTF-8.

Le principal problème pour moi est que je ne sais pas ce que le codage de la source de n'importe quelle chaîne va être - c'est peut-être à partir d'une zone de texte (à l'aide d' <form accept-charset="utf-8"> n'est utile que si l'utilisateur est soumis le formulaire), ou il pourrait être téléchargés à partir d'un fichier texte, donc je n'ai pas vraiment de contrôle sur l'entrée.

J'ai besoin d'une fonction ou d'une classe qui fait que les choses qui se passent dans ma base de données est, dans la mesure du possible, codé en UTF-8. J'ai essayé d' iconv(mb_detect_encoding($text), "UTF-8", $text); mais qui a des problèmes (si l'entrée est 'fiancée' il renvoie 'fiancé'). J'en ai essayé beaucoup de choses =/

Pour les uploads de fichier, j'aime l'idée de demander à l'utilisateur de préciser l'encodage qu'ils utilisent, et de leur montrer les aperçus de ce que la sortie va ressembler, mais ce n'aide pas contre les méchants pirates (en fait, on pourrait leur rendre la vie un peu plus facile).

J'ai lu les autres questions sur le sujet, mais ils semblent tous avoir des différences subtiles comme "j'ai besoin d'analyser les flux RSS" ou "je gratter les données de sites web" (ou, en fait, "on ne peut pas").

Mais il doit y avoir quelque chose qui au moins a un bon d'essayer!

281voto

Jeff Day Points 1241

Ce que vous me demandez est extrêmement difficile. Si possible, arriver à l'utilisateur de spécifier l'encodage est le meilleur. La prévention d'une attaque ne devrait pas être beaucoup plus facile ou plus difficile de cette façon.

Cependant, vous pourriez essayer de faire ceci:

iconv(mb_detect_encoding($text, mb_detect_order(), true), "UTF-8", $text);

Le paramètre stricte pourrait vous aider à obtenir un meilleur résultat.

30voto

Oroboros102 Points 967

Dans la mère-patrie, la Russie, nous avons 4 encodages populaires, de sorte que votre question est en grande demande ici.

Seulement par char codes de symboles vous ne pouvez pas détecter l'encodage, car les pages de code se croisent. Certaines pages de code dans différentes langues ont même intersection complète. Donc, nous avons besoin d'une autre approche.

Le seul moyen de travailler avec des inconnus encodages est de travailler avec des probabilités. Donc, nous ne voulons pas répondre à la question "qu'est-ce que l'encodage de ce texte?", nous essayons de comprendre "ce qui est le plus susceptible d'encodage de ce texte?".

Un gars ici en russe populaire tech blog inventé cette approche:

Construire la probabilité gamme de char codes dans chaque encodage que vous voulez soutenir. Vous pouvez le construire à l'aide de quelques grands textes dans votre langue (par exemple, un peu de fiction, l'utilisation de Shakespeare pour l'anglais et de Tolstoï pour le russe, lol ). Vous obtiendrez qch comme ceci:

    encoding_1:
    190 => 0.095249209893009,
    222 => 0.095249209893009,
    ...
    encoding_2:
    239 => 0.095249209893009,
    207 => 0.095249209893009,
    ...
    encoding_N:
    charcode => probabilty

Prochaine. Vous prenez un texte inconnu de codage et pour chaque encodage dans votre "probabilité dictionnaire" de votre recherche pour la fréquence de chaque symbole dans l'inconnu du texte codé. La somme des probabilités des symboles. Encodage avec le plus grand cote est probable que le vainqueur. De meilleurs résultats pour les plus grands textes.

Si vous êtes intéressé, je peux volontiers vous aider dans cette tâche. Nous pouvons grandement améliorer la précision par la construction de deux charcodes probabilty liste.

Btw. mb_detect_encoding certanly ne fonctionne pas. Oui, à tous. S'il vous plaît, jetez un oeil de mb_detect_encoding code source dans "ext/mbstring/libmbfl/mbfl/mbfl_ident.c".

12voto

Alexey Gerasimov Points 1658

Vous avez probablement essayé, mais pourquoi ne pas simplement utiliser le mb_convert_encoding fonction? Il va tenter d'auto-détecter le jeu de caractères du texte ou vous pouvez passer d'une liste.

Aussi, j'ai essayé d'exécuter:

$text = "fiancée";
echo mb_convert_encoding($text, "UTF-8");
echo "<br/><br/>";
echo iconv(mb_detect_encoding($text), "UTF-8", $text);

et les résultats sont les mêmes pour les deux. Comment voyez-vous que votre texte est tronqué à 'fiancé'? est-il dans la base de données ou dans un navigateur?

5voto

matthiasmullie Points 1127

Il n'y a aucun moyen d'identifier le jeu de caractères d'une chaîne qui est tout à fait exact. Il y a des façons d'essayer de deviner le jeu de caractères. L'un de ces moyens, et probablement/actuellement le meilleur en PHP, est mb_detect_encoding(). Cela va scanner votre chaîne et de rechercher les occurrences de choses uniques à certains jeux de caractères. En fonction de votre chaîne, il y a peut-être pas à distinguer les occurrences.

Prendre l'ISO-8859-1 jeu de caractères vs ISO-8859-15 ( http://en.wikipedia.org/wiki/ISO/IEC_8859-15#Changes_from_ISO-8859-1 )

Il ya seulement une poignée de personnages différents, et pour empirer les choses, ils sont représentés par le même octets. Il n'y a aucun moyen de le détecter, étant donné une chaîne de caractères, sans le savoir, l'encodage, si octet 0xA4 est censé signifier ¤ € ou dans votre chaîne, donc il n'y a aucun moyen de savoir c'est exact charset.

(Remarque: vous pouvez ajouter un facteur humain, ou encore plus avancé de la technique de balayage (par exemple, ce qui Oroboros102 l'indique), pour essayer de comprendre basée sur le contexte environnant, si le caractère doit être ¤ € ou, si cela semble comme un pont trop loin)

Il y a plus à distinguer les différences entre par exemple UTF-8 et ISO-8859-1, c'est donc toujours la peine d'essayer de comprendre quand vous n'êtes pas sûr, si vous pouvez et ne devez jamais compter sur elle.

Lecture intéressante: http://kore-nordmann.de/blog/php_charset_encoding_FAQ.html#how-do-i-determine-the-charset-encoding-of-a-string

Il y a d'autres façons d'assurer le bon charset. Concernant les formes, essayez de faire respecter l'UTF-8 comme beaucoup que possible (consultez le bonhomme de neige à assurez-vous que votre présentation doit être en UTF-8 dans tous les navigateurs: http://intertwingly.net/blog/2010/07/29/Rails-and-Snowmen) Cela étant fait, au moins vous pouvez être sûr que chaque texte soumis par le biais de vos formulaires est utf_8. Concernant les fichiers téléchargés, essayez d'exécuter la unix "fichier -i' commande, par exemple par exec() (si possible sur votre serveur) à l'aide de la détection (en utilisant le document de la NOMENCLATURE.) Concernant le raclage de données, vous pouvez lire les en-têtes HTTP, qui, généralement, spécifier le jeu de caractères. Lors de l'analyse des fichiers XML, voir si le XML meta-données contiennent un jeu de caractères définition.

Plutôt que d'essayer de deviner automatiquement le jeu de caractères, vous devez d'abord essayer de s'assurer une certaine charset vous-même, si possible, ou en essayant d'attraper une définition de la source que vous êtes obtenir à partir (le cas échéant) avant de recourir à la détection.

2voto

hakre Points 102271

Le principal problème pour moi est que je ne sais pas ce que le codage de la source de n'importe quelle chaîne va être - c'est peut-être à partir d'une zone de texte (à l'aide n'est utile que si l'utilisateur est soumis le formulaire), ou il pourrait être téléchargés à partir d'un fichier texte, donc je n'ai pas vraiment de contrôle sur l'entrée.

Je ne pense pas que c'est un problème. Une application connaît la source de l'entrée. Si c'est à partir d'un formulaire, utiliser l'encodage UTF-8 dans votre cas. Qui fonctionne. Suffit de vérifier les données fournies est codé correctement (validation). Gardez à l'esprit que toutes les bases de données de support de l'UTF-8 dans sa gamme complète.

Si c'est un fichier que vous n'aurez pas l'enregistrer en UTF-8 dans la base de données, mais sous forme binaire. Lors de la sortie de nouveau le fichier, utiliser les binaires de sortie, ce qui est totalement transparent.

Votre idée est sympa qu'un utilisateur peut déterminer l'encodage, il/elle peut dire de toute façon après avoir téléchargé le fichier, c'est du binaire.

Donc, je dois avouer que je ne vois pas un problème spécifique, vous relancez avec votre question. Mais peut-être que vous pouvez ajouter un peu plus de détails ce qu'est ton problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X