У меня проблема с удалением из строки символов, отличных от UTF-8, которые не отображаются должным образом. Символы выглядят так: 0x97 0x61 0x6C 0x6F (шестнадцатеричное представление). Как лучше всего их удалить? Использовать регулярное выражение или что-то еще?
Ответ 1
Если вы примените utf8_encode() к уже существующей UTF8-строке, она вернет искаженный UTF8-вывод. Я создал функцию, которая решает все эти проблемы. Она называется Encoding::toUTF8(). Вам не нужно знать, какая кодировка у ваших строк. Это может быть Latin1 (ISO8859-1), Windows-1252 или UTF-8, или строка может иметь их комбинацию. Encoding::toUTF8() преобразует все в UTF-8. Я сделал это потому, что один сервис выдавал мне данные в перепутанном виде, смешивая эти кодировки в одной строке.
Использование:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // Теперь это пространство имен.
$utf8_string = Encoding::toUTF8($mixed_string);
$latin1_string = Encoding::toLatin1($mixed_string);
Я включил еще одну функцию, Encoding::fixUTF8(), которая исправит каждую строку UTF-8, которая выглядит искаженной из-за того, что была закодирована в UTF-8 несколько раз.
Использование:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // Теперь это пространство имен.
$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
Ответ 2
Использование регексного подхода:
$regex = <<<'END'
/
(
(?: [\x00-\x7F] # однобайтовые последовательности 0xxxxxxx
| [\xC0-\xDF][\x80-\xBF] # двухбайтовые последовательности 110xxxxx 10xxxxxx
| [\xE0-\xEF][\x80-\xBF]{2} # трехбайтовые последовательности 1110xxxx 10xxxxxx * 2
| [\xF0-\xF7][\x80-\xBF]{3} # четырехбайтовые последовательности 11110xxx 10xxxxxx * 3
){1,100} # ...один или более раз
)
| . # что-либо еще
/x
END;
preg_replace($regex, '$1', $text);
Этот код ищет последовательности UTF-8 и захватывает их в группу 1. Он также ищет отдельные байты, которые не могут быть идентифицированы как часть последовательности UTF-8, но не захватывает их. Заменой является то, что было захвачено в группу 1. Это эффективно удаляет все недопустимые байты. Можно восстановить строку, закодировав недопустимые байты как символы UTF-8. Но если ошибки случайны, это может привести к появлению странных символов.
$regex = <<<'END'
/
(
(?: [\x00-\x7F] # однобайтовые последовательности 0xxxxxxx
| [\xC0-\xDF][\x80-\xBF] # двухбайтовые последовательности 110xxxxx 10xxxxxx
| [\xE0-\xEF][\x80-\xBF]{2} # трехбайтовые последовательности 1110xxxx 10xxxxxx * 2
| [\xF0-\xF7][\x80-\xBF]{3} # четырехбайтовые последовательности 11110xxx 10xxxxxx * 3
){1,100} # ...один или более раз
)
| ( [\x80-\xBF] ) # недопустимый байт в диапазоне 10000000 - 10111111
| ( [\xC0-\xFF] ) # недопустимый байт в диапазоне 11000000 - 11111111
/x
END;
function utf8replacer($captures) {
if ($captures[1] != "") {
// Действительная последовательность байтов. Возвращается немодифицированной.
return $captures[1];
}
elseif ($captures[2] != "") {
// Неверный байт формы 10xxxxx.
// Закодировать как 11000010 10xxxxx.
return "\xC2".$captures[2];
}
else {
// Неверный байт формы 11xxxxx.
// Закодировать как 11000011 10xxxxx.
return "\xC3".chr(ord($captures[3])-64);
}
}
preg_replace_callback($regex, "utf8replacer", $text);
!empty(x) будет соответствовать непустым значениям ("0" считается пустым).
x != "" будет соответствовать непустым значениям, включая "0".
x !== "" будет соответствовать чему угодно, кроме "".
x != "" кажется лучшим вариантом для использования в этом случае.
Я также немного ускорил выборку. Вместо сопоставления каждого символа по отдельности он сопоставляет последовательности допустимых символов UTF-8.
Ответ 3
Эта функция удаляет все символы НЕ 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Æ?'); // Привет, как дела?
Ответ 4
Попробуйте это:
$string = iconv("UTF-8","UTF-8//IGNORE",$string);
Согласно руководству iconv, функция будет принимать первый параметр как кодировку ввода, второй параметр — как кодировку вывода, а третий — как фактическую строку ввода. Если вы установите как входную, так и выходную кодировку в UTF-8 и добавите флаг //IGNORE к выходной кодировке, функция отбросит (удалит) все символы во входной строке, которые не могут быть представлены выходной кодировкой. Таким образом, действует фильтрация входной строки.
Ответ 5
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'));
}
Ответ 6
Я сделал функцию, которая удаляет недопустимые символы UTF-8 из строки. Я использую ее для очистки описания 27 000 товаров перед созданием файла экспорта 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;
}

Web