ソース:
[This] is some text with [some [blocks that are nested [in a [variety] of ways]]]
結果のテキスト:
[This] is some text with
スタックオーバーフローのスレッドを見ると、これに対して正規表現を実行できるとは思いません。
これを行う簡単な方法はありますか->またはpyparsing(または他の解析ライブラリ)に手を伸ばす必要がありますか?
依存関係を必要としない簡単な方法は次のとおりです。テキストをスキャンし、渡した中括弧のカウンターを保持します。「[」が表示されるたびにカウンターをインクリメントします。「]」が表示されるたびにデクリメントします。
[
。ゼロより小さい場合は、その数の超過があります]
。)OPの例を規範として(さらにネストされたブロックを含むすべてのブロックを削除する必要があります)、どうでしょうか...:
import itertools
x = '''[This] is some text with [some [blocks that are nested [in a [variety]
of ways]]] and some [which are not], and [any [with nesting] must go] away.'''
def nonest(txt):
pieces = []
d = 0
level = []
for c in txt:
if c == '[': d += 1
level.append(d)
if c == ']': d -= 1
for k, g in itertools.groupby(zip(txt, level), lambda x: x[1]>0):
block = list(g)
if max(d for c, d in block) > 1: continue
pieces.append(''.join(c for c, d in block))
print ''.join(pieces)
nonest(x)
これは放出します
[This] is some text with and some [which are not], and away.
これは、通常の仮説の下では、望ましい結果であるように思われます。
level
アイデアは、「この時点でどのくらいネストされているか」(つまり、これまでに開いたブラケットとまだ閉じていないブラケットの数)のカウントの並列リストを計算することです。level
次に、テキスト付きのzipをgroupby
、ネストがゼロでネストが0より大きい代替ブロックにセグメント化します。各ブロックについて、ここでの最大ネストが計算されます(ネストがゼロのブロックではゼロのままになります。より一般的には、ブロック全体のネストレベルの最大値)、結果のネストが1未満の場合、対応するテキストブロックが保持されます。g
グループをリストにする必要があることに注意してくださいblock
2つの反復パス(1つは最大のネストを取得するため、もう1つは文字をテキストのブロックに再結合するため)を実行するため、1つのパスで実行するには、ネストされたループで補助状態を維持する必要があります。この場合、これは少し不便です。
特にpyparsingのようなパーサージェネレーターを使用する場合は、パーサーを作成する方がよいでしょう。それはより保守可能で拡張可能になります。
実際、pyparsingはすでにパーサーを実装しているので、パーサーの出力をフィルター処理する関数を作成するだけです。
expression.transformString()で使用できる単一のパーサー式を作成する際に2回パスを取りましたが、解析時にネストされた[]とネストされていない[]を区別するのが困難でした。最終的に、transformStringでループを開き、scanStringジェネレーターを明示的に反復処理する必要がありました。
元の質問に基づいて[一部]を含める必要があるかどうかの質問に対処するために、次の文字列を使用して、最後に「ネストされていない」テキストを追加することでこれを調査しました。
src = """[This] is some text with [some [blocks that are
nested [in a [variety] of ways]] in various places]"""
私の最初のパーサーは、元の質問の先頭に従い、ネストされている括弧で囲まれた式を拒否します。2番目のパスは、角かっこで囲まれた式の最上位トークンを受け取り、角かっこで返します。「一部」と「さまざまな場所」が連続していないという情報が失われるため、このソリューションはあまり好きではありませんでした。そのため、最後のパスを1つ取得し、nestedExprのデフォルトの動作にわずかな変更を加える必要がありました。以下のコードを参照してください。
from pyparsing import nestedExpr, ParseResults, CharsNotIn
# 1. scan the source string for nested [] exprs, and take only those that
# do not themselves contain [] exprs
out = []
last = 0
for tokens,start,end in nestedExpr("[","]").scanString(src):
out.append(src[last:start])
if not any(isinstance(tok,ParseResults) for tok in tokens[0]):
out.append(src[start:end])
last = end
out.append(src[last:])
print "".join(out)
# 2. scan the source string for nested [] exprs, and take only the toplevel
# tokens from each
out = []
last = 0
for t,s,e in nestedExpr("[","]").scanString(src):
out.append(src[last:s])
topLevel = [tok for tok in t[0] if not isinstance(tok,ParseResults)]
out.append('['+" ".join(topLevel)+']')
last = e
out.append(src[last:])
print "".join(out)
# 3. scan the source string for nested [] exprs, and take only the toplevel
# tokens from each, keeping each group separate
out = []
last = 0
for t,s,e in nestedExpr("[","]", CharsNotIn('[]')).scanString(src):
out.append(src[last:s])
for tok in t[0]:
if isinstance(tok,ParseResults): continue
out.append('['+tok.strip()+']')
last = e
out.append(src[last:])
print "".join(out)
与える:
[This] is some text with
[This] is some text with [some in various places]
[This] is some text with [some][in various places]
これらの1つがOPの質問に近づくことを願っています。しかし、他に何もないとしても、nestedExprの動作をもう少し詳しく調べる必要があります。