如何捕获可选的分隔字符串并将其返回?

时间:2014-04-30 17:52:28

标签: ruby regex

我有:

"here is BN1234 and other BN456[these are things] and other".scan(/BN([a-zA-Z0-9\-_.]+)/)
=>[["1234"], ["456"]]

我想添加一个可选片段,它可以捕获结果后面的可选括号分隔字符串。捕获可以包含任何UTF-8字符,直到终止端括号。理想的情况是:

"here is BN1234 and other BN456[these are things]".scan(???)
=>[["1234"], ["456[these are things]"]]

谷歌搜索,我尝试过:

"here is BN1234 and other BN456[these are other things]".scan(/BN([a-zA-Z0-9\-_.]+)([.+])?/)
=> [["1234", nil], ["456", nil]] # <- NO

"here is BN1234 and other BN456[these are other things]".scan(/BN([a-zA-Z0-9\-_.]+\[\d+\])?/)
=> [[nil], [nil]]

获得所需结果的可选模式是什么?

3 个答案:

答案 0 :(得分:1)

"here is BN1234 and other BN456[these are things] and other".scan(/BN([a-zA-Z0-9\-_.]+(?:\[[^\]]+\])?)/)
# =>  [["1234"], ["456[these are things]"]] 

您可以查看here

答案 1 :(得分:1)

如果您从像Rubular这样的网站开始,您会发现创建/调试模式要容易得多。

这是一个起点:http://rubular.com/r/G93iPo0j5C

/BN([\w.-]+)(\[[^\]]+\])?/

将其应用于scan

'here is BN1234 and other BN456[these are things]'.scan(/BN([\w.-]+)(\[[^\]]+\])?/)
# => [["1234", nil], ["456", "[these are things]"]]

稍微调整以调整捕获结果:

'here is BN1234 and other BN456[these are things]'.scan(/BN([\w.-]+(?:\[[^\]]+\])?)/)
# => [["1234"], ["456[these are things]"]]

展平结果以摆脱嵌入的子阵列:

'here is BN1234 and other BN456[these are things]'.scan(/BN([\w.-]+(?:\[[^\]]+\])?)/).flatten
# => ["1234", "456[these are things]"]

创建图案时,简单地开始并尽可能减少视觉噪音非常重要。而不是使用[a-zA-Z0-9\-_.],了解字符集的工作原理。 \d相当于[0-9],因此您可以将模式缩减为[a-zA-Z\d\-_.]

进一步缩小\w相当于[a-zA-Z0-9_],因此模式变为[\w\-.]

最后,-内的[]应该在集合的开头或结尾定义,因为它会自动转义,进一步降低视觉噪音:[\w.-]

  

我可以问内部[^]会做什么吗?

是。你可以问。让我们分解一下:你想要在方括号内捕获文本。要捕获方括号,您必须逃避前导方括号,为清楚起见,您应该避开尾随方括号。 \[[^\]]+\]是:

  • \[ =文字起点&#39; [&#39;。
  • [^\]] =符合文字&#39; ]&#39;的 字符集。
  • \] =结束文字&#39; ]&#39;字符。

关于正则表达式模式,有一些非常重要的事情需要学习和记住。它们非常强大,但它们 NOT 是存在的每个计算/解析问题的答案。我们遇到的每个程序员,当他们了解到正则表达式时,立即尝试将它们用于所有可能的事情。这不是一个好的决定,因为编写在逻辑上漏洞的模式非常容易,通常非常糟糕。未能逃脱.[,或滥用*+,或遇到贪婪问题,可能会导致很难发现的错误。编写长图案只会增加打开这些洞的可能性,所以要保持它们的简短和甜蜜,测试它们,然后学习如何聚合它们以重复使用它们来创建大的洞。

Stack Exchange上一个臭名昭着的角色写了&#34; Regular Expressions: Now You Have Two Problems&#34;,这是一个很好的阅读。这本书&#34; Perl Best Practices&#34;有一个很好的章节解释了如何以及为什么使用模式以及如何以可读/可维护的方式编写模式。还有&#34; Mastering Regular Expressions&#34;这是必须参考的。你不必知道关于他们的一切,但是拥有&#34; MRE&#34;当你有疑问时,书会给你一个好看的地方。

答案 2 :(得分:1)

我会使用与其他人建议的略有不同的正则表达式

str = "here is BN1234 and other BN456[these are other things]"

str.scan(/(?:^|\s)BN(\d+(?:\[[^\]]+\])?)/i)
  #=> [["1234"], ["456[these are other things]"]] 
  • (?:...)表示非捕获组。
  • (?:^|\s)要求BN位于字符串的开头(^)或(|)前面至少有一个空白字符({{1} }})。
  • \s要求\d+后跟至少一位数。
  • BN末尾的?表示非捕获组(?:\[[^\]]+\])?是可选的。
  • (?:\[[^\]]+\])\[是(字面意思)打开和关闭大括号。需要转义才能将它们与正则表达式组的开头和结尾区分开来。
  • \]匹配字符类[^\]+中的一个或多个字符。 [^\]表示右括号以外的所有字符都与字符类匹配。