【问题标题】:PHP - Fast way to strip all characters not displayable in browser from utf8 stringPHP - 从 utf8 字符串中删除浏览器中不可显示的所有字符的快速方法
【发布时间】:2011-11-29 21:36:47
【问题描述】:

我有一个有点杂乱的数据库,其中包含世界各地许多机构的名称。

我想显示它们,包括国家字符,但不包括无效字符 - 那些在 Firefox 中显示为 unicode 数字的字符。

如何过滤掉它们?

数据库有 utf8 编码,但插入的一些字符串编码错误,或者源中已经一团糟。

我不想修复数据库 - 它太大了。我只想把它过滤掉——“眼不见心不烦”

【问题讨论】:

    标签: php regex unicode utf-8 character-encoding


    【解决方案1】:

    我只想过滤掉它

    您的数据有一个未指定的编码/字符集。这是个大问题。

    您可以先尝试将其转换为utf-8,然后去除所有不可打印的字符:

    $str = iconv('utf-8', 'utf-8//ignore', $str);
    
    echo preg_replace('/[^\pL\pN\pP\pS\pZ]/u', '', $str);
    

    问题是,iconv 函数只能尝试。它将删除任何无效的字符序列。但是,从 php 5.4 开始,如果指定的输入编码无效,它将删除完整的字符串。

    自 PHP 5.3 起,您将看到输入字符串的编码无效的警告。

    您可以通过首先删除所有无效的utf-8 字节序列来解决此问题:

    $str = valid_utf8_bytes($str);
    
    echo preg_replace('/[^\pL\pN\pP\pS\pZ]/u', '', $str);
    
    /**
     * get valid utf-8 byte squences
     *
     * take over all matching bytes, drop an invalid sequence until first
     * non-matching byte.
     * 
     * @param string $str
     * @return string
     */
    function valid_utf8_bytes($str)
    {
        $return = '';
        $length = strlen($str);
        $invalid = array_flip(array("\xEF\xBF\xBF" /* U-FFFF */, "\xEF\xBF\xBE" /* U-FFFE */));
    
        for ($i=0; $i < $length; $i++)
        {
            $c = ord($str[$o=$i]);
    
            if ($c < 0x80) $n=0; # 0bbbbbbb
            elseif (($c & 0xE0) === 0xC0) $n=1; # 110bbbbb
            elseif (($c & 0xF0) === 0xE0) $n=2; # 1110bbbb
            elseif (($c & 0xF8) === 0xF0) $n=3; # 11110bbb
            elseif (($c & 0xFC) === 0xF8) $n=4; # 111110bb
            else continue; # Does not match
    
            for ($j=++$n; --$j;) # n bytes matching 10bbbbbb follow ?
                if ((++$i === $length) || ((ord($str[$i]) & 0xC0) != 0x80))
                    continue 2
            ;
    
            $match = substr($str, $o, $n);
    
            if ($n === 3 && isset($invalid[$match])) # test invalid sequences
                continue;
    
            $return .= $match;
        }
        return $return;
    }
    

    【讨论】:

    • 谢谢,我会尝试该功能并报告。
    • 它只是部分工作 - valid_utf8_bytes 不会改变任何东西,但 preg_replace('/[^\pL\pN\pP\pS\pZ]/u', '', $str) 没有按预期工作:马里奥·内格里学院 > 马里奥·内格里学院(好的),但是:哥廷根大学 > 哥廷根大学(不行 - 一些区域字符被删除)
    • 我假设 Universität 中的 ä 不止一个代码点,a 加上顶部的两个点。您可能也希望允许这些,请参阅Unicode character properties PCRE PHP 了解正则表达式中使用(和可能使用)的内容,但很难判断没有原始字符串。
    • 我已经添加了\pM,现在可以了。 $text = preg_replace('/[^\pL\pN\pP\pS\pZ\pM]/u', ' ', $text);.谢谢!
    • 酷,感谢您的反馈。保持字母顺序,这样更容易维护;)
    【解决方案2】:

    数据库可能不完全是问题 - 如果表是 utf8 编码的,其中的字符串应该已经转换(我认为)。我遇到的问题是正确确保编码是一致的。例如,默认情况下,mysqli 连接器恢复为 Latin-8859 IIRC,因此很有可能在 utf8 中输出,在 utf8 中的数据库仍然以 ?字符,因为它们被 mysqli 连接器转换为拉丁文。

    要确保全面使用 utf8,您需要执行以下操作:

    在数据库中:

    确保排序规则类似于utf8_general_ci

    在 PHP 视图文件的顶部:

    <?php header('Content-Type:Text/Plain;charset=utf-8'); ?>
    

    在 HTML 元标记中(可选):

    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    

    数据库连接器中的 AND(以 MySQLi 为例):

    mysqli::set_charset('utf8'); #note that for MySQL it isn't hyphenated
    

    您可能会发现无论如何都能解决问题。

    【讨论】:

    • 很公平——至少这已经排除了可能的原因;)我认为 hakre 的上述解决方案目前为您提供了最好的选择;这有点老套,但我真的想不出在 PHP 中这样做的优雅方式 - MB 字符串库中有 可能 的东西吗? php.net/manual/en/book.mbstring.php
    【解决方案3】:

    如果数据库是您的问题(并且修复它不碍事),那么也许只需使用 ORD 从字符串中打印出每个字符,并找到不是的控制字符的值发送得很好。

    然后,当您知道控制字符的值时,将这些值传递给搜索该控制字符的函数,并尝试使用相应的 UTF8 字符实时更改 utf-8 编码(有缺陷的编码)。

    【讨论】:

    • 数据库是从许多来源构建的,其中一些已经包含编码错误的项目(每次都不同)。修复它将是一项艰巨的任务。我需要找到一些简单的解决方法。
    猜你喜欢
    • 1970-01-01
    • 2013-03-04
    • 1970-01-01
    • 2016-03-03
    • 2015-11-27
    • 2011-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多