首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当检测到非preg_replace字符时,PHP UTF8 ()失败

问当检测到非preg_replace字符时,PHP UTF8 ()失败
EN

Stack Overflow用户
提问于 2015-07-14 02:21:27
回答 1查看 1.3K关注 0票数 2

当找到非UTF 8字符时PHP正则表达式失败!

我需要剥离40,000条数据库记录,以便从custom_size mysql表字段中获取宽度和高度值。

该字段采用各种不同的随机格式。

最可靠的方法是从x的左侧和右侧获取一个数值,并从它们中删除所有非数字值。

下面的代码在99%的时间里运行得很好,直到找到了一些非UTF 8字符的记录。

31*32和35”x21”是两个例子。

当这些被运行时,我得到这些PHP错误和脚本停止..。

代码语言:javascript
复制
Warning: preg_replace(): Compilation failed: this version of PCRE is not compiled with PCRE_UTF8 support at offset 1683977065 on line 21

Warning: preg_match(): Compilation failed: this version of PCRE is not compiled with PCRE_UTF8 support at offset 0 on line 24

演示:

代码语言:javascript
复制
<?php

$strings = array(

    '12x12',
    '172.61 cm x 28.46 cm',
    '31"x21"',
    '1"x1"',
    '31*32',
    '35”x21”'
);


foreach($strings as $string){

    if($string != ''){

        $string = str_replace('”','"',$string);

        // Strip out all characters except for numbers, letter x, and decimal points
        $string = preg_replace( '/([^0-9x\.])/ui', '', strtolower( $string ) );

        // Find anything that fits the number X number format
        preg_match( '/([0-9]+(\.[0-9]+)?)x([0-9]+(\.[0-9]+)?)/ui', $string, $values ); 

        echo 'Original value: ' .$string.'<br>';
        echo 'Width: ' .$values[1].'<br>';
        echo 'Height: ' .$values[3].'<br><hr><br>';         

    }

}

对此有什么想法吗?我不能重新生成服务器软件来添加支持。

刚刚找到了一个可以转换为UTF8的PHP库,这似乎对https://stackoverflow.com/a/3521396/143030有很大帮助。

EN

回答 1

Stack Overflow用户

发布于 2016-05-20 01:09:22

默认情况下,PCRE regex引擎一次读取一个字符串,因此默认情况下,它忽略了在使用UTF-8这样的多字节编码时可能组成单个字符的字节序列,并将它们视为分隔字节(一个字节,一个字符)。

例如,字符U+201D: RIGHT双引号在UTF-8中使用三个字节:

代码语言:javascript
复制
$a = '”';

for ($i=0; $i < strlen($a); $i++) {
    echo dechex(ord($a[$i])), ' ';
}

结果:

代码语言:javascript
复制
e2 80 9d

要启用PCRE regex引擎中的多字节读取,可以在模式的开头使用以下指令之一:(*UTF)、(*UTF8)、(*UTF16)、(*UTF32)或u修饰符(打开可用的多字节模式,但这也扩展了\s__、\d__、\w__等简写字符类的含义.敬unicode。换句话说,u修饰符是更改字符类的(*UTFx)和(*UCP)的快捷方式。)

但是,只有在这些编码支持下编译了PCRE模块时,这些特性才可用。(大多数默认PHP安装都是这样,但并不是绝对系统的或强制性的。)

似乎情况并非如此,因为当您使用u修饰符时,您将获得以下显式消息:

代码语言:javascript
复制
this version of PCRE is not compiled with PCRE_UTF8 support

除了您决定用UTF8支持编译的PCRE模块更改PHP安装之外,您不能对此进行任何操作。

但是,在您的情况下,这并不是真正的问题,因为在您的模式中,u修饰符是完全无用的,即使您的输入是UTF8编码的。

原因是您的两种模式只使用ASCII文字字符(在00-7F范围内的字符),而且因为UTF8编码中超出ASCII范围的字符从不使用此范围内的字节:

代码语言:javascript
复制
Unicode  char   UTF8    Name
--------------------------------------------------------
U+007D     }       7d   RIGHT CURLY BRACKET
U+007E     ~       7e   TILDE
U+007F             7f   <control>
U+0080          c2 80   <control>
U+0081          c2 81   <control>
...
U+00BE     ¾    c2 be   VULGAR FRACTION THREE QUARTERS
U+00BF     ¿    c2 bf   INVERTED QUESTION MARK
U+00C0     À    c3 80   LATIN CAPITAL LETTER A WITH GRAVE
U+00C1     Á    c3 81   LATIN CAPITAL LETTER A WITH ACUTE
...

所以你可以写:

代码语言:javascript
复制
$string = preg_replace( '/[^0-9x.]+/', '', strtolower( $string ) );

(不需要使用i修饰符,因为字符串已经是小写的了。不需要转义字符类中的点并使用捕获组。添加+量词可以加快替换速度,因为在一个替换中删除了几个连续字符,而不是一个接一个地删除)。

以及:

代码语言:javascript
复制
if (preg_match('/([0-9]+(?:\.[0-9]+)?)x([0-9]+(?:\.[0-9]+)?)/', $string, $values)) {
    echo 'Original value: ', $string, '<br>';
    echo 'Width: ', $values[1], '<br>';
    echo 'Height: ', $values[2], '<br><hr><br>';
}

但是,对于某些模式来说,这可能是危险的,例如,如果这个字符用几个字节编码,那么它将不会像预期的那样删除第一个字符,而只删除该字符的第一个字节:

代码语言:javascript
复制
$a = preg_replace('/^./', '', '”abc');

for ($i=0; $i < strlen($a); $i++) {
    echo ' ', dechex(ord($a[$i]));
}

返回:

代码语言:javascript
复制
 80 9d 61 62 63
# �  �  a  b  c
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31396590

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档