2016-05-22 9 views
1

入力文字列を解析し、二重引用符(")で囲まれた文字列が含まれているかどうかを確認したいと考えています。 バックスラッシュでエスケープされない限り、文字のシーケンス自体には二重引用符を使用することはできません(例:\")。エスケープ文字を許可する文字列リテラルをスキャンする方法は?

物事をより複雑にするために、バックスラッシュはそうのように、自分自身をエスケープすることができます:\\。 2つの(または偶数の)バックスラッシュ(\\")で始まる二重引用符はエスケープされません。 さらに悪化させるために、エスケープしない単一のバックスラッシュ(つまり、"\のあとに続かない)が許可されます。

私は、Pythonのreモジュールとそれを解決しようとしています。ターゲット文字列が走査されるよう

、左から右に'|'によって分離RESが試されている: module documentationパイプオペレータA|Bについて教えてくれる。 1つのパターンが完全に一致すると、そのブランチが受け入れられます。つまり、Aが一度一致すると、全体的に長い一致が生成されても、Bはそれ以上テストされません。言い換えれば、'|'オペレータは貪欲ではありません。

しかし、これは私が期待どおりに動作しません:

>>> import re 
>>> re.match(r'"(\\[\\"]|[^"])*"', r'"a\"') 
<_sre.SRE_Match object; span=(0, 4), match='"a\\"'> 

この正規表現のアイデアは、エスケープ文字(\\または\")のための最初のチェックにあり、それが見つからないだ場合にのみ、チェック"ではない文字(但し、単一の場合もあります)\です。 これは任意の回数発生することがあり、リテラル"文字で囲む必要があります。

私は全く一致しない文字列"a\"を期待するが、どうやらそれはありません。 私はAの部分とBの部分をテストしない部分に一致させるために\"と期待していますが、明らかにそうです。

私は本当にバックトラックは、この非常に場合にはどのように動作するかわかりませんが、それを回避する方法はありますか?

私は初期"文字の最初のチェック(および入力から削除)場合、それは別のステップで働くだろうと思います。 私は、文字列の内容を取得するには、次の正規表現を使用することができます。

>>> re.match(r'(\\[\\"]|[^"])*', r'a\"') 
<_sre.SRE_Match object; span=(0, 3), match='a\\"'> 

これは、エスケープ引用符が含まれます。左引用符が残っていないので、私は全体的に、指定された文字列が一致しないことを知っています。

は、私はそのようにそれをしなければならないか、それが単一の正規表現となし、追加の手動チェックでこれを解決することができますか?

私の実際のアプリケーションでは、" - 囲まれた文字列は、より大きなパターンの一部にすぎません。したがって、単一の正規表現ですべてを一度に行う方が簡単だと思います。

同様の質問がありましたが、エスケープされない単一のバックスラッシュが文字列の一部であるとは考えられません。regex to parse string with escaped charactersParsing for escape characters with a regular expression

+0

参照:?(?:[^ \\ "] | \\。)*" ' - >' "[^" \\] *(?:\\。[^ "\\] *)* "' –

+0

ありがとう、それは素晴らしい作品!矢印の意味は?代替案の1つは他のものより優れていますか?それらはまったく同等ですか? – Matthias

+0

はい、違いがあります。さて、それはあなたのために働くので、私は答えとしてそれを入れてみましょう。 –

答えて

1

あなたが"(\\[\\"]|[^"])*"を使用する場合は、あなたが\または"、または非"のいずれかに続いて\の0+のシーケンスに続いて、次に「閉じる」"続い"と一致します。入力が"a\"の場合、\は2番目の代替ブランチ[^"](バックスラッシュは有効な非 - ")と一致します。

あなたは非"から\を除外する必要があります。

"(?:[^\\"]|\\.)*" 
     ^^ 

だから、私たちは"非と非\[^\\"]付き)または(\\.付き)すべてのエスケープシーケンスのいずれか、その後、"と一致、0回以上。

しかし、この正規表現は、多くのバックトラックが行われているので、効率的ではありません(代替と量指定子によって発生します)。アンロールバージョンです:

  • " - 二重引用符
  • [^"\\]* - \以外のゼロ以上の文字と"
  • を:

    "[^"\\]*(?:\\.[^"\\]*)*" 
    

    regex demo

    は最後のパターンが一致して参照してください。

  • (?:\\.[^"\\]*)* - ze RO以上
    • \\.のシーケンス - バックスラッシュは、任意の文字が、改行
    • [^"\\]*に続く - \以外のゼロ以上の文字と"
  • " - 二重引用符
+0

これは確かに行く方法ですが、元の式が '\'の代わりに '' \ 'にマッチするのはなぜか疑問に思っています。後者は' A'です( '|'の左側にあります)。 "B"はこの場合どのようにマッチするのですか? – Matthias

+1

答えは:バックトラッキングは '[^"] 'が' '' 'にマッチすることを可能にします。あなたの '\\ [" \\] 'は' '^ 'の2回のパスとマッチします。最後の '' 'は* obligatory *ですが、これは正規表現エンジンがこれまでに見つかった捕捉された値をグループ化して' ''のある場所に対応させることを意味します。第2の代替ブランチ '[^"] 'とマッチする' ''の前に '' 'を見つける。* regexのデバッガの*ページで、[あなたのregexの動作](https://regex101.com/r/dZ4bB2/2)、手順12,13を参照してください。 –

+1

説明をありがとう、今私は理解すると思う!私はこの正規表現のことを初めて知っていて、正規表現のデバッガ*についてはわかりませんでした。これは、正規表現の仕組みを理解するためのすばらしいツールです。 – Matthias

関連する問題