是否可以为正则表达式中的可选捕获组返回null?

时间:2018-02-06 18:52:35

标签: ruby regex

我正在使用Ruby将系统中的版本号分解为其相应的部分。有两种格式需要考虑:

  type version (date)
  type date

我有一个regualr表达式可以处理version并不总是存在的事实。它是^([^\s]+)\s([^\s]+)?\s?\(?(.*?)\)?$\

然而,虽然这有效但当version不在字符串中时,可选的捕获组不存在,这是有道理的。例如(Ruby的输出):

['type', 'version', 'date']
['type', 'date', '']

有没有办法让RegEx在不存在的情况下为可选捕获组返回null?输出将是:

['type', 'version', 'date']
['type', '', 'date']

3 个答案:

答案 0 :(得分:1)

如果您正在寻找以下功能:

type version (date)
type date

结果:

['type', 'version', 'date']
['type',          , 'date']

您可能希望使用非捕获组,例如(?:version) 以及(version)|中的(?:(version)|),这将允许您捕获某些内容或捕获'nothing / null / nil'

试试这个正则表达式:

^([^\s]+)\s(?:([^\s]*)\s|)\(?(.*)\)?$

我建议使用http://regex101.com来了解不同符号的含义。

答案 1 :(得分:0)

您可以使用此regex来匹配两种格式:

/^(\S+)\s+(?:(\S+)\s+\((\S+)\)|(\S+))$/

我使用\S代替[^\s]。它们具有same meaning(任何非空格字符),但\S更短且更易于阅读。

逐句表达

/             # regex delimiter
^             # match the start of the string
(\S+)         # capturing group that matches one or more non-space characters (type)
\s+           # one or more space characters
(?:           # start of a non-capturing group
              #   +--- alternative #1: format "type version (date)"
   (\S+)      #   | capturing group for version 
   \s+        #   |
   \(         #   | match a literal '('
   (\S+)      #   | capturing group for date
   \)         #   | match a literal ')'
              #   +-------------------
|             # OR - either match alternative #1 or alternative #2
              #   +--- alternative #2: format "type date"
   (\S+)      #   | capturing group for date
              #   +-------------------
)             # end of the non-capturing group
$             # match the end of the string
/             # regex delimiter

匹配此regex的字符串产生:

irb (main)> "Type Version (Date)".match(/^(\S+)\s+(?:(\S+)\s+\((\S+)\)|(\S+))$/)
=> #<MatchData "Type Version (Date)" 1:"Type" 2:"Version" 3:"Date" 4:nil>

irb(main)> "Type Date".match(/^(\S+)\s+(?:(\S+)\s+\((\S+)\)|(\S+))$/)
=> #<MatchData "Type Date" 1:"Type" 2:nil 3:nil 4:"Date">

生成的MatchData对象中有4个项目,因为regex中有4个捕获组。替代品中存在的组被加起来,来自非匹配替代品的组被设置为nil

下一步是name the capturing groups。通过这种方式,我们可以更轻松地识别它们,并识别(和崩溃)替代方案。

现在regex是:

/^(?<type>\S+)\s+(?:(?<version>\S+)\s+\((?<date>\S+)\)|(?<date>\S+))$/

并产生:

irb(main)> "Type Version (Date)".match(/^(?<type>\S+)\s+(?:(?<version>\S+)\s+\((?<date>\S+)\)|(?<date>\S+))$/)
=> #<MatchData "Type Version (Date)" type:"Type" version:"Version" date:"Date" date:nil>

irb(main)> "Type Date".match(/^(?<type>\S+)\s+(?:(?<version>\S+)\s+\((?<date>\S+)\)|(?<date>\S+))$/)
=> #<MatchData "Type Date" type:"Type" version:nil date:nil date:"Date">

最后一步是使用方法MatchData#named_captures根据匹配的替代方法,在Hash中获取包含date正确值的匹配项。

最终结果

irb(main)> "Type Version (Date)".match(/^(?<type>\S+)\s+(?:(?<version>\S+)\s+\((?<date>\S+)\)|(?<date>\S+))$/).named_captures
=> {"type"=>"Type", "version"=>"Version", "date"=>"Date"}

irb(main)> "Type Date".match(/^(?<type>\S+)\s+(?:(?<version>\S+)\s+\((?<date>\S+)\)|(?<date>\S+))$/).named_captures
=> {"type"=>"Type", "version"=>nil, "date"=>"Date"}

附加

如果您需要在格式#2上获取空字符串而不是nil version,则可以在{{{{}}之前添加名为version的空捕获组。 1}}组:

date

答案 2 :(得分:0)

正则表达式自由区。

strs = [
  'type version (date)',
  'type date'
]

results = strs.map do |str|
  type, *version, date = str.split
  [type, version[0].to_s, date.tr('()', '')]
end

p results


--output:--
[["type", "version", "date"], ["type", "", "date"]]

如果您希望nil而不是空白字符串,请删除to_s。在nil之后,您可以编写如下内容:

if arr[1]
   #do one thing
else
   #do another thing

因为ruby中的空字符串被认为是true,所以如果数组在索引1处有一个空字符串,if-branch将始终执行。在ruby中唯一被认为是false的东西是nil和{ {1}}。