40 votes

Comment gérer la saisie par l'utilisateur de caractères UTF-8 non valides?

Je suis à la recherche d'général d'une stratégie ou des conseils sur comment gérer l'UTF-8 non valide les données des utilisateurs.

Même si ma webapp utilise UTF-8, en quelque sorte, certains utilisateurs d'entrer des caractères non valides. Cela provoque des erreurs dans PHP json_encode() et l'ensemble semble être une mauvaise idée d'avoir autour.

W3C I18N FAQ: Multilingue Formes dit "Si non-UTF-8 de la réception des données, un message d'erreur sera envoyé en retour.".

  • Exactement comment cela devrait-il être pratiquement terminé, l'ensemble d'un site avec des dizaines de lieux différents où les données peuvent être entrées?
  • Comment voulez-vous présenter l'erreur d'une manière utile pour l'utilisateur?
  • Comment voulez-vous stocker temporairement et affichage de la mauvaise forme de données de sorte que l'utilisateur ne veut pas perdre tout leur texte? Bande de mauvais caractères? Utiliser un caractère de remplacement, et comment?
  • Pour les données existantes dans la base de données, lors de l'UTF-8 non valide de données est détectée, il faut que j'essaie de le convertir et de les enregistrer en arrière (comment? utf8_encode()? mb_convert_encoding()?), ou laisser comme tel dans la base de données, mais de faire quelque chose (quoi?) avant json_encode()?

EDIT: je suis très familier avec l'extension mbstring et ne te demande pas "comment UTF-8 en PHP". J'aimerais des avis de personnes ayant de l'expérience dans des situations du monde réel comment ils ont traité ce.

EDIT2: en tant Que partie de la solution, j'aimerais vraiment voir un rapide méthode pour convertir des caractères non valides à U+FFFD

62voto

Alix Axel Points 63455

L' accept-charset="UTF-8" attribut n'est qu'une ligne directrice pour les navigateurs à suivre, ils ne sont pas contraints de se soumettre que de cette manière, merde formulaire de soumission les bots sont un bon exemple...

Ce que je fais habituellement est de les ignorer, de mauvais caractères, soit par l'intermédiaire d' iconv() ou avec le moins fiable utf8_encode() / utf8_decode() fonctions, si vous utilisez iconv vous avez également l'option pour translittérer les mauvais caractères.

Voici un exemple d'utilisation de iconv():

$str_ignore = iconv('UTF-8', 'UTF-8//IGNORE', $str);
$str_translit = iconv('UTF-8', 'UTF-8//TRANSLIT', $str);

Si vous souhaitez afficher un message d'erreur à vos utilisateurs, je serais probablement le faire de manière globale plutôt que par la valeur reçue base, quelque chose comme ce serait probablement faire tout aussi bien:

function utf8_clean($str)
{
    return iconv('UTF-8', 'UTF-8//IGNORE', $str);
}

$clean_GET = array_map('utf8_clean', $_GET);

if (serialize($_GET) != serialize($clean_GET))
{
    $_GET = $clean_GET;
    $error_msg = 'Your data is not valid UTF-8 and has been stripped.';
}

// $_GET is clean!

Vous pouvez également normaliser les nouvelles lignes et de la bande de (non-)visibles les caractères de contrôle, comme ceci:

function Clean($string, $control = true)
{
    $string = iconv('UTF-8', 'UTF-8//IGNORE', $string);

    if ($control === true)
    {
            return preg_replace('~\p{C}+~u', '', $string);
    }

    return preg_replace(array('~\r\n?~', '~[^\P{C}\t\n]+~u'), array("\n", ''), $string);
}

Code pour convertir en UTF-8 Unicode codepoints:

function Codepoint($char)
{
    $result = null;
    $codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));

    if (is_array($codepoint) && array_key_exists(1, $codepoint))
    {
        $result = sprintf('U+%04X', $codepoint[1]);
    }

    return $result;
}

echo Codepoint('à'); // U+00E0
echo Codepoint('ひ'); // U+3072

Probablement plus rapide que n'importe quel autre, n'ai pas testé beaucoup de bien.


Exemple:

$string = 'hello world�';

// U+FFFEhello worldU+FFFD
echo preg_replace_callback('/[\p{So}\p{Cf}\p{Co}\p{Cs}\p{Cn}]/u', 'Bad_Codepoint', $string);

function Bad_Codepoint($string)
{
    $result = array();

    foreach ((array) $string as $char)
    {
        $codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));

        if (is_array($codepoint) && array_key_exists(1, $codepoint))
        {
            $result[] = sprintf('U+%04X', $codepoint[1]);
        }
    }

    return implode('', $result);
}

Est-ce ce que vous recherchez?

4voto

Archimedix Points 3595

La réception des caractères non valides à partir de votre application web pourrait avoir à faire avec les jeux de caractères supposé pour les formulaires HTML. Vous pouvez spécifier le jeu de caractères à utiliser pour les formulaires avec l' accept-charset d'attribut:

<form action="..." accept-charset="UTF-8">

Vous pouvez également jeter un oeil à des questions similaires dans StackOverflow pour les pointeurs sur la façon de gérer des caractères non valides, par exemple ceux dans la colonne à droite, mais je pense que la signalisation d'une erreur de l'utilisateur est mieux que d'essayer de nettoyer ces caractères non valides qui causent la perte inattendue de données importantes ou changement inattendu de l'utilisateur de votre intrants.

2voto

Nev Stokes Points 3162

J'ai mis en place une classe assez simple pour vérifier si l'entrée est en UTF-8 et pour parcourir utf8_encode() selon les besoins:

 class utf8
{

    /**
     * @param array $data
     * @param int $options
     * @return array
     */
    public static function encode(array $data)
    {
        foreach ($data as $key=>$val) {
            if (is_array($val)) {
                $data[$key] = self::encode($val, $options);
            } else {
                if (false === self::check($val)) {
                    $data[$key] = utf8_encode($val);
                }
            }
        }

        return $data;
    }

    /**
     * Regular expression to test a string is UTF8 encoded
     * 
     * RFC3629
     * 
     * @param string $string The string to be tested
     * @return bool
     * 
     * @link http://www.w3.org/International/questions/qa-forms-utf-8.en.php
     */
    public static function check($string)
    {
        return 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);
    }
}

// For example
$data = utf8::encode($_POST);
 

1voto

Otar Points 1528

Il existe une extension multi-octets pour PHP, consultez-la: http://www.php.net/manual/en/book.mbstring.php

Vous devriez essayer la fonction mb_check_encoding () .

Bonne chance!

0voto

Elzo Valugi Points 10906

Que diriez-vous de supprimer tous les caractères en dehors de votre sous-ensemble donné. Au moins dans certaines parties de mon application, je n'autoriserais pas l'utilisation de caractères en dehors des [aZ] [0-9 sets], par exemple les noms d'utilisateur. Vous pouvez créer une fonction de filtrage qui supprime silencieusement tous les caractères en dehors de cette plage, ou qui renvoie une erreur s'il les détecte et envoie la décision à l'utilisateur.

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