2012-12-09 18 views
2

有沒有一種方法可以在Ruby中通過組名來執行使用分組正則表達式的替換?用Ruby中的組名稱替換分組的正則表達式

這是我到目前爲止有(但你會看到它缺少這使得一些有價值的環境下很常見的情況是無用的):

class String 

    def scan_in_groups(regexp) 
     raise ArgumentError, 'Regexp does not contain any names.' if regexp.names.empty? 

     captures = regexp.names.inject({}){ |h, n| h[n] = []; h } 

     scan(regexp).each do |match| 
      captures.keys.zip(match).each do |group, gmatch| 
       next if !gmatch 
       captures[group] << gmatch 
      end 
     end 

     captures.reject { |_, v| v.empty? } 
    end 

    def sub_in_groups(regexp, group_hash) 
     dup.sub_in_groups!(regexp, group_hash) 
    end 

    def sub_in_groups!(regexp, group_hash) 
     scan_in_groups(regexp).each do |name, value| 
      next if !group_hash[name] 
      sub!(value.first, group_hash[name]) 
     end 
     self 
    end 

end 

regexp =/
    \/(?<category>\w+)   # matches category type 
    \/       # path separator 
    (?<book-id>\d+)   # matches book ID numbers 
    \/       # path separator 
    .*       # irrelevant 
    \/       # path separator 
    chapter-(?<chapter-id>\d+) # matches chapter ID numbers 
    \/       # path separator 
    stuff(?<stuff-id>\d+)  # matches stuff ID numbers 
/x 

path = '/book/12/blahahaha/test/chapter-3/stuff4/12' 

p path.scan_in_groups(regexp) 
#=> {"category"=>["book"], "book-id"=>["12"], "chapter-id"=>["3"], "stuff-id"=>["4"]} 

update = { 
    'category' => 'new-category', 
    'book-id' => 'new-book-id', 
    'chapter-id' => 'new-chapter-id', 
    'stuff-id' => '-new-stuff-id' 
} 

p path.sub_in_groups(regexp, update) 
#=> "/new-category/new-book-id/blahahaha/test/chapter-new-chapter-id/stuff-new-stuff-id/12" 

p '/12/book/12/blahahaha/test/chapter-3/stuff4/12'.sub_in_groups(regexp, update) 
#=> /new-book-id/new-category/12/blahahaha/test/chapter-new-chapter-id/stuff-new-stuff-id/12 

我需要的是一個解決方案,蜜餞正則表達式匹配的背景和obly替換它們,這樣最終的結果將是:

#=> /12/new-category/new-book-id/blahahaha/test/chapter-new-chapter-id/stuff-new-stuff-id/12

這可能嗎?

回答

0

一種方法是這樣的

def substitute!(regexp, string,updates) 
    if match = regexp.match(string) 
    keys_in_order = updates.keys.sort_by {|k| match.offset(k)}.reverse 
    keys_in_order.each do |k| 
     offsets_for_group = match.offset(k) 
     string[offsets_for_group.first...offsets_for_group.last] = updates[k] 
    end 
    end 
end 

這就地修改字符串。

當你有匹配數據時,然後match.offset(capture_name)返回該組的開始和結束偏移量,然後該代碼用於執行更新。您需要首先從字符串末尾進行置換,以便它們不會移動偏移量。

如果你只需要改變一個組,你可以做

x = "/foo/bar/baz" 
x[/(?<group>bar)/, 'group'] = 'new' 
# x is now '/foo/bar/baz' 
+0

你錯過了「更新」的說法,但比它似乎只是正常工作等,謝謝。 :) –

0

單詞要改變一樣嗎?這樣做的

replacements = [ ["category", "new-category"], ["book-id", "new-book-id"], ["chapter-id", "new-chapter-id"], ["stuff-id", "-new-stuff-id"] ] 
replacements.each {|replacement| str.gsub!(replacement[0], replacement[1])}