繁体   English   中英

Ruby 正则表达式:多次捕获相同的捕获组

[英]Ruby Regex: Capture the same capture groups multiple times

我在我们的身份验证系统中有这些字符串,我正在尝试开发正确的 REGEX 以从中捕获特定信息。

STRING = CR*reduced*downsized*U*reduced*D*own_only

现在我需要能够提取第一个捕获组中的大写字母(如 CRUD),以及紧随其后的星号所包含的“属性”(例如downsized )。 对于大多数使用以下 REGEX 的情况,这非常有效

(C)\*?([a-z_]+)?\*?    --> Capture Group 1: "C", Capture Group2: empty
(U)\*?([a-z_]+)?\*?    --> Capture Group 1: "U", Capture Group2: 'reduced'
(C)\*?([a-z_]+)?\*?    --> Capture Group 1: "D", Capture Group2: 'own_only'

对于 R,我需要返回这两个属性,因此 Capture Group2 应该是“reduced”,Capture Group3 应该是“downsized”。 但是使用相同的正则表达式,我只得到以下结果

(R)\*?([a-z_]+)?\*?    --> Capture Group 1: "R", Capture Group2: 'reduced'

关于正则表达式的任何建议?

由于这是一个涉及重复捕获组的场景,您可以使用像这样的多步骤解决方案

text = 'CR*reduced*downsized*U*reduced*D*own_only'
rx = /([CRUD])((?:\*[a-z_]+(?:\*[a-z_]+)*(?:\*|$))?)/
matches = text.scan(rx)
p matches.map { |x| [x[0], x[1].split("*").reject(&:empty?)]};
# => [["C", []], ["R", ["reduced", "downsized"]], ["U", ["reduced"]], ["D", ["own_only"]]]

请参阅Ruby 演示正则表达式演示

详情

  • ([CRUD]) - 第 1 组:4 个字母之一
  • ((?:\*[a-z_]+(?:\*[a-z_]+)*(?:\*|$))?) - 第 2 组:可选序列
    • \* - *字符
    • [a-z_]+ - 一个或多个 ASCII 小写字母或下划线
    • (?:\*[a-z_]+)* - 零个或多个*序列和一个或多个 ASCII 小写字母或下划线
    • (?:\*|$) - *或一行的结尾(使用\z匹配整个字符串的结尾)。

使用.map { |x| [x[0], x[1].split("*").reject(&:empty?)]} .map { |x| [x[0], x[1].split("*").reject(&:empty?)]} ,你可以用*拆分第二组值并删除空项。

提取所需信息的一种方法如下。

def breakup(str)
  str.scan(/[A-Z][a-z_*]*/).map { |s| [s[0], s.scan(/[a-z_]+/)]}
end

相应的正则表达式为“匹配一个大写字母后跟零个或多个(最后的‘*’)小写字母、下划线和星号”和“匹配一个或多个(‘+’)小写字母和下划线”。


str1 = "CR*reduced*downsized*U*reduced*D*own_only"
breakup(str1)
  #=> [["C", []], ["R", ["reduced", "downsized"]], ["U", ["reduced"]],
  #    ["D", ["own_only"]]]
str2 = "CR*reduced*downsized*U*reduced*DE"    
breakup(str2)
  #=> [["C", []], ["R", ["reduced", "downsized"]], ["U", ["reduced"]],
  #    ["D", []], ["E", []]]

注意

str1.scan(/[A-Z][a-z_*]*/)
  #=> ["C", "R*reduced*downsized*", "U*reduced*", "D*own_only"]

如果有必要测试字符串是否具有有效的构造,可以尝试将其与以下正则表达式进行匹配(我已经以自由间距模式表示以使其自我记录)。

RGX =
  /
  \A              # match beginning of the string
  (?:             # begin a non-capture group
    [A-Z]         # match one upcase letter
    (?:           # begin a non-capture group
      (?:         # begin a non-capture group
        \*        # match '*'
        [a-z_]+   # match one or more of the characters indicated
      )+          # end non-capture group and execute one or more times 
      (?:         # begin a non-capture group
        \*        # match '*'
        (?=[A-Z]) # pos lookahead asserts next char is an upcase letter
        |         # or     
        \z        # at end of string
      )           # end non-capture group
    )?            # end non-capture group and optionally match it
  )+              # end non-capture group and execute it one or more times
  \z              # match end of string
  /x              # invoke free-spacing regex definition mode

str3 = "CR*reduced*downsizedU*reduced*D*own_only"
str4 = "CR*reduced*downsized*U*reduced*D*own_only*"
str5 = "*CR*reduced*downsized*U*reduced*D*own_only"

[str1, str2, str3, str4, str5].map { |s| valid?(s) }
  #=> [true, true, false, false, false]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM