用PHP进行正则表达式字符类减法

时间:2011-01-24 15:56:37

标签: php regex

HI,

我正在尝试使用http://interim.cabinetoffice.gov.uk/media/291370/bs7666-v2-0-xsd-PostCodeType.htm

中的模式匹配英国邮政编码
/^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][A-Z-[CIKMOV]]{2}$/

我在PHP中使用它,但它与有效的邮政编码OL13 0EF不匹配。但是,当我删除-[CIKMOV]字符类减法时,此邮政编码确实匹配。

我得到的印象是我在PHP中做了字符减法错误。如果有人能纠正我的错误,我将非常感激。

提前感谢您的帮助。

罗斯

4 个答案:

答案 0 :(得分:7)

大多数正则表达式都不支持字符类减法。相反,你可以使用先行断言:

/^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9](?!.?[CIKMOV])[A-Z]{2}$/

答案 1 :(得分:5)

如果不支持类减法,您应该能够使用负类来实现减法。

有些例子是[^\D] = \d[^[:^alpha:]] = [a-zA-Z]

您的问题可以像这样解决,在[^a-z[:^alpha:]CIKMOV]等字符类中使用负POSIX字符类

[^
a-z # not a-z
[:^alpha:] # not not A-Za-z
CIKMOV # not C,I,K,M,O,V
]

修改 - 这也有效,可能更容易阅读:[^[:^alpha:][:lower:]CIKMOV]

[^
[:^alpha:] # A-Za-z
[:lower:] # not a-z
CIKMOV # not C,I,K,M,O,V
]

结果是一个没有C,I,K,M,O,V的A-Z字符类 基本上是一个减法。

这是对2种不同类混合物的测试(在Perl中):

use strict;
use warnings;

my $match = '';

   # ANYOF[^\0-@CIKMOV[-\377!utf8::IsAlpha]
for (0 .. 255) {
   if (chr($_) =~ /^[^a-z[:^alpha:]CIKMOV]$/) {
       $match .= chr($_); next;
   }
   $match .= ' ';
}
$match =~ s/^ +//;
$match =~ s/ +$//;
print "'$match'\n";
$match = '';

   # ANYOF[^\0-@CIKMOV[-\377+utf8::IsDigit !utf8::IsWord]
for (0 .. 255) {
   if (chr($_) =~ /^[^a-z\d\W_CIKMOV]$/) {
       $match .= chr($_); next;
   }
   $match .= ' ';
}
$match =~ s/^ +//;
$match =~ s/ +$//;
print "'$match'\n";

输出显示A-Z减去CIKMOV的中断,来自测试的ascii字符0-255:
'AB DEFGH J L N PQRSTU WXYZ'
'AB DEFGH J L N PQRSTU WXYZ'

答案 2 :(得分:4)

PCRE不支持char类减法。

因此,您可以枚举除CIKMOV以外的所有大写字母:

^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][ABDEFGHJLNPQRSTUWXYZ]{2}$

可以使用范围缩短为:

^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-JLNP-UW-Z]{2}$

答案 3 :(得分:1)

我认为您必须将[A-Z-[CIKMOV]]替换为[ABD-HJLNP-UW-Z]。我不认为php支持字符类减法。我的替代方案是“A,B,D到H,J,L,N,P到U,W到Z”。