【问题标题】:regex doesn't match the word if it's not the last word如果它不是最后一个单词,则正则表达式不匹配该单词
【发布时间】:2015-11-22 18:55:19
【问题描述】:

我正在尝试编写一个正则表达式,它可以匹配具有这些条件的字符串中的单词:

  1. 单词长度必须为 8 个字符。
  2. 单词的任何位置都必须有 1 个字母字符 字。
  3. 单词的任何位置都必须有 7 位数字。

\b(?=\w{8}\z)(?=[^a-zA-Z]*[a-zA-Z]{1})(?=(?:[\D]*[\d]){7}).*\b

这可以找到"123r1234""foo 123r1234",但找不到"foo bar 123r1234 foo"。 我试图添加单词边界,但它没有用。 我的正则表达式有什么问题,我该如何解决?

谢谢。

【问题讨论】:

  • @stribizhev 你是对的 :)
  • @MarcosPérezGude:那为什么要发布其他人的建议?你能解释什么是错的,什么是错的吗?为什么要删除这个或那个?
  • 不要在你的正则表达式中使用 \z - “如果你只想在字符串的绝对末尾进行匹配,请使用 \z。”。您需要编辑您的正则表达式,但 \z 是它仅匹配字符串的最后一个单词的原因。
  • @stribizhev 是的,它有效。谢谢。如果您将其发布为答案,我会批准它。

标签: regex


【解决方案1】:

您可以使用以下正则表达式:

\b(?=[^a-zA-Z]*[a-zA-Z])(?=(?:\D*\d){7})\w{8}\b

demo

这里有几点需要注意:

  1. 没有必要将单个速记类(如\d)包含在字符类中(模式变得太笨拙且可读性差)。因此,请使用\D 而不是[\D]
  2. 预测次数的规则应该等于条件数 - 1(请参阅Fine-Tuning: Removing One Condition at rexegg.com)。大多数情况下,只有 1 个字符/字符类的长度限制前瞻是被移植到基本模式的有效候选者。在这里,(?=\w{8}) 可以轻松替换最后的.*
  3. (?=\w{8}\z) 前瞻包含一个字符串结尾 \z 锚,它强制在字符串末尾进行匹配,而您需要(现在我知道)单词的结尾。
  4. [a-zA-Z]{1} 等于 [a-zA-Z],因为 {1} 表示 * 正好是一次重复,并且是多余的(同样,正则表达式模式应该尽可能简洁明了)。

更新(+1 转到@Jonny5)

解决当前问题还有另一种方法:让单词包含 8 个单词字符,但仅匹配包含任意位数的 1 个字母。这可以通过

来实现
(?i)\b(?=\w{8}\b)\d*[a-z]\d*\b

another demo(注意i在这里使用修饰符)

【讨论】:

  • 我觉得你甚至可以shorten this down to this:\b(?=\w{8}\b)\d*[A-Za-z]\d*\b
  • @Jonny5:你不放弃单词的任何位置都必须有7位数字的条件吗?我同意我们可以使用 i 修饰符缩短正则表达式:(?i)\b(?=[^a-z]*[a-z])(?=(?:\D*\d){7})\w{8}\b/\b(?=[^a-z]*[a-z])(?=(?:\D*\d){7})\w{8}\b/i
  • 我需要 8 个单词字符,其中包含一个 [A-Za-z] 和其余数字以满足单词边界。
  • @Jonny5:我明白了,它使用了相反的排除条件逻辑。是的,也应该工作。您认为我应该添加它,还是将其作为替代解决方案发布?
  • 您的回答中详细解释了这些问题。如果您添加它,我会感到自豪,不会发布。
【解决方案2】:

您可以删除最后一个星号并通过 8 计数器更改它。

\b(?=[^a-zA-Z]*[a-zA-Z])(?=(?:[\D]*[\d]){7})\w{8}\b

你可以在这里查看它的运行情况:

https://regex101.com/r/bX6rK8/1

【讨论】:

  • 解释 last asterisk and change it by the 8 counter 是不恰当的,因为这不是 OP 的正则表达式失败的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多