php 匹配所有空格分隔的“单词”,其中至少包含1个字母和1个数字,并且可能包含斜杠和连字符

e37o9pze  于 2023-05-05  发布在  PHP
关注(0)|答案(3)|浏览(153)

我有以下字符串:

SEDCVBNT S800BG09 7GFHFGD6H 324235346 RHGF7U S8-00BG/09 7687678

下面的regex:

preg_match_all('/\b(?=.+[0-9])(?=.+[A-Z])[A-Z0-9-\/]{4,20}/i', $string, $matches)

我想实现的是返回所有完整的“单词”:

  • 至少包含1个数字
  • 至少包含1个字母
  • 可能包含/
  • 可能包含-

不幸的是,上面的正则表达式返回纯字母和纯数字单词:

Array (
  [0] => Array (
      [0] => SEDCVBNT
      [1] => S800BG09
      [2] => 7GFHFGD6H
      [3] => 324235346
      [4] => RHGF7U
      [5] => S8-00BG/09
  )
)

我不希望返回SEDCVBNT324235346

0s0u357o

0s0u357o1#

对于这个问题,您需要稍微高级的正则表达式语法。
我想出来的正则表达式是

(?<=\s|^)(?=[\w/-]*\d[\w/-]*)(?=[\w/-]*[A-Za-z][\w/-]*)([\w/-])+(?=\s|$)

让我们来解释一下:

  • 语法[\w/-]经常出现;这意味着“任何单词字符(包括字母、数字、重音字母等)或斜线或破折号”--实际上,您认为是有效标记的一部分的所有字符。
  • 正则表达式使用正向前看来确保在尝试匹配的地方,下面的文本确实满足某些条件。正向前瞻如下所示:(?=[\w/-]*\d[\w/-]*)
  • 它还使用了positive(最后的那个:(?=\s|$))* 和 * 负(在开始时:(?<=\s|^))lookahead,以确保仅当整个文本标记在空白字符之后开始或在输入字符串的开头(\s|^)* 并且 * 后跟空白字符或终止输入字符串(\s|$)时才进行匹配。
  • 由于两个内部先行模式几乎与捕获组模式([\w/-])+相同,实际上我只使用它们来匹配匹配 * 多个 * 模式的文本:两个lookaheads * 和 * 捕获组模式在最后。
  • 第一个先行确保下一个令牌至少包括一个数字(\d)。
  • 第二个预看确保下一个令牌包括至少一个字母(A-Za-z)。
  • 捕获组匹配一个或多个单词字符和/或/-

因此,要使捕获组匹配,所检查的文本必须:
1.前面有空格或输入字符串的开头(这可以防止在不允许的字符之后开始部分字匹配)
1.在下一段允许的字符中至少包括一个数字(第一个正先行)
1.在下一段允许的字符中至少包含一个字母(第二个正先行)
1.仅包含单词字符/-(捕获组)。
1.后面跟着空格或输入字符串的结尾(这可以防止部分单词匹配以不允许的字符结尾)。
这正是你所需要的。:)

**注意:**refiddle.com似乎不能很好地处理负向后查找,因此链接后的regexp不包括初始的(?<=\s|^)部分。这意味着它将错误地匹配ABC123$DEF456中的DEF456

3qpi33ja

3qpi33ja2#

不能依赖字边界标记(\b)来识别该任务的“字”的边缘,因为例如,以斜线结尾的字后面跟着空格将不满足字边界。字边界仅适用于确定\w\W之间的零宽度位置(反之亦然)。
代码:(Demo

$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-z\d/-]+    #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);
ergxz8rk

ergxz8rk3#

下面是原始正则表达式:\b(?=\S*?\d)(?=\S*?[a-z])\S+?(?=$|\s)

preg_match_all('/\b(?=\S*?\d)(?=\S*?[a-z])\S+?(?=$|\s)/i', $string, $matches)

相关问题