当检测到非UTF8字符时,PHP preg_replace()失败
找到非UTF 8字符时PHP正则表达式失败 我需要剥离40000条数据库记录,从当检测到非UTF8字符时,PHP preg_replace()失败,php,regex,utf-8,pcre,Php,Regex,Utf 8,Pcre,找到非UTF 8字符时PHP正则表达式失败 我需要剥离40000条数据库记录,从custom\u sizemysql表字段中获取宽度和高度值 该字段有各种不同的随机格式 最可靠的方法是从x的左右两侧获取一个数值,并从中删除所有非数值 下面的代码在99%的时间内运行良好,直到找到一些非UTF 8字符的记录 31*32和35“x21”是两个示例 当这些都运行时,我得到这些PHP错误和脚本暂停 Warning: preg_replace(): Compilation failed: this vers
custom\u 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>';
}
}
默认情况下,PCRE正则表达式引擎每次读取一个字节的字符串,因此,默认情况下,当使用UTF-8等多字节编码时,它忽略可能构成单个字符的字节序列,并将它们视为分离的字节(一个字节,一个字符)
例如,字符U+201D:RIGHT双引号在UTF-8中使用了三个字节:
$a = '”';
for ($i=0; $i < strlen($a); $i++) {
echo dechex(ord($a[$i])), ' ';
}
要在PCRE正则表达式引擎中启用多字节读取,可以在模式开头使用以下指令之一:(*UTF)
,(*UTF8)
,(*UTF16)
,(*UTF32)
,或u修饰符(这将打开可用的多字节模式,但也扩展了速记字符类的含义,如\s
,\d
,\w
…到unicode。换句话说,u修饰符是更改字符类的(*UTFx)
和(*UCP)
的快捷方式。)
但是这些特性只有在PCRE模块是在这些编码的支持下编译的情况下才可用(大多数默认PHP安装都是这种情况,但它不是绝对系统的或强制性的)
似乎情况并非如此,因为当您使用u修饰符时,您会获得以下明确信息:
此版本的PCRE未使用PCRE\u UTF8支持编译
除非您决定使用UTF8支持编译的PCRE模块将PHP安装更改为1,否则您无法对此进行任何操作
然而,在您的情况下这并不是一个真正的问题,因为在您的模式中,u修饰符是完全无用的,即使您的输入是UTF8编码的
原因是您的两种模式仅使用ASCII文字字符(00-7F范围内的字符),并且UTF8编码中超出ASCII范围的字符从不使用此范围内的字节:
Unicode字符UTF8名称
--------------------------------------------------------
U+007D}7d右花括号
U+007E~7e瓷砖
U+007F 7f
U+0080 c2 80
U+0081 c2 81
...
U+00BE¾c2为普通分数四分之三
U+00BF?c2 bf倒问号
U+00C0Àc3 80拉丁文大写字母A随以坟墓
U+00C1Ác3 81带锐音符的拉丁文大写字母A
...
所以你可以写:
$string = preg_replace( '/[^0-9x.]+/', '', strtolower( $string ) );
(无需使用i修饰符,因为您的字符串已经是小写的。无需转义字符类中的点并使用捕获组。添加+
量词可以加快替换速度,因为在一次替换中会删除多个连续字符,而不是一个接一个。)
以及:
如果您的输入不是utf-8,为什么要使用u
标志?模式似乎也不需要它。@Jonny5:如果输入是Unicode文本,u
标志是必须的,因为它会影响模式的解释方式。相关:顺便说一句,如果您发现其他问题解决了您的问题,您可以作为dupl关闭您的问题复制或将其作为答案发布,而不是将解决方案编辑到问题中。@nhahtdh他只匹配ascii字符0-9
,x
和文本
没有区别。对于其他情况,我同意你的看法。此外,他使用的strtolower
函数不是为utf-8输入>点设计的g输入不是多字节的,否则将使用mb\u strtolower
。
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 c