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

时间:2015-07-14 02:21:27

标签: php regex utf-8 pcre

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

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

该档案有各种不同的随机格式。

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

以下代码在99%的时间内运行良好,直到找到一些非UTF 8字符的记录。

31*3235”x21”是两个例子。

当这些运行时,我会收到这些PHP错误并停止脚本....

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

演示:

<?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>';         

    }

}

围绕这个想法?我无法重建服务器软件以添加支持

刚刚找到一个PHP库的答案转换为UTF8似乎有很多帮助https://stackoverflow.com/a/3521396/143030

1 个答案:

答案 0 :(得分:2)

默认情况下,PCRE正则表达式引擎一次读取一个字节的字符串,因此,默认情况下,当使用UTF-8等多字节编码时,它会忽略可能组成单个字符的字节序列,并查看它们作为分隔的字节(一个字节,一个字符)。

例如,字符U + 201D:RIGHT DOUBLE QUOTATION MARK使用UTF-8中的三个字节:

$a = '”';

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

结果:

e2 80 9d

要在PCRE正则表达式引擎中启用多字节读取,您可以在模式的开头使用以下指令之一:(*UTF)(*UTF8)(*UTF16),{{1或者u修饰符(它打开了可用的多字节模式,但这也扩展了速记字符类的含义,如(*UTF32)\s\d。换句话说,u修饰符是\w(*UTFx)的快捷方式,用于更改字符类。)

但是这些功能仅在PCRE模块已经在这些编码的支持下编译时才可用。 (大多数默认PHP安装都是这种情况,但它不是绝对系统的或强制性的。)

似乎情况并非如此,因为当您使用u修饰符时,您会获得此明确消息:

(*UCP)

除非您决定使用支持UTF8的PCRE模块更改PHP安装,否则您无法执行任何操作。

但是,在你的情况下它并不是一个真正的问题,因为在你的模式中,即使输入是UTF8编码,u修饰符也是完全没用的。

原因是您的两个模式仅使用ASCII文字字符(00-7F范围内的字符),并且因为UTF8编码中超出ASCII范围的字符永远不会使用此范围内的字节:

this version of PCRE is not compiled with PCRE_UTF8 support

所以你可以写:

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
...

(不需要使用i修饰符,因为你的字符串已经是小写的。不需要在字符类中转义一个点并使用捕获组。添加$string = preg_replace( '/[^0-9x.]+/', '', strtolower( $string ) ); 量词可以加速替换因为在一次替换中删除了几个连续的字符,而不是一个一个。)

+

但是,某些模式可能会有危险,例如,如果第一个字符使用多个字节进行编码,但不会删除第一个字符,而只会删除该字符的第一个字节:

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>';
}

返回:

$a = preg_replace('/^./', '', '”abc');

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