Подтвердить что ты не робот

Как удалить плохие символы, которые не подходят для кодировки utf8 в MySQL?

У меня грязные данные. Иногда он содержит символы типа . Я использую эти данные для создания таких запросов, как

WHERE a.address IN ('mydatahere')

Для этого символа я получаю

org.hibernate.exception.GenericJDBCException: Нелегальное сочетание сортировок (utf8_bin, IMPLICIT), (utf8mb4_general_ci, COERCIBLE), (utf8mb4_general_ci, COERCIBLE) для операции "IN"

Как я могу отфильтровать такие символы? Я использую Java.

Спасибо.

4b9b3361

Ответ 1

Может быть, это поможет кому-то, поскольку это помогло мне.

public static String removeBadChars(String s) {
  if (s == null) return null;
  StringBuilder sb = new StringBuilder();
  for(int i=0;i<s.length();i++){ 
    if (Character.isHighSurrogate(s.charAt(i))) continue;
    sb.append(s.charAt(i));
  }
  return sb.toString();
}

Ответ 2

Когда у меня возникла такая проблема, я использовал Perl script, чтобы убедиться, что данные были преобразованы в действительный UTF-8 с помощью следующего кода:

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

Этот script принимает (возможно, поврежден) UTF-8 на stdin и перепечатывает действительный UTF-8 до stdout. Недопустимые символы заменяются на (U+FFFD, символ замены Юникода).

Если вы запустите этот script на хорошем входе UTF-8, вывод должен быть идентичным входу.

Если у вас есть данные в базе данных, имеет смысл использовать DBI для сканирования ваших таблиц и сглаживания всех данных с помощью этого подхода, чтобы убедиться, что все верно UTF-8.

Это однострочная версия Perl этого же script:

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

EDIT: добавлено решение только для Java.

Это пример того, как это сделать в 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: Пр?вет
    }
}

Ответ 3

Вы можете кодировать и затем декодировать его в/из 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);

выход:

look into my eyes ?.?

Ответ 4

Вы можете фильтровать суррогатные символы с помощью этого регулярного выражения:

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

Ответ 5

Как только вы преобразуете массив байтов в String на java-машине, вы получите (по умолчанию на большинстве машин) кодированную строку UTF-16. Правильное решение, чтобы избавиться от символов не UTF-8, имеет следующий код:

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
            , ""));
}

или если вы хотите проверить, содержит ли какая-либо строка символы не utf8, вы должны использовать Pattern.matches, например:

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]));
}

Для того, чтобы сделать все веб-приложение совместимым с UTF8, читайте здесь:
Как получить UTF-8, работающий в Java Webapps
Подробнее о байтовых кодировках и строках.
Вы можете проверить свой шаблон здесь.
То же самое в PHP здесь.