【问题标题】:PHP: case-insensitive preg_replace of a cyrillic string in UTF8PHP:UTF8 中西里尔字符串的不区分大小写 preg_replace
【发布时间】:2011-07-24 10:10:27
【问题描述】:

我有a PHP 5.3 script 显示我网站的用户,并想用它的旧名称替换某个俄罗斯城市(在 PostgreSQL 8.4.7 数据库 + CentOS 5.5/64 位 Linux 中以 UTF8 存储)(它是内部人员)笑话):

preg_replace('/Волгоград/iu', 'Сталинград', $city);

不幸的是,这仅适用于完全匹配:Волгоград

这不适用于其他情况,例如 ВОЛГОГРАДволгоград

如果我将源代码修改为

preg_replace('/[Вв]олгоград/iu', 'Сталинград', $city);

那么它将捕获上面的第二种情况。

有人知道它发生了什么以及如何解决它(假设我不想为每个字母写[Xx])?

谢谢! 亚历克斯

更新:

# rpm -qa|grep php
php53-bcmath-5.3.3-1.el5
php53-gd-5.3.3-1.el5
php53-common-5.3.3-1.el5
php53-pdo-5.3.3-1.el5
php53-mbstring-5.3.3-1.el5
php53-xml-5.3.3-1.el5
php53-5.3.3-1.el5
php53-cli-5.3.3-1.el5
php53-pgsql-5.3.3-1.el5

# rpm -qa|grep pcre
pcre-6.6-2.el5_1.7

【问题讨论】:

    标签: php utf-8 internationalization preg-replace


    【解决方案1】:

    对于那些支持庞大的遗留代码库、在字符集和编码问题上苦苦挣扎并且没有转换代码字符集选项的人 - 这是一个答案:

    //for 
    setlocale(LC_ALL, 'ru_RU.cp1251');  
    //(or any other locale) to take effect, 
    //you MUST generate system locale, i.e.
    
    sudo su
    #view supported locales
    #less /usr/share/i18n/SUPPORTED
    echo "ru_RU.cp1251 CP1251" >> /var/lib/locales/supported.d/local
    dpkg-reconfigure locales
    exit
    
    #and (for ubuntu/debian)
    
    apt-get install php5-intl
    

    虽然您可以重写您的正则表达式以使用一些 utf 技巧,将您的代码转换为 utf,但当您使用庞大的代码库/数据库等时,这不是一个选择

    【讨论】:

      【解决方案2】:

      这个解决了问题:

      setlocale(LC_ALL, 'ru_RU.CP1251', 'rus_RUS.CP1251', 'Russian_Russia.1251');
      

      【讨论】:

        【解决方案3】:

        实际上,在 Windows 上使用 PHP 5.2.x 时选择的已解决答案对我不起作用。

        我必须通过转换到 Windows-1251 才能使其工作。

        例子如下:

        $new_content = preg_replace(iconv('UTF-8', 'Windows-1251', "/\bгъз\b/i"), iconv('UTF-8', 'Windows-1251', "YYYYYY"), iconv('UTF-8', 'Windows-1251', "ти си gyz gyz гъз ГЪЗ gyzgyz гЪз gyz"));
        $new_content = iconv('Windows-1251', 'UTF-8', $new_content);
        

        上面的示例将成功(不区分大小写)'гъз' 替换为 YYYYYY,并返回 UTF-8 版本。

        问候!

        【讨论】:

        • 注意:我有一个@setlocale(LC_ALL, "bg_BG.UTF-8");
        【解决方案4】:

        您可以跳过正则表达式,它在 PHP 5.2.11 中对我有用 :)

        $city = 'Unfortunately this only works for exact matches: Волгоград.
        
        This does not work for other cases, like ВОЛГОГРАД or волгоград.';
        
        echo str_ireplace('Волгоград', '[found]', $city);
        

        输出

        "Unfortunately this only works for exact matches: [found].
        
        This does not work for other cases, like [found] or [found]."
        

        这引起了我的兴趣,所以I asked a question

        【讨论】:

        • 不幸的是,str_ireplace('Волгоград', 'Сталинград', $city);有同样的问题。
        • @Alexander 那是什么问题?干杯。
        【解决方案5】:

        也许试试:mb_eregi_replace http://www.php.net/manual/en/function.mb-eregi-replace.php

        mb_eregi_replace — 用忽略大小写的多字节支持替换正则表达式

        【讨论】:

          【解决方案6】:

          只是猜测,但将字符串显式编码为 un​​icode 可能会有所帮助:

          preg_replace('/Волгоград/iu', utf8_encode('Сталинград'), $city);
          

          【讨论】:

          • 不 - 根本没有替换发生
          【解决方案7】:

          在我的盒子上就像一个魅力......

          <?php
              $city = 'Волгоград';
              var_dump(preg_match('/волгоград/ui', $city));
              var_dump(preg_match('/ВОЛГОГРАД/ui', $city));
              var_dump(preg_replace('/волгоград/ui', 'Сталинград', $city));
              var_dump(preg_replace('/ВОЛГОГРАД/ui', 'Сталинград', $city));
          

          输出:

          int 1
          int 1
          string 'Сталинград' (length=20)
          string 'Сталинград' (length=20)
          

          您确定输入数据 ($city) 是 UTF8 格式吗?

          【讨论】:

            【解决方案8】:

            我复制+粘贴了你的大号В。确实是U+D092,而不是普通的拉丁语B。但是因为它们看起来非常相似:ВB 我相信俄罗斯字母被整理到U+0042 的拉丁文 B 上。

            所以要么是 PHP 对其进行了预格式化,要么 PCRE 在那里也有些不精确。测试您的 print PCRE_VERSION; 并查看更改日志。

            无论如何,为了避免这个问题,我建议你只使用小写字母。它们更有可能与拉丁字母不同。

            preg_replace('/волгоград/iu', 'Сталинград', $city);
            

            P.S.:恶作剧!

            【讨论】:

            • 我希望我知道里面的笑话:)
            • 我的网站嵌入了古老的俄罗斯纸牌游戏“Preferans”,当出价最高的玩家宣布“6 黑桃”时(即以黑桃为王牌拿 6 墩;可能是最低的比赛中出价),则其他 2 名玩家在防守时不允许向对方出示他们的牌。所以他们在“最后一战”(就像苏军在保卫斯大林格勒市对抗德国人时一样)。
            • 我很确定我的数据库中有俄语“В”,而不是拉丁语“B”。替换确实适用于俄语“Волгоград”
            • @Alexander:可能是旧的 PCRE 库。 6.6 已经 5 岁了,即使是 7.x 版本也有一些类似的错误。 pcre.org/changelog.txt - 尝试更新,libpcre 独立于 PHP。我想这就是你原来问题的根源。 (并不是说数据库包含错误的字符。)
            【解决方案9】:

            我无法使用 PHP 5.3.3 (PHP 5.3.3-1ubuntu9.3 with Suhosin-Patch (cli)) 重现您的问题:

            $str1 = 'Волгоград';
            $str2 = 'ВОЛГОГРАД';
            $str3 = 'волгоград';
            
            var_dump(preg_replace('/Волгоград/iu', 'Сталинград', $str1));
            var_dump(preg_replace('/Волгоград/iu', 'Сталинград', $str2));
            var_dump(preg_replace('/Волгоград/iu', 'Сталинград', $str3));
            

            输出

            string(20) "Сталинград"
            string(20) "Сталинград"
            string(20) "Сталинград"
            

            您的 PHP 使用的是哪个 PCRE 版本?检查phpinfo()pcre 部分。这是我系统上的那个:

            ...
            pcre
            
            PCRE (Perl Compatible Regular Expressions) Support => enabled
            PCRE Library Version => 8.02 2010-03-19
            ...
            

            【讨论】:

            • “PCRE 库版本 6.6 06-Feb-2006”,CentOS 5.5/64 位,带有:php53-bcmath-5.3.3-1.el5 php53-gd-5.3.3-1.el5 php53 -common-5.3.3-1.el5 php53-pdo-5.3.3-1.el5 php53-mbstring-5.3.3-1.el5 php53-xml-5.3.3-1.el5 php53-5.3.3-1 .el5 php53-cli-5.3.3-1.el5 php53-pgsql-5.3.3-1.el5
            • 可能是这个问题……PCRE版本好像有点老了。
            • 好吧,这就是 CentOS 的常见问题
            猜你喜欢
            • 2011-12-06
            • 1970-01-01
            • 2021-11-16
            • 2017-04-19
            • 2019-10-21
            • 2022-11-22
            • 2016-01-26
            • 1970-01-01
            • 2013-10-19
            相关资源
            最近更新 更多