Как удалить плохие символы, которые не подходят для кодировки utf8 в MySQL?
У меня грязные данные. Иногда он содержит символы типа . Я использую эти данные для создания таких запросов, как
WHERE a.address IN ('mydatahere')
Для этого символа я получаю
org.hibernate.exception.GenericJDBCException: Нелегальное сочетание сортировок (utf8_bin, IMPLICIT), (utf8mb4_general_ci, COERCIBLE), (utf8mb4_general_ci, COERCIBLE) для операции "IN"
Как я могу отфильтровать такие символы? Я использую Java.
Спасибо.
Ответы
Ответ 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 здесь.