web-dev-qa-db-fra.com

Comment supprimer les mauvais caractères qui ne conviennent pas au codage utf8 dans MySQL?

J'ai des données sales. Parfois, il contient des caractères comme this . J'utilise ces données pour faire des requêtes comme

WHERE a.address IN ('mydatahere')

Pour ce personnage, je reçois

org.hibernate.exception.GenericJDBCException: mélange illégal de classements (utf8_bin, IMPLICIT), (utf8mb4_general_ci, COERCIBLE), (utf8mb4_general_ci, COERCIBLE) pour l'opération 'IN'

Comment filtrer des personnages comme celui-ci? J'utilise Java.

Merci.

16
Stepan Yakovenko

Quand j'ai eu un problème comme celui-ci, j'ai utilisé un script Perl pour m'assurer que les données sont converties en UTF-8 valide en utilisant du code comme celui-ci:

use Encode;
binmode(STDOUT, ":utf8");
while (<>) {
    print Encode::decode('UTF-8', $_);
}

Ce script prend (éventuellement corrompu) UTF-8 sur stdin et réimprime UTF-8 valide dans stdout. Les caractères non valides sont remplacés par (U+FFFD, caractère de remplacement Unicode ).

Si vous exécutez ce script sur une bonne entrée UTF-8, la sortie doit être identique à l'entrée.

Si vous avez des données dans la base de données, il est judicieux d'utiliser DBI pour analyser vos tables et nettoyer toutes les données en utilisant cette approche pour vous assurer que tout est UTF-8 valide.

Ceci est la version Perl one-liner de ce même script:

Perl -MEncode -e "binmode STDOUT,':utf8';while(<>){print Encode::decode 'UTF-8',\$_}" < bad.txt > good.txt

EDIT: Ajout d'une solution Java uniquement .

Voici un exemple comment faire cela en Java:

import Java.nio.ByteBuffer;
import Java.nio.CharBuffer;
import Java.nio.charset.CharacterCodingException;
import Java.nio.charset.Charset;
import Java.nio.charset.CharsetDecoder;
import Java.nio.charset.CodingErrorAction;

public class UtfFix {
    public static void main(String[] args) throws InterruptedException, CharacterCodingException {
        CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
        decoder.onMalformedInput(CodingErrorAction.REPLACE);
        decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
        ByteBuffer bb = ByteBuffer.wrap(new byte[] {
            (byte) 0xD0, (byte) 0x9F, // 'П'
            (byte) 0xD1, (byte) 0x80, // 'р'
            (byte) 0xD0,              // corrupted UTF-8, was 'и'
            (byte) 0xD0, (byte) 0xB2, // 'в'
            (byte) 0xD0, (byte) 0xB5, // 'е'
            (byte) 0xD1, (byte) 0x82  // 'т'
        });
        CharBuffer parsed = decoder.decode(bb);
        System.out.println(parsed);
        // this prints: Пр?вет
    }
}
9
mvp

Vous pouvez l'encoder puis le décoder vers/depuis UTF-8:

String label = "look into my eyes 〠.〠";

Charset charset = Charset.forName("UTF-8");
label = charset.decode(charset.encode(label)).toString();

System.out.println(label);

production:

look into my eyes ?.?

edit: Je pense que cela pourrait fonctionner uniquement sur Java 6.

7
Ring

Vous pouvez filtrer les caractères de substitution avec cette expression régulière:

String str  = "????"; //U+20000, represented by 2 chars in Java (UTF-16 surrogate pair)
str = str.replaceAll( "([\\ud800-\\udbff\\udc00-\\udfff])", "");
System.out.println(str.length()); //0
3
Esailija

Une fois que vous avez converti le tableau d'octets en chaîne sur la machine Java, vous obtiendrez (par défaut sur la plupart des machines) une chaîne codée UTF-16. La bonne solution pour se débarrasser des non UTF-8 caractères est avec le code suivant:

String[] values = {"\\xF0\\x9F\\x98\\x95", "\\xF0\\x9F\\x91\\x8C", "/*", "look into my eyes 〠.〠", "fkdjsf ksdjfslk", "\\xF0\\x80\\x80\\x80", "aa \\xF0\\x9F\\x98\\x95 aa", "Ok"};
for (int i = 0; i < values.length; i++) {
    System.out.println(values[i].replaceAll(
                    //"[\\\\x00-\\\\x7F]|" + //single-byte sequences   0xxxxxxx - commented because of capitol letters
                    "[\\\\xC0-\\\\xDF][\\\\x80-\\\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                    "[\\\\xE0-\\\\xEF][\\\\x80-\\\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                    "[\\\\xF0-\\\\xF7][\\\\x80-\\\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
            , ""));
}

ou si vous voulez valider si une chaîne contient des caractères non utf8, vous utiliserez Pattern.matches comme:

String[] values = {"\\xF0\\x9F\\x98\\x95", "\\xF0\\x9F\\x91\\x8C", "/*", "look into my eyes 〠.〠", "fkdjsf ksdjfslk", "\\xF0\\x80\\x80\\x80", "aa \\xF0\\x9F\\x98\\x95 aa", "Ok"};
for (int i = 0; i < values.length; i++) {
    System.out.println(Pattern.matches(
                    ".*(" +
                    //"[\\\\x00-\\\\x7F]|" + //single-byte sequences   0xxxxxxx - commented because of capitol letters
                    "[\\\\xC0-\\\\xDF][\\\\x80-\\\\xBF]|" + //double-byte sequences   110xxxxx 10xxxxxx
                    "[\\\\xE0-\\\\xEF][\\\\x80-\\\\xBF]{2}|" + //triple-byte sequences   1110xxxx 10xxxxxx * 2
                    "[\\\\xF0-\\\\xF7][\\\\x80-\\\\xBF]{3}" //quadruple-byte sequence 11110xxx 10xxxxxx * 3
                    + ").*"
            , values[i]));
}

Pour rendre une application Web entière compatible UTF8, lisez ici:
Comment faire fonctionner UTF-8 dans Java webapps
Plus d'informations sur les codages et les chaînes d'octets .
Vous pouvez vérifier votre modèle ici .
La même chose dans PHP ici .

2
despot

In PHP - J'approche cela en n'autorisant que les données imprimables. Cela aide vraiment à nettoyer les données pour la base de données.
C'est du prétraitement et parfois, vous n'avez pas ce luxe.

$str = preg_replace('/[[:^print:]]/', '', $str);
0
Chris Lambrou