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

それを行う1つの方法は、次のようなものです

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)と、そのグループの開始オフセットと終了オフセットが返され、このコードはそれを使用して更新を行います。オフセットがシフトしないように、最初に文字列の末尾から置換を行う必要があります。

1つのグループのみを変更する必要がある場合は、次のことができます

x = "/foo/bar/baz"
x[/(?<group>bar)/, 'group'] = 'new'
# x is now '/foo/bar/baz'
于 2012-12-09T13:12:50.453 に答える
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 に答える