0

テキストファイルとログを処理する簡単なスクリプトに取り組んでいます。コマンドラインから、置換用の正規表現のリストを取得する必要があります。例えば:

./myscript.py --replace=s/foo/bar/ --replace=s@/etc/hosts@/etc/foo@ --replace=@test\@email.com@root\@email.com@

ユーザー指定の置換パターンを python re ライブラリに提供する簡単な方法はありますか? そして、そのパターンは文字列に対して実行されましたか? エレガントなソリューションはありますか?

可能であれば、独自のパーサーを作成することは避けたいと思います。/g や /i などの修飾子のサポートが欲しいことに注意してください。

ありがとう!

4

3 に答える 3

0

コメントで述べたように、 を使用できますが、それは明らかにing とingre.compile()でのみ機能します。置換のみがあると仮定すると、次のようにすることができます。matchsearch

modifiers_map = {
    'i': re.IGNORE,
    ...
}

for replace in replacements:
    # Look for a generalized separator in front of a command
    m = re.match(r'(s?)(.)([^\2]+)\2([^\2]+)\2([ig]*)', replace)
    if not m:
        print 'Invalid command: %s' % replace
        continue
    command, separator, query, substitution, modifiers = m.groups()
    # Convert the modifiers to flags
    flags = reduce(operator.__or__, [modifiers_map[char] for char in modifiers], 0)
    # This needs a little bit of tweaking if you want to support
    # group matching (like \1, \2, etc.). This also assumes that
    # you're only getting 's' as a command
    my_text = re.sub(query, substitution, my_text, flags=flags)

これは大まかなドラフトですが、探しているものの 90% に到達すると思います。

于 2013-03-26T19:03:39.690 に答える
0

スペースを区切り文字として使用して、シェルのコマンドライン パーサーを利用できます。

$ myscript --replace=foo bar \
>          --replace=/etc/hosts /etc/foo gi \
>          --replace=test@email.com root@email.com 

gflag は Python のデフォルトであるため、特別なサポートを追加する必要があります。

#!/usr/bin/env python
import re
from argparse import ArgumentParser
from functools import partial

all_re_flags = 'Lgimsux' # regex flags
parser = ArgumentParser(usage='%(prog)s [--replace PATTERN REPL [FLAGS]]...')
parser.add_argument('-e', '--replace', action='append', nargs='*')
args = parser.parse_args()
print(args.replace)

subs = [] # replacement functions: input string -> result
for arg in args.replace:
    count = 1 # replace only the first occurrence if no `g` flag
    if len(arg) == 2:
        pattern, repl = arg
    elif len(arg) == 3:
        pattern, repl, flags = arg
        if ''.join(sorted(flags)) not in all_re_flags:
            parser.error('invalid flags %r for --replace option' % flags)
        if 'g' in flags: # add support for `g` flag
            flags = flags.replace('g', '')
            count = 0 # replace all occurrences
        if flags: # embed flags
            pattern = "(?%s)%s" % (flags, pattern)
    else:
        parser.error('wrong number of arguments for --replace option')
    subs.append(partial(re.compile(pattern).sub, repl, count=count))

subs次のように使用できます。

input_string = 'a b a b'
for replace in subs:
    print(replace(input_string))

例:

$ ./myscript -e 'a b' 'no flag' -e 'a B' 'with flags' ig

出力:

[['a b', 'no flag'], ['a B', 'with flags', 'ig']]
no flag a b
with flags with flags
于 2013-03-27T14:05:37.113 に答える
0

答えてくれてありがとう。提案されたソリューションの複雑さと、標準ライブラリに事前にサポートされたパーサーがないことを考えると、私はさらに一歩進んで独自のパーサーを実装しました。

他の提案よりもそれほど複雑ではありません。以下を参照してください。今はテストを書くだけです。

ありがとう!

class Replacer(object):
  def __init__(self, patterns=[]):
    self.patterns = []
    for pattern in patterns:
      self.AddPattern(pattern)

  def ParseFlags(self, flags):
    mapping = {
      'g': 0, 'i': re.I, 'l': re.L, 'm': re.M, 's': re.S, 'u': re.U, 'x': re.X,
      'd': re.DEBUG
    }

    result = 0
    for flag in flags:
      try:
        result |= mapping[flag]
      except KeyError:
        raise ValueError(
            "Invalid flag: %s, known flags: %s" % (flag, mapping.keys()))
    return result

  def Apply(self, text):
    for regex, repl in self.patterns:
      text = regex.sub(repl, text)
    return text

  def AddPattern(self, pattern):
    separator = pattern[0]
    match = []
    for position, char in enumerate(pattern[1:], start=1):
      if char == separator:
        if pattern[position - 1] != '\\':
          break
        match[-1] = separator
        continue
      match += char
    else:
      raise ValueError("Invalid pattern: could not find divisor.")

    replacement = []
    for position, char in enumerate(pattern[position + 1:], start=position + 1):
      if char == separator:
        if pattern[position - 1] != '\\':
          break
        replacement[-1] = separator
        continue
      replacement += char
    else:
      raise ValueError(
          "Invalid pattern: could not find divisor '%s'." % separator)

    flags = self.ParseFlags(pattern[position + 1:])
    match = ''.join(match)
    replacement = ''.join(replacement)
    self.patterns.append((re.compile(match, flags=flags), replacement))
于 2013-03-27T16:41:07.793 に答える