【问题标题】:How to do named capture in ruby如何在 ruby​​ 中进行命名捕获
【发布时间】:2013-09-20 11:08:40
【问题描述】:

我想命名从扫描中获取的字符串捕获。怎么做?

"555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten #=> ["555", "333", "7777"]

有没有可能变成这样

{:area => "555", :city => "333", :local => "7777" }

[["555","area"], [...]]

我试过了

"555-333-7777".scan(/((?<area>)\d{3})-(\d{3})-(\d{4})/).flatten

但它会返回

[]

【问题讨论】:

    标签: ruby regex


    【解决方案1】:

    您应该将match 与命名捕获一起使用,而不是scan

    m = "555-333-7777".match(/(?<area>\d{3})-(?<city>\d{3})-(?<number>\d{4})/)
    m # => #<MatchData "555-333-7777" area:"555" city:"333" number:"7777">
    m[:area] # => "555"
    m[:city] # => "333"
    

    如果你想要一个实际的哈希,你可以使用这样的东西:

    m.names.zip(m.captures).to_h # => {"area"=>"555", "city"=>"333", "number"=>"7777"}
    

    或者这个(ruby 2.4 或更高版本)

    m.named_captures # => {"area"=>"555", "city"=>"333", "number"=>"7777"}
    

    【讨论】:

    • 在 Ruby 2.4.0 中,我们得到了 MatchData#named_captures: m.named_captures #=&gt; {"area"=&gt;"555", "city"=&gt;"333", "number"=&gt;"7777"}
    【解决方案2】:

    这样的?

    "555-333-7777" =~ /^(?<area>\d+)\-(?<city>\d+)\-(?<local>\d+)$/
    Hash[$~.names.collect{|x| [x.to_sym, $~[x]]}]
     => {:area=>"555", :city=>"333", :local=>"7777"}
    

    奖励版本:

    Hash[[:area, :city, :local].zip("555-333-7777".split("-"))]
    => {:area=>"555", :city=>"333", :local=>"7777"}
    

    【讨论】:

      【解决方案3】:

      如果你真的不需要哈希,只需要局部变量:

      if /(?<area>\d{3})-(?<city>\d{3})-(?<number>\d{4})/ =~ "555-333-7777"
        puts area
        puts city
        puts number
      end
      

      它是如何工作的?

      • 您需要使用=~ 正则表达式运算符。
      • 正则表达式(很遗憾)需要在左侧。如果你使用string =~ regex,它不起作用。
      • 否则,?&lt;var&gt; 的语法与 named_captures 的语法相同。
      • Ruby 1.9.3 支持!

      Official documentation:

      当命名捕获组与文字正则表达式一起使用时 表达式的左侧和 =~ 运算符,捕获的文本 也分配给具有相应名称的局部变量。

      【讨论】:

        【解决方案4】:

        将捕获组名称及其值转换为哈希的一种方法是使用带有命名捕获的正则表达式,使用 (?&lt;capture_name&gt;,然后访问 %~ 全局“最后匹配”变量。

        regex_with_named_capture_groups = %r'(?<area>\d{3})-(?<city>\d{3})-(?<local>\d{4})'
        "555-333-7777"[regex_with_named_capture_groups]
        
        match_hash = $~.names.inject({}){|mem, capture| mem[capture] = $~[capture]; mem}
        # => {"area"=>"555", "city"=>"333", "local"=>"7777"}
        
        # If ActiveSupport is available
        match_hash.symbolize_keys!
        # => {area: "555", city: "333", local: "7777"}
        

        【讨论】:

          【解决方案5】:

          这个替代方案也有效:

          regex = /^(?<area>\d+)\-(?<city>\d+)\-(?<local>\d+)$/
          m = "555-333-7777".match regex
          m.named_captures
           => {"area"=>"555", "city"=>"333", "local"=>"7777"}
          

          【讨论】:

            【解决方案6】:

            有很多方法可以创建命名捕获,其中许多已经被提及。不过,为了记录,我们甚至可以像这样使用最初发布的代码和Multiple Assignment

            a, b, c =  "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
            hash = {area: a, city: b, local: c}
            #=>  {:area=>"555", :city=>"333", :local=>"7777"}
            

            hash = {}
            hash[:area], hash[:city], hash[:local] =  "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
            hash
            #=>  {:area=>"555", :city=>"333", :local=>"7777"}
            

            或连同zip 和可选的to_h

            [:area, :city, :local].zip "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
            #=>  [[:area, "555"], [:city, "333"], [:local, "7777"]]
            
            ([:area, :city, :local].zip "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten).to_h
            #=>  {:area=>"555", :city=>"333", :local=>"7777"}
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-11-10
              • 2017-06-15
              • 1970-01-01
              • 2010-09-12
              • 2011-10-17
              相关资源
              最近更新 更多