当找到非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>';
}
}
围绕这个想法?我无法重建服务器软件以添加支持
刚刚找到一个PHP库的答案转换为UTF8似乎有很多帮助https://stackoverflow.com/a/3521396/143030
答案 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]));
}