Web

Как удалить из строки символы, отличные от UTF-8

У меня проблема с удалением из строки символов, отличных от 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;

}

 

Схожие статьи

Как узнать с помощью PHP количество дней в месяце: особенности функции
Web

Как узнать с помощью PHP количество дней в месяце: особенности функции

Web

Почему форма с enctype возвращает нулевой файл в запросе

Песочницы HTML, CSS и для JS-кода: что это и для чего нужны
Web

Песочницы HTML, CSS и для JS-кода: что это и для чего нужны

Что за профессия веб-разработчик: обязанности и зарплата
Web

Что за профессия веб-разработчик: обязанности и зарплата