代码之家  ›  专栏  ›  技术社区  ›  Dan Sosedoff

从字符串中删除非utf8字符

  •  85
  • Dan Sosedoff  · 技术社区  · 15 年前

    我在从字符串中删除不正确显示的非UTF8字符时遇到问题。字符类似于0x97 0x61 0x6c 0x6f(十六进制表示)

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

    15 回复  |  直到 6 年前
        1
  •  72
  •   Markus Jarderot    11 年前

    使用regex方法:

    $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);
    

    编辑:

    • !empty(x) 将匹配非空值( "0" 视为空)。
    • x != "" 将匹配非空值,包括 “0” .
    • x !== "" 将匹配除 "" .

    X!=“” 在这种情况下似乎是最好的选择。

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

        2
  •  115
  •   Jason Aller    6 年前

    如果你申请 utf8_encode() 对于已经是utf8字符串,它将返回一个乱码的utf8输出。

    我做了一个处理所有这些问题的函数。它叫 Encoding::toUTF8() .

    您不需要知道字符串的编码是什么。它可以是latin1(iso8859-1)、windows-1252或utf8,或者字符串可以混合使用。 编码::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::fix utf8(),它将修复每一个看起来像是多次编码到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

        3
  •  52
  •   Community kfsone    7 年前

    您可以使用mbstring:

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

    …将删除无效字符。

    见: Replacing invalid UTF-8 characters by question marks, mbstring.substitute_character seems ignored

        4
  •  17
  •   John    7 年前

    此函数删除所有非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?
    
        5
  •  11
  •   Markus Hedlund    13 年前
    $text = iconv("UTF-8", "UTF-8//IGNORE", $text);
    

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

        6
  •  10
  •   Paweł Tomkiel    9 年前

    试试这个:

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

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

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

        7
  •  6
  •   masakielastic    11 年前

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

    从php 5.4开始,htmlspecialchars可以用来删除无效的字节序列。htmlspecialchars在处理大字节和精度方面优于preg_匹配。使用正则表达式可以看到很多错误的实现。

    function replace_invalid_byte_sequence3($str)
    {
        return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, 'UTF-8'));
    }
    
        8
  •  5
  •   benRollag    10 年前

    我做了一个从字符串中删除无效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;
    }
    
        9
  •  5
  •   Paweł Tomkiel    9 年前

    文本可能包含 非UTF8字符 . 先试着做:

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

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

        10
  •  3
  •   Alix Axel    15 年前
    $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'));
    
        11
  •  2
  •   Oleksii Chekulaiev    9 年前

    从最近的补丁到Drupal的feeds json解析器模块:

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

    如果您关心是,它将保留空格作为有效字符。

    做了我需要的。它删除了现在广泛使用的emoji字符,这些字符不适合MySQL的“utf8”字符集,这给了我一些错误,比如“sqlstate[hy000]:general error:1366 incorrect string value”。

    详情见 https://www.drupal.org/node/1824506#comment-6881382

        12
  •  1
  •   Will    15 年前

    所以规则是第一个 UTF-8 octlet将高位设置为标记,然后1到4位指示多个附加的octlet;然后每个附加的octlet必须将高位2位设置为10。

    伪python应该是:

    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。然而,当你得到一个畸形的角色时,它不清楚要做什么样的剥离。

        13
  •  1
  •   Sam Hanley    9 年前

    要删除Unicode基本语言平面之外的所有Unicode字符,请执行以下操作:

    $str = preg_replace("/[^\\x00-\\xFFFF]/", "", $str);
    
        14
  •  0
  •   misaxi    11 年前

    与问题略有不同,但我要做的是使用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 中文呢?"
    

    我知道这并不完美,但这是我的工作。

        15
  •  -1
  •   Ben    15 年前

    ICONV怎么样:

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

    没有在PHP内部使用过它,但在命令行上它总是为我表现良好。您可以让它替换无效字符。