簡體   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