У меня есть небольшая грязная база данных, содержащая имена многих учреждений по всему миру.
Я хочу отображать их, включая национальные символы, но без недопустимых символов – те, которые отображаются в firefox как номера юникода.
Как их отфильтровать?
База данных имеет кодировку utf8, но некоторые строки были вставлены с неправильными кодировками или были беспорядок уже в источниках.
Я не хочу исправлять базу данных – она слишком большая. Я хочу просто отфильтровать его – «с глаз долой из головы»
Я хочу просто отфильтровать его
У вас есть неуказанная кодировка / кодировка с вашими данными. Это огромная проблема.
Сначала вы можете преобразовать его в utf-8
а затем удалить все непечатаемые символы:
$str = iconv('utf-8', 'utf-8//ignore', $str); echo preg_replace('/[^\pL\pN\pP\pS\pZ]/u', '', $str);
Проблема в том, что функция iconv
может только попробовать. Это приведет к потере любой недопустимой последовательности символов. Начиная с php 5.4, он будет отбрасывать полную строку, если указанная входная кодировка недействительна.
После PHP 5.3 вы увидите предупреждение, что входная строка имеет недопустимую кодировку.
Вы можете обойти это, сначала удалив все недействительные последовательности байтов utf-8
:
$str = valid_utf8_bytes($str); echo preg_replace('/[^\pL\pN\pP\pS\pZ]/u', '', $str); /** * get valid utf-8 byte squences * * take over all matching bytes, drop an invalid sequence until first * non-matching byte. * * @param string $str * @return string */ function valid_utf8_bytes($str) { $return = ''; $length = strlen($str); $invalid = array_flip(array("\xEF\xBF\xBF" /* U-FFFF */, "\xEF\xBF\xBE" /* U-FFFE */)); for ($i=0; $i < $length; $i++) { $c = ord($str[$o=$i]); if ($c < 0x80) $n=0; # 0bbbbbbb elseif (($c & 0xE0) === 0xC0) $n=1; # 110bbbbb elseif (($c & 0xF0) === 0xE0) $n=2; # 1110bbbb elseif (($c & 0xF8) === 0xF0) $n=3; # 11110bbb elseif (($c & 0xFC) === 0xF8) $n=4; # 111110bb else continue; # Does not match for ($j=++$n; --$j;) # n bytes matching 10bbbbbb follow ? if ((++$i === $length) || ((ord($str[$i]) & 0xC0) != 0x80)) continue 2 ; $match = substr($str, $o, $n); if ($n === 3 && isset($invalid[$match])) # test invalid sequences continue; $return .= $match; } return $return; }
База данных может быть не полностью проблемой – если таблицы закодированы в utf8, строки в них должны были быть преобразованы (я думаю). Проблема, с которой я столкнулся, заключается в правильном обеспечении согласованности кодирования. Например, соединитель mysqli, по умолчанию, возвращается к Latin-8859 IIRC, поэтому вполне возможно получить вывод в utf8, базе данных в utf8 и все еще в конечном итоге? потому что они были переведены на латиницу с помощью соединителя mysqli.
Чтобы обеспечить utf8 по всем направлениям, вам нужно сделать что-то вроде:
В базе данных:
убедитесь, что сортировка – это что-то вроде utf8_general_ci
В верхней части файла представления PHP:
<?php header('Content-Type:Text/Plain;charset=utf-8'); ?>
В метатеге HTML (необязательно):
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
И в соединителе базы данных (например, с использованием MySQLi):
mysqli::set_charset('utf8'); #note that for MySQL it isn't hyphenated
Возможно, вы все равно решите проблему.
Если база данных – это проблема, которая, по-видимому, в вашем случае (и исправление ее в стороне), возможно, просто распечатайте каждый символ из строки с помощью ORD и найдите значение для неконтролируемого отправления.
Затем, когда вы знаете значение контрольного символа, передайте эти значения в функцию, которая ищет этот управляющий символ, и попытайтесь изменить кодировку utf-8 (некорректную) с соответствующими символами UTF8 в реальном времени.