2

有没有办法在 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

我需要的是一种解决方案,它保留正则表达式匹配的上下文并直接替换它们,以便最终结果是:

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

那可能吗?

4

2 回答 2

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])}
于 2012-12-09T12:48:10.657 回答
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'
于 2012-12-09T13:12:50.453 回答