2009-10-06 8 views
18

J'utilise PHP pour gérer le texte à partir d'une variété de sources. Je ne prévois pas que ce sera autre chose que UTF-8, ISO-8859-1, ou peut-être WINDOWS-1252. Si c'est autre chose que l'un d'entre eux, je dois juste m'assurer que le texte est transformé en une chaîne UTF-8 valide, même si les caractères sont perdus. Est-ce que l'option // TRANSLIT de iconv résout ce problème? Par exemple, ce code garantirait-il qu'une chaîne peut être insérée en toute sécurité dans un document codé UTF-8 (ou une base de données)?Garantir utf-8 valide en PHP

function make_safe_for_utf8_use($string) { 

    $encoding = mb_detect_encoding($string, "UTF-8,ISO-8859-1,WINDOWS-1252"); 

    if ($encoding != 'UTF-8') { 
     return iconv($encoding, 'UTF-8//TRANSLIT', $string); 
    } else { 
     return $string; 
    } 
} 

Répondre

32

UTF-8 peut stocker n'importe quel caractère Unicode. Si votre encodage est autre chose, y compris ISO-8859-1 ou Windows-1252, UTF-8 peut y stocker tous les caractères. Vous n'avez donc pas à vous soucier de perdre des caractères lorsque vous convertissez une chaîne de n'importe quel autre encodage en UTF-8. En outre, ISO-8859-1 et Windows-1252 sont des codages codés sur un seul octet où tout octet est valide. Il n'est techniquement pas possible de les distinguer. Je choisirais Windows-1252 comme correspondance par défaut pour les séquences non-UTF-8, car les seuls octets qui décodent différemment sont la gamme 0x80-0x9F. Ceux-ci décodent à divers caractères comme les citations intelligentes et l'euro dans Windows-1252, tandis que dans ISO-8859-1 ils sont des caractères de contrôle invisibles qui ne sont presque jamais utilisés. Les navigateurs Web peuvent parfois dire qu'ils utilisent ISO-8859-1, mais ils utiliseront souvent Windows-1252.

serait-ce ensure qu'une chaîne est en sécurité à insérer dans un document codé UTF-8

Vous voulez certainement définir le paramètre « stricte » en option à TRUE à cette fin. Mais je ne suis pas sûr que cela couvre réellement toutes les séquences UTF-8 invalides. La fonction ne prétend pas vérifier explicitement une séquence d'octets pour la validité de l'UTF-8. Il y a eu des cas connus où mb_detect_encoding devinerait incorrectement UTF-8 avant, bien que je ne sache pas si cela peut encore arriver en mode strict.

Si vous voulez être sûr, faites-le vous-même en utilisant le W3-recommended regex:

if (preg_match('%^(?: 
     [\x09\x0A\x0D\x20-\x7E]   # ASCII 
    | [\xC2-\xDF][\x80-\xBF]    # non-overlong 2-byte 
    | \xE0[\xA0-\xBF][\x80-\xBF]   # excluding overlongs 
    | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte 
    | \xED[\x80-\x9F][\x80-\xBF]   # excluding surrogates 
    | \xF0[\x90-\xBF][\x80-\xBF]{2}  # planes 1-3 
    | [\xF1-\xF3][\x80-\xBF]{3}   # planes 4-15 
    | \xF4[\x80-\x8F][\x80-\xBF]{2}  # plane 16 
)*$%xs', $string)) 
    return $string; 
else 
    return iconv('CP1252', 'UTF-8', $string); 
+0

Merci beaucoup. Je sais que les développeurs commenteront toujours la lenteur des regex - à quel point devrais-je utiliser ceci dans de grandes boucles avec beaucoup de texte? Par exemple, une boucle qui effectue une itération 200 fois et nettoie le texte de 10 000 caractères à chaque itération. – Brian

+0

Alors que je ne suis pas fan de regex, dans ce cas, il ne devrait pas être si mauvais. Regex devient lent lorsque vous avez des séquences '?'/'*'/'+' Imbriquées ou imbriquées qui peuvent obliger à revenir en arrière en cherchant différentes façons de correspondre. Cela n'arrivera pas dans ce cas. – bobince

+0

Excellent. Donc, lorsque vous utilisez iconv comme décrit ci-dessus, si je spécifie CP1252 comme jeu de caractères d'entrée, et que la chaîne est autre que CP1252 ou ISO-8859-1, elle renvoie une chaîne de sécurité UTF-8, bien que certains caractères puissent être perdus. Est-ce exact? – Brian

-1

Je ne sais pas si cela obtenir la même chose, mais ne pouvait pas utiliser simplement vous utf8_encode() sur tout le texte sans se soucier de la détection? Si le texte est déjà en UTF-8, cela ne le blessera pas. Et si ce n'est pas le cas, il sera converti. Si vous avez déjà pensé à le faire, y a-t-il une raison pour que cela ne fonctionne pas pour vous?

+3

utf8_encode est idempotent pas pour les séquences d'octets qui sont déjà UTF-8. Au lieu de cela, il les convertit en UTF-8 comme s'ils étaient auparavant ISO-8859-1; donc vous aurez par exemple. 'Α' au lieu de 'α'. – bobince

12

Avec mbstring bibliothèque, vous avez mb_check_encoding().

Exemple d'utilisation:

mb_check_encoding($string, 'UTF-8'); 

lorsque la performance, ce est plus rapide que le regex prévu dans la réponse acceptée.

Un test rapide sur mes spectacles de configuration (pour 20 000 itérations):

  • regex: ~ 310ms
  • mb_check_encoding: ~ 90ms

EDIT

Avec PHP 7.1.9 sur un récent système de Windows 10, la solution regex pour toute mb_check_encoding() surpasse longueur de la chaîne (encore 20 000 itérations):

  • 10 CHARS: regex => 4ms, mb_check_encoding() => 64ms
  • 10000 caractères: regex => 125ms, mb_check_encoding() => 2.4s
+0

Votre système doit crier rapidement, parce que je reçois ~ 5 secondes sur 7500 itérations sur un système assez moderne (Bien que je traite avec des chaînes assez grandes, pensez à l'HTML d'un site Web assez moderne.) –

3

Juste une remarque: au lieu d'utiliser le souvent recommandé (assez complexe) regular expression by W3C, vous pouvez simplement utiliser le modificateur 'u' pour tester une chaîne pour Validité UTF-8:

<?php 
    if (preg_match("//u", $string)) { 
     // $string is valid UTF-8 
    } 
+0

aussi dans les jours: [Comment détecter si vous devez appliquer utf8 décoder ou encoder sur une chaîne?] (http: // stackoverflow .com/a/4407996/367456) – hakre

+0

Vérification simple, mais pas totalement fiable, son comportement dépend de la version de PHP, mais plus important encore, il permet des séquences multi-octets non valides http://www.phpwact.org/php/ i18n/charsets # checking_utf-8_for_well_formedness –

0

réponse à "iconv est idempotente"

ne iconv - iconv n'idempotente

une grande différence entre utf8_encode() & iconv() est que iconv peut soulever des erreurs comme celle-ci " détection d'un caractère multi-octets incomplète dans la chaîne d'entrée » même avec

iconv ('ISO-8859-1', 'UTF-8'. '// IGNORE', $ str)

dans le code ci-dessus:

$ encoding = mb_detect_encoding ($ string, "UTF-8, ISO-8859-1, WINDOWS-1252");

vous devez savoir mb_detect_encoding peut répondre à UFT-8, même pour les chaînes de UTF8 invalides (mal formé utf8)

Questions connexes