Как заменить / удалить 4 (+) – байтовые символы из строки UTF-8 в PHP?

Похоже, что MySQL не поддерживает символы с более чем 3 байтами в своей кодировке UTF-8 по умолчанию.

Итак, в PHP, как я могу избавиться от всех 4 (-и больше) -байтных символов в строке и заменить их чем-то вроде какого-то другого персонажа?

Related of "Как заменить / удалить 4 (+) – байтовые символы из строки UTF-8 в PHP?"

ПРИМЕЧАНИЕ. Вы не должны просто снимать, а заменять с помощью символа U + FFFD для избежания юникодных атак, в основном XSS:

http://unicode.org/reports/tr36/#Deletion_of_Noncharacters

 preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value); 

Поскольку 4-байтовые последовательности UTF-8 всегда начинаются с байтов 0xF0-0xF7 , следующее должно работать:

 $str = preg_replace('/[\xF0-\xF7].../s', '', $str); 

В качестве альтернативы вы можете использовать preg_replace в режиме UTF-8, но это, вероятно, будет медленнее:

 $str = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $str); 

Это работает, потому что 4-байтовые последовательности UTF-8 используются для кодовых точек в дополнительных Unicode-плоскостях, начиная с 0x10000 .

Вот пример:

 <?php mb_internal_encoding("UTF-8"); //utf8 string, 13 bytes, 9 utf8 chars, 7 ASCII, 1 in latin1, 1 outside the BMP $str = "qué \xF0\x9D\x92\xB3 tal"; $array = mbStringToArray($str); print "str: [$str] strlen:" . strlen($str) . " chars:" . count($array) . "\n"; $str1 = ""; foreach($array as $c) { // print "$c : " . strlen($c) ."\n"; $str1 .= strlen($c)<=3? $c : '?'; } print "[$str1]\n"; function mbStringToArray ($str) { if (empty($str)) return false; $len = mb_strlen($str); $array = array(); for ($i = 0; $i < $len; $i++) { $array[] = mb_substr($str, $i, 1); } return $array; } 

Или, немного более компактный и эффективный:

 <?php /// mb_internal_encoding("UTF-8"); //utf8 string, 13 bytes, 9 utf8 chars, 7 ASCII, 1 in latin1, 1 outside the BMP $str = "qué \xF0\x9D\x92\xB3 tal"; $str1 = trimOutsideBMP($str); print "original: [$str]\n"; print "trimmed: [$str1]\n"; // Replaces non-BMP characters in the UTF-8 string by a '?' character // Assumes UTF-8 default encoding ( if not sure, call first mb_internal_encoding("UTF-8"); ) function trimOutsideBMP($str) { if (empty($str)) return $str; $len = mb_strlen($str); $str1 = ''; for ($i = 0; $i < $len; $i++) { $c = mb_substr($str, $i, 1); $str1 .= strlen($c) <= 3 ? $c : '?'; } return $str1; } 

Пошел на этот вопрос, пытаясь решить мою собственную проблему (Facebook выплескивает некоторые смайлики в виде 4-байтовых символов, Amazon Mechanical Turk не принимает 4-байтовые символы).

Я закончил использовать это, не требует расширения mbstring:

 function remove_4_byte($string) { $char_array = preg_split('/(?<!^)(?!$)/u', $string ); for($x=0;$x<sizeof($char_array);$x++) { if(strlen($char_array[$x])>3) { $char_array[$x] = ""; } } return implode($char_array, ""); } 

Ниже функция изменяет 3 и 4 байта символов из строки utf8 в '#':

 function remove3and4bytesCharFromUtf8Str($str) { return preg_replace('/([\xF0-\xF7]...)|([\xE0-\xEF]..)/s', '#', $str); } 

Вот моя реализация, чтобы отфильтровать 4-байтовые символы

 $string = preg_replace_callback( '/./u', function (array $match) { return strlen($match[0]) >= 4 ? null : $match[0]; }, $string ); 

вы можете настроить его и заменить null (который удаляет символ) с помощью некоторой строки-заменителя. Вы также можете заменить >= 4 на некоторую другую проверку длины байта.