Вопрос:
У меня проблема с удалением не-utf8 символов из строки, которые отображаются неправильно. Символы подобны 0x97 0x61 0x6C 0x6F (шестнадцатеричное представление)
Каков наилучший способ их удаления? Регулярное выражение или что-то еще?
Лучший ответ:
Использование подхода с регулярным выражением:
$regex = <<<‘END’ / ( (?: [x00-x7F] # single-byte sequences 0xxxxxxx | [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 ){1,100} # …one or more times ) | . # anything else /x END; preg_replace($regex, ‘$1’, $text);
Он ищет последовательности UTF-8 и захватывает их в группу 1. Он также соответствует одиночным байтам, которые не могут быть идентифицированы как часть последовательности UTF-8, но не захватывают их. Замена – это то, что было записано в группу 1. Это эффективно удаляет все недопустимые байты.
Можно восстановить строку, закодировав недопустимые байты как символы UTF-8. Но если ошибки случайны, это может оставить некоторые странные символы.
$regex = <<<‘END’ / ( (?: [x00-x7F] # single-byte sequences 0xxxxxxx | [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 ){1,100} # …one or more times ) | ( [x80-xBF] ) # invalid byte in range 10000000 — 10111111 | ( [xC0-xFF] ) # invalid byte in range 11000000 — 11111111 /x END; function utf8replacer($captures) { if ($captures[1] != «») { // Valid byte sequence. Return unmodified. return $captures[1]; } elseif ($captures[2] != «») { // Invalid byte of the form 10xxxxxx. // Encode as 11000010 10xxxxxx. return «xC2».$captures[2]; } else { // Invalid byte of the form 11xxxxxx. // Encode as 11000011 10xxxxxx. return «xC3».chr(ord($captures[3])-64); } } preg_replace_callback($regex, «utf8replacer», $text);
EDIT:
- !empty(x) будет соответствовать непустым значениям («0» считается пустым).
- x != «» будет соответствовать непустым значениям, включая «0».
- x !== «» будет соответствовать всем, кроме «».
x != «» кажется лучшим в этом случае.
Я немного ускорил матч. Вместо того, чтобы сопоставлять каждый символ отдельно, он соответствует последовательностям действительных символов UTF-8.
Ответ №1
Если вы примените utf8_encode() к уже строке UTF8, она вернет искаженный вывод UTF8.
Я сделал функцию, которая решает все эти проблемы. Он называется Encoding::toUTF8().
Вам не нужно знать, какова кодировка ваших строк. Это может быть Latin1 (ISO8859-1), Windows-1252 или UTF8, или в строке может быть их сочетание. Encoding::toUTF8() преобразует все в UTF8.
Я сделал это, потому что служба давала мне поток данных, которые все перепутали, смешивая эти кодировки в одной строке.
Использование:
require_once(‘Encoding.php’); use ForceUTF8Encoding; // It namespaced now. $utf8_string = Encoding::toUTF8($mixed_string); $latin1_string = Encoding::toLatin1($mixed_string);
Я включил еще одну функцию, Encoding :: fixUTF8(), которая будет фиксировать каждую строку UTF8, которая выглядит искаженным продуктом, потому что она была закодирована в UTF8 несколько раз.
Использование:
require_once(‘Encoding.php’); use ForceUTF8Encoding; // It namespaced now. $utf8_string = Encoding::fixUTF8($garbled_utf8_string);
Примеры:
echo Encoding::fixUTF8(«Fédération Camerounaise de Football»); echo Encoding::fixUTF8(«Fédération Camerounaise de Football»); echo Encoding::fixUTF8(«FÃÂédÃÂération Camerounaise de Football»); echo Encoding::fixUTF8(«Fédération Camerounaise de Football»);
выведет:
Fédération Camerounaise de Football Fédération Camerounaise de Football Fédération Camerounaise de Football Fédération Camerounaise de Football
Скачать:
https://github.com/neitanod/forceutf8
Ответ №2
Вы можете использовать mbstring:
$text = mb_convert_encoding($text, ‘UTF-8’, ‘UTF-8’);
… удалит недопустимые символы.
Смотрите: Замена недопустимых символов UTF-8 вопросительными знаками, mbstring.substitute_character кажется проигнорированным
Ответ №3
Эта функция удаляет все символы NON ASCII, это полезно, но не решает вопрос:
Это моя функция, которая всегда работает независимо от кодировки:
function remove_bs($Str) { $StrArr = str_split($Str); $NewStr = »; foreach ($StrArr as $Char) { $CharNo = ord($Char); if ($CharNo == 163) { $NewStr .= $Char; continue; } // keep £ if ($CharNo > 31 && $CharNo < 127) { $NewStr .= $Char; } } return $NewStr; }
Как это работает:
echo remove_bs(‘Hello õhowå åare youÆ?’); // Hello how are you? Ответ №4$text = iconv(«UTF-8», «UTF-8//IGNORE», $text);
Это то, что я использую. Кажется, работает очень хорошо. Взято из http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/
Ответ №5
попробуйте следующее:
$string = iconv(«UTF-8″,»UTF-8//IGNORE»,$string);
В соответствии с iconv manual, функция примет первый параметр в качестве входной кодировки, второй параметр в качестве выходной кодировки и третий как фактическая строка ввода.
Если вы установите как кодировку ввода и вывода на UTF-8, так и добавьте флаг //IGNORE к выходной кодировке, функция отбросит (разделит) все символы в строке ввода, которые не может быть представлена выходной кодировкой. Таким образом, фильтрация входной строки в действии.
Ответ №6
Текст может содержать символ не-utf8. Сначала попробуйте:
$nonutf8 = mb_convert_encoding($nonutf8 , ‘UTF-8’, ‘UTF-8’);
Подробнее об этом можно прочитать здесь: http://php.net/manual/en/function.mb-convert-encoding.php news
Ответ №7
UConverter можно использовать с PHP 5.5. UConverter лучше выбирать, если вы используете расширение intl и не используете mbstring.
function replace_invalid_byte_sequence($str) { return UConverter::transcode($str, ‘UTF-8’, ‘UTF-8’); } function replace_invalid_byte_sequence2($str) { return (new UConverter(‘UTF-8’, ‘UTF-8’))->convert($str); }
htmlspecialchars можно использовать для удаления недопустимой последовательности байтов с PHP 5.4. Htmlspecialchars лучше, чем preg_match для обработки большого размера байта и точности. Можно увидеть много неправильной реализации, используя регулярное выражение.
function replace_invalid_byte_sequence3($str) { return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, ‘UTF-8’)); } Ответ №8
Я создал функцию, которая удаляет недопустимые символы UTF-8 из строки.
Я использую его, чтобы очистить описание 27000 продуктов, прежде чем он создаст файл экспорта XML.
public function stripInvalidXml($value) { $ret = «»; $current; if (empty($value)) { return $ret; } $length = strlen($value); for ($i=0; $i < $length; $i++) { $current = ord($value{$i}); if (($current == 0x9) || ($current == 0xA) || ($current == 0xD) || (($current >= 0x20) && ($current <= 0xD7FF)) || (($current >= 0xE000) && ($current <= 0xFFFD)) || (($current >= 0x10000) && ($current <= 0x10FFFF))) { $ret .= chr($current); } else { $ret .= «»; } } return $ret; } Ответ №9$string = preg_replace(‘~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i’, ‘$1’, htmlentities($string, ENT_COMPAT, ‘UTF-8’)); Ответ №10
От недавнего патча к Drupal Feeds Модуль JSON-парсера:
//remove everything except valid letters (from any language) $raw = preg_replace(‘/(?:\\u[pLp{Zs}])+/’, », $raw);
Если вы обеспокоены да, он сохраняет пробелы как допустимые символы.
Сделал то, что мне было нужно. Он удаляет широко распространенные в настоящее время символы emoji, которые не вписываются в набор символов MySQL ‘utf8’, и это давало мне такие ошибки, как “SQLSTATE [HY000]: Общая ошибка: 1366 Неверное строковое значение”.
Подробнее см.
Ответ №11
Итак, правила состоят в том, что первый UTF-8 octlet имеет высокий бит, установленный как маркер, а затем от 1 до 4 бит до указать количество дополнительных октетов; то каждый из дополнительных октов должен иметь два бита с высоким значением, равным 10.
Псевдо-питон будет:
newstring = » cont = 0 for each ch in string: if cont: if (ch >> 6) != 2: # high 2 bits are 10 # do whatever, e.g. skip it, or skip whole point, or? else: # acceptable continuation of multi-octlet char newstring += ch cont -= 1 else: if (ch >> 7): # high bit set? c = (ch << 1) # strip the high bit marker while (c & 1): # while the high bit indicates another octlet c <<= 1 cont += 1 if cont > 4: # more than 4 octels not allowed; cope with error if !cont: # illegal, do something sensible newstring += ch # or whatever if cont: # last utf-8 was not terminated, cope
Эта же логика должна быть переводимой на php. Тем не менее, неясно, какой вид зачистки нужно делать, как только вы получите неверный характер.
Ответ №12
Чтобы удалить все символы Unicode за пределами базовой плоскости Unicode:
$str = preg_replace(«/[^\x00-\xFFFF]/», «», $str); Ответ №13
Немного отличается от вопроса, но то, что я делаю, это использовать HtmlEncode (string),
псевдо-код здесь
var encoded = HtmlEncode(string); encoded = Regex.Replace(encoded, «&#d+?;», «»); var result = HtmlDecode(encoded);
вход и выход
«Headlightx007E Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?» «Headlight~ Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?»
Я знаю, что это не идеально, но выполняет эту работу для меня.
Ответ №14
Добро пожаловать в 2019 и модификатор /u в регулярных выражениях, который будет обрабатывать многобайтовые символы UTF-8 для вас
Если вы используете только mb_convert_encoding($value, ‘UTF-8’, ‘UTF-8’), у вас все равно останутся непечатаемые символы в вашей строке
Этот метод будет:
- Удалите все недопустимые многобайтовые символы UTF-8 с помощью mb_convert_encoding
- Удалите все непечатаемые символы, такие как r, x00 (NULL-байт) и другие контрольные символы с помощью preg_replace
Метод:
function utf8_filter(string $value): string{ return preg_replace(‘/[^[:print:]n]/u’, », mb_convert_encoding($value, ‘UTF-8’, ‘UTF-8’)); }
[:print:] сопоставляет все печатные символы и n переводы строк и удаляет все остальное
Вы можете увидеть таблицу ASCII ниже. Печатные символы варьируются от 32 до 127, но символ новой строки n является частью контрольных символов, которые варьируются от 0 до 31, поэтому мы должны добавить новую строку в регулярное выражение /[^[:print:]n]/u
Вы можете попытаться отправить строки через регулярное выражение с символами за пределами диапазона печати, например x7F (DEL), x1B (Esc) и т.д., И посмотреть, как они удаляются
function utf8_filter(string $value): string{ return preg_replace(‘/[^[:print:]n]/u’, », mb_convert_encoding($value, ‘UTF-8’, ‘UTF-8’)); } $arr = [ ‘Danish chars’ => ‘Hello from Denmark with æøå’, ‘Non-printable chars’ => «x7FHello with invalid charsr x00» ]; foreach($arr as $k => $v){ echo «$k:n———n»; $len = strlen($v); echo «$vn(«.$len.»)n»; $strip = utf8_decode(utf8_filter(utf8_encode($v))); $strip_len = strlen($strip); echo $strip.»n(«.$strip_len.»)nn»; echo «Chars removed: «.($len — $strip_len).»nnn»; }
https://www.tehplayground.com/q5sJ3FOddhv1atpR
Ответ №15
Как насчет iconv:
http://php.net/manual/en/function.iconv.php
Не использовал его внутри самого PHP, но он всегда хорошо работал у меня в командной строке. Вы можете заставить его заменить недопустимые символы.