我有以下字符串:
SEDCVBNT S800BG09 7GFHFGD6H 324235346 RHGF7U S8-00BG/09 7687678
和以下正则表达式:
preg_match_all('/b(?=.+[0-9])(?=.+[A-Z])[A-Z0-9-/]{4,20}/i', $string, $matches)
我试图实现的是;单词";即:
- 至少包含1个数字
- 至少包含一个字母
- 可能包含
/
- 可能包含
-
不幸的是,上面的正则表达式也返回纯字母和纯数字单词:
Array (
[0] => Array (
[0] => SEDCVBNT
[1] => S800BG09
[2] => 7GFHFGD6H
[3] => 324235346
[4] => RHGF7U
[5] => S8-00BG/09
)
)
我不希望返回SEDCVBNT
或324235346
。
您需要稍微高级一点的regex语法。
我想到的正则表达式是
(?<=s|^)(?=[w/-]*d[w/-]*)(?=[w/-]*[A-Za-z][w/-]*)([w/-])+(?=s|$)
让我们来解释一下:
- 语法
[w/-]
出现了很多;这意味着";任何单词字符(包括字母、数字、重音字母等)或斜线或短划线"——实际上,您认为是有效令牌一部分的所有字符 - 正则表达式使用正向前瞻来确保在尝试匹配的地方,以下文本确实满足某些条件。正面展望如下:
(?=[w/-]*d[w/-]*)
- 它还使用正数(末尾的一个:
(?=s|$)
)和负数(开头的:(?<=s|^)
)前瞻,以确保只有当整个文本标记开始于空白字符之后或位于输入字符串(s|^
)的开头并带有空白字符或终止输入字符串(s|$
)时,才进行匹配 - 由于两个内部先行模式几乎与捕获组模式
([w/-])+
相同,因此实际上我使用它们只匹配与多个模式匹配的文本:先行模式和都是末尾的捕获组模式 - 第一前瞻确保下一个令牌包括至少一个数字(
d
) - 第二前瞻确保下一个令牌包括至少一个字母(
A-Za-z
) - 捕获组匹配一个或多个单词字符和/或CCD_ 14和CCD_
因此,为了使捕获组匹配,被检查的文本必须:
- 前面是空白或输入字符串的开头(这样可以防止部分单词匹配从不允许的字符后面开始)
- 在下一段允许的字符中至少包含一个数字(第一个正向前瞻)
- 在下一段允许的字符中至少包含一个字母(第二个正向前瞻)
- 仅由单词字符
/
和-
(捕获组)组成 - 后跟空白或输入字符串的末尾(这样可以防止部分单词匹配以不允许的字符结尾)
这正是您所需要的。:)
注意:refidle.com似乎不能很好地处理负查找,因此链接后的regexp不包括初始(?<=s|^)
部分。这意味着它将错误地匹配ABC123$DEF456
中的DEF456
。
不能依赖单词边界标记(b
)来识别";单词";对于这个任务,因为例如,以斜杠后跟空格结尾的单词将不满足单词边界。只有当确定w
和W
之间的零宽度位置时,字边界才是合适的(反之亦然)。
代码:(演示)
$string = 'SEDCVBNT S800BG09 7GFHFGD6H 324235346 RHGF7U S8-00BG/09 7687678';
preg_match_all(
'~
(?:^|s) #match start of string or whitespace
K #release previously matched characters
(?=S*[a-z]) #lookahead for zero or more visible characters followed by letter
(?=S*d) #lookahead for zero or more visible characters followed by number
[a-zd/-]+ #match one or more consecutive whitelisted characters
(?=s|$) #lookahead for a whitespace or the end of string
~xi', #ignore literal whitespaces in pattern, use case-insensitivity with letters
$string,
$m
);
var_export($m);
这是原始正则表达式:b(?=S*?d)(?=S*?[a-z])S+?(?=$|s)
preg_match_all('/b(?=S*?d)(?=S*?[a-z])S+?(?=$|s)/i', $string, $matches)