Удалить не-utf8 символы из строки - PullRequest
96 голосов
/ 09 сентября 2009

У меня проблема с удалением не-utf8 символов из строки, которые отображаются неправильно. Символы похожи на это 0x97 0x61 0x6C 0x6F (шестнадцатеричное представление)

Как лучше всего их удалить? Регулярное выражение или что-то еще?

Ответы [ 15 ]

119 голосов
/ 19 августа 2010

Если вы примените utf8_encode() к уже строке UTF8, она вернет искаженный вывод UTF8.

Я сделал функцию, которая решает все эти проблемы. Называется Encoding::toUTF8().

Вам не нужно знать, какова кодировка ваших строк. Это может быть Latin1 (ISO8859-1), Windows-1252 или UTF8, или в строке может быть их сочетание. Encoding::toUTF8() преобразует все в UTF8.

Я сделал это, потому что служба давала мне все данные, перепутанные, смешивая эти кодировки в одной строке.

Использование:

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::toUTF8($mixed_string);

$latin1_string = Encoding::toLatin1($mixed_string);

Я включил еще одну функцию, Encoding :: fixUTF8 (), которая будет фиксировать каждую строку UTF8, которая выглядит искаженным продуктом, будучи зашифрованной в UTF8 несколько раз.

Использование:

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's 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

76 голосов
/ 09 сентября 2009

Использование подхода регулярных выражений:

$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.

57 голосов
/ 21 ноября 2011

Вы можете использовать mbstring:

$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');

... удалит недопустимые символы.

См .: Замена недопустимых символов UTF-8 на вопросительные знаки, mbstring.substitute_character, похоже, игнорируется

18 голосов
/ 20 ноября 2013

Эта функция удаляет все символы 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?
12 голосов
/ 17 декабря 2014

попробуйте это:

$string = iconv("UTF-8","UTF-8//IGNORE",$string);

Согласно руководству iconv , функция будет принимать первый параметр в качестве входной кодировки, второй параметр в качестве выходной кодировки и третий в качестве фактической входной строки.

Если вы установите для входной и выходной кодировки значение UTF-8 и добавите флаг //IGNORE к выходной кодировке, функция удалит (обрезает) все символы во входной строке, которые могут не будет представлен выходной кодировкой. Таким образом, фильтрация входной строки действует.

12 голосов
/ 25 января 2012
$text = iconv("UTF-8", "UTF-8//IGNORE", $text);

Это то, что я использую. Кажется, работает довольно хорошо. Взято из http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/

6 голосов
/ 11 марта 2015

Текст может содержать не-utf8 символ . Попробуйте сделать сначала:

$nonutf8 = mb_convert_encoding($nonutf8 , 'UTF-8', 'UTF-8');

Подробнее об этом можно прочитать здесь: http://php.net/manual/en/function.mb-convert-encoding.php новости

6 голосов
/ 17 июля 2014

Я сделал функцию, которая удаляет недопустимые символы 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;
}
6 голосов
/ 03 июня 2013

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'));
}
3 голосов
/ 10 сентября 2009
$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'));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...