Удалить не utf8 символов из строки


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

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

15 80

15 ответов:

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

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

если вы примените 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

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

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

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

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

эта функция удаляет все символы, отличные от 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?
$text = iconv("UTF-8", "UTF-8//IGNORE", $text);

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

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

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

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

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

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'));
}

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

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

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

Я сделал функцию, которая удаляет недопустимые символы 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;
}
$string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '', htmlentities($string, ENT_COMPAT, 'UTF-8'));

из последнего патча в Drupal'S Feeds JSON parser module:

//remove everything except valid letters (from any language)
$raw = preg_replace('/(?:\\u[\pL\p{Zs}])+/', '', $raw);

Если вы обеспокоены да он сохраняет пробелы в качестве допустимых символов.

сделал то, что мне нужно. Он удаляет широко распространенные в настоящее время emoji-символы, которые не вписываются в набор символов MySQL 'utf8', и это дало мне такие ошибки, как "SQLSTATE[HY000]: General error: 1366 неверное строковое значение".

Подробнее см. https://www.drupal.org/node/1824506#comment-6881382

правила таковы, что первый UTF-8 октлет имеет высокий бит, установленный в качестве маркера,а затем от 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. Однако не ясно, какой вид зачистки должен быть сделан, как только вы получите искаженный характер.

чтобы удалить все символы Юникода за пределами базовой языковой плоскости Юникода:

$str = preg_replace("/[^\x00-\xFFFF]/", "", $str);

немного отличается от вопроса, но то, что я делаю, это использовать HtmlEncode(string),

псевдокод здесь

var encoded = HtmlEncode(string);
encoded = Regex.Replace(encoded, "&#\d+?;", "");
var result = HtmlDecode(encoded);

вход и выход

"Headlight\x007E Bracket, &#123; Cafe Racer<> Style, Stainless Steel 中文呢?"
"Headlight~ Bracket, &#123; Cafe Racer<> Style, Stainless Steel 中文呢?"

Я знаю, что это не идеально, но делает работу за меня.

Как насчет iconv:

http://php.net/manual/en/function.iconv.php

не использовали его внутри самого PHP, но он всегда хорошо работал для меня в командной строке. Вы можете заставить его заменить недопустимые символы.