从字符串中删除非utf8字符

Dan*_*off 102 php regex

我在从字符串中删除非utf8字符时遇到问题,这些字符无法正常显示.字符是这样的0x97 0x61 0x6C 0x6F(十六进制表示)

删除它们的最佳方法是什么?正则表达式还是其他什么?

Seb*_*oli 126

如果您应用于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);
Run Code Online (Sandbox Code Playgroud)

我已经包含了另一个函数Encoding :: fixUTF8(),它将修复每个UTF8字符串,该字符串看起来多次被编码为UTF8的乱码产品.

用法:

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

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
Run Code Online (Sandbox Code Playgroud)

例子:

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");
Run Code Online (Sandbox Code Playgroud)

将输出:

Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Run Code Online (Sandbox Code Playgroud)

下载:

https://github.com/neitanod/forceutf8

  • 杰出的东西!所有其他解决方案都会丢弃无效字符,但是这个字符会修复它.真棒. (11认同)
  • 你做了很棒的功能!我过去使用XML Feeds工作很多,并且总是遇到编码问题.谢谢. (3认同)
  • 我爱你.你在糟糕的UTF8字符上为我节省了"bloomoin"的工作时间.谢谢. (3认同)
  • 这是太棒了.谢谢 (3认同)
  • 伟大的工作人.感谢您抽出宝贵时间来写这篇文章. (2认同)

Mar*_*rot 80

使用正则表达式方法:

$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);
Run Code Online (Sandbox Code Playgroud)

它搜索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);
Run Code Online (Sandbox Code Playgroud)

编辑:

  • !empty(x)将匹配非空值("0"被视为空).
  • x != ""将匹配非空值,包括"0".
  • x !== ""将匹配任何除外"".

x != "" 在这种情况下,似乎是最好用的.

我也加快了比赛的速度.它不是分别匹配每个字符,而是匹配有效UTF-8字符的序列.


Fro*_*y Z 60

你可以使用mbstring:

$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
Run Code Online (Sandbox Code Playgroud)

...将删除无效字符.

请参阅:用问号替换无效的UTF-8字符,mbstring.substitute_character似乎被忽略

  • 在调用 mb Convert 之前,我必须将 mbstring 替换字符设置为 none `ini_set('mbstring.substitute_character', 'none');` 否则我会在结果中得到问号。 (4认同)
  • @Alliswell 如果我没有记错的话,`�` 虽然不是可打印字符,但却是一个完全有效的 UTF-8 序列。您可能对不可打印的字符有疑问?检查这个:/sf/ask/82383311/ (2认同)

Dav*_*d D 20

此函数删除所有非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;
}
Run Code Online (Sandbox Code Playgroud)

这个怎么运作:

echo remove_bs('Hello õhowå åare youÆ?'); // Hello how are you?
Run Code Online (Sandbox Code Playgroud)

  • 为什么全包功能名称?EWWW. (8认同)
  • 它是ASCII,甚至不接近问题所需. (5认同)

Zna*_*kus 13

$text = iconv("UTF-8", "UTF-8//IGNORE", $text);
Run Code Online (Sandbox Code Playgroud)

这就是我正在使用的.似乎工作得很好.摘自http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/

  • 对不起,经过一些更多的测试,我意识到这并没有真正做到我的想法.我现在正在使用http://stackoverflow.com/a/8215387/138023 (3认同)

tec*_*rya 13

试试这个:

$string = iconv("UTF-8","UTF-8//IGNORE",$string);
Run Code Online (Sandbox Code Playgroud)

根据iconv手册,该函数将第一个参数作为输入字符集,第二个参数作为输出字符集,第三个参数作为实际输入字符串.

如果将输入和输出字符集都设置为UTF-8,并将//IGNORE标志附加到输出字符集,则该函数将删除(去除)输出字符串中无法由输出字符集表示的所有字符.因此,过滤输入字符串有效.

  • 我试过这个,而且`// IGNORE`似乎并没有抑制无效UTF-8存在的注意事项(当然,我知道这一点,并希望修复).一个评价很高的评论[在手册中](https://php.net/manual/en/function.iconv.php)似乎认为它已经成为一个多年的bug. (3认同)

小智 12

您好,您可以使用简单的正则表达式

$text = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $text);
Run Code Online (Sandbox Code Playgroud)

它会截断字符串中的所有非 UTF-8 字符


小智 8

文本可能包含非utf8字符.先尝试做:

$nonutf8 = mb_convert_encoding($nonutf8 , 'UTF-8', 'UTF-8');
Run Code Online (Sandbox Code Playgroud)

你可以在这里阅读更多相关信息:http://php.net/manual/en/function.mb-convert-encoding.php news


mas*_*tic 6

从PHP 5.5开始就可以使用UConverter.如果使用intl扩展并且不使用mbstring,则UConverter是更好的选择.

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);
}
Run Code Online (Sandbox Code Playgroud)

从PHP 5.4开始,htmlspecialchars可用于删除无效的字节序列.Htmlspecialchars比preg_match更好地处理大字节和精度.可以看到使用正则表达式的许多错误实现.

function replace_invalid_byte_sequence3($str)
{
    return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, 'UTF-8'));
}
Run Code Online (Sandbox Code Playgroud)


小智 6

我已经创建了一个从字符串中删除无效UTF-8字符的函数.在生成XML导出文件之前,我用它来清除27000个产品的描述.

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;
}
Run Code Online (Sandbox Code Playgroud)


Ali*_*xel 5

$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'));
Run Code Online (Sandbox Code Playgroud)


cla*_*rkk 5

欢迎来到 2019 和/u正则表达式中的修饰符,它将为您处理 UTF-8 多字节字符

如果您只使用mb_convert_encoding($value, 'UTF-8', 'UTF-8'),您的字符串中仍然会出现不可打印的字符

该方法将:

  • 删除所有无效的 UTF-8 多字节字符 mb_convert_encoding
  • 删除所有不可打印的字符,如\r, \x00(NULL-byte) 和其他控制字符preg_replace

方法:

function utf8_filter(string $value): string{
    return preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($value, 'UTF-8', 'UTF-8'));
}
Run Code Online (Sandbox Code Playgroud)

[:print:]匹配所有可打印的字符和\n换行符并去除其他所有内容

您可以看到下面的 ASCII 表.. 可打印字符的范围从 32 到 127,但换行符\n是控制字符的一部分,范围从 0 到 31,因此我们必须向正则表达式添加换行符/[^[:print:]\n]/u

https://cdn.shopify.com/s/files/1/1014/5789/files/Standard-ASCII-Table_large.jpg?10669400161723642407

您可以尝试通过正则表达式发送带有超出可打印范围的字符的字符串,例如\x7F(DEL)、\x1B(Esc) 等,并查看它们是如何被剥离的

function utf8_filter(string $value): string{
    return preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($value, 'UTF-8', 'UTF-8'));
}

$arr = [
    'Danish chars'          => 'Hello from Denmark with æøå',
    'Non-printable chars'   => "\x7FHello with invalid chars\r \x00"
];

foreach($arr as $k => $v){
    echo "$k:\n---------\n";
    
    $len = strlen($v);
    echo "$v\n(".$len.")\n";
    
    $strip = utf8_decode(utf8_filter(utf8_encode($v)));
    $strip_len = strlen($strip);
    echo $strip."\n(".$strip_len.")\n\n";
    
    echo "Chars removed: ".($len - $strip_len)."\n\n\n";
}
Run Code Online (Sandbox Code Playgroud)

https://www.tehplayground.com/q5sJ3FOddhv1atpR


Fra*_*rte 5

substr() 可能会破坏多字节字符!

就我而言,我用来substr($string, 0, 255)确保用户提供的值适合数据库。有时,它会将多字节字符分成两半,并导致数据库错误“字符串值不正确”。

您可以使用mb_substr($string,0,255), 对于 MySQL 5 来说可能没问题,但 MySQL 4 计算的是字节而不是字符,因此根据多字节字符的数量,它仍然会太长。

为了防止这些问题,我实施了以下步骤:

  1. 我增加了字段的大小(在本例中,它是更改日志,因此无法防止更长的输入。)
  2. 我还是做了一个,mb_substring以防它仍然太长
  3. 我使用@Markus Jarderot 上面接受的答案来确保是否有一个非常长的条目,并且在长度限制处包含多字节字符,我们可以在末尾删除多字节字符的一半。