当找到非UTF 8字符时PHP正则表达式失败!
我需要剥离40,000条数据库记录,以便从custom_size mysql表字段中获取宽度和高度值。
该字段采用各种不同的随机格式。
最可靠的方法是从x的左侧和右侧获取一个数值,并从它们中删除所有非数字值。
下面的代码在99%的时间里运行得很好,直到找到了一些非UTF 8字符的记录。
31*32和35”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>';
}
}对此有什么想法吗?我不能重新生成服务器软件来添加支持。
刚刚找到了一个可以转换为UTF8的PHP库,这似乎对https://stackoverflow.com/a/3521396/143030有很大帮助。
发布于 2016-05-20 01:09:22
默认情况下,PCRE regex引擎一次读取一个字符串,因此默认情况下,它忽略了在使用UTF-8这样的多字节编码时可能组成单个字符的字节序列,并将它们视为分隔字节(一个字节,一个字符)。
例如,字符U+201D: RIGHT双引号在UTF-8中使用三个字节:
$a = '”';
for ($i=0; $i < strlen($a); $i++) {
echo dechex(ord($a[$i])), ' ';
}结果:
e2 80 9d要启用PCRE regex引擎中的多字节读取,可以在模式的开头使用以下指令之一:(*UTF)、(*UTF8)、(*UTF16)、(*UTF32)或u修饰符(打开可用的多字节模式,但这也扩展了\s__、\d__、\w__等简写字符类的含义.敬unicode。换句话说,u修饰符是更改字符类的(*UTFx)和(*UCP)的快捷方式。)
但是,只有在这些编码支持下编译了PCRE模块时,这些特性才可用。(大多数默认PHP安装都是这样,但并不是绝对系统的或强制性的。)
似乎情况并非如此,因为当您使用u修饰符时,您将获得以下显式消息:
this version of PCRE is not compiled with PCRE_UTF8 support除了您决定用UTF8支持编译的PCRE模块更改PHP安装之外,您不能对此进行任何操作。
但是,在您的情况下,这并不是真正的问题,因为在您的模式中,u修饰符是完全无用的,即使您的输入是UTF8编码的。
原因是您的两种模式只使用ASCII文字字符(在00-7F范围内的字符),而且因为UTF8编码中超出ASCII范围的字符从不使用此范围内的字节:
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
...所以你可以写:
$string = preg_replace( '/[^0-9x.]+/', '', strtolower( $string ) );(不需要使用i修饰符,因为字符串已经是小写的了。不需要转义字符类中的点并使用捕获组。添加+量词可以加快替换速度,因为在一个替换中删除了几个连续字符,而不是一个接一个地删除)。
以及:
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]));
}返回:
80 9d 61 62 63
# � � a b chttps://stackoverflow.com/questions/31396590
复制相似问题