5

バックスラッシュでエスケープされていない(それ自体がバックスラッシュでエスケープされていない)バランスの取れた区切り文字のペアを(ネストを考慮する必要なしに)一致させるにはどうすればよいですか?たとえば、バッククォートを使用してこれを試しましたが、エスケープされたバッククォートがエスケープされたように機能していません。

regex = /(?!<\\)`(.*?)(?!<\\)`/
"hello `how\` are` you"
# => $1: "how\\"
# expected "how\\` are"

また、上記の正規表現では、バックスラッシュによってエスケープされ、バッククォートの前にあるバックスラッシュは考慮されていませんが、私はそうしたいと思います。

StackOverflowはこれをどのように行いますか?

これの目的はそれほど複雑ではありません。StackOverflowと同じように、インラインコードのバッククォート表記を含むドキュメントテキストがあります。これを、スパンマテリアルで装飾されたインラインコードを含むHTMLファイルに表示したいと思います。ネストはありませんが、エスケープされたバックティックまたはエスケープされたバックスラッシュがどこにでも表示される可能性があります。

4

2 に答える 2

6

ルックビハインドは、この種の問題について誰もが最初に考えることですが、無制限のルックビハインドをサポートする.NETのようなフレーバーであっても、間違ったツールです。何かをハックすることはできますが、.NETでも醜いものになります。より良い方法は次のとおりです。

`[^`\\]*(\\.[^`\\]*)*`

最初の部分は、開始区切り文字から始まり、区切り文字または円記号以外のものをすべて飲み込みます。次の文字が円記号の場合、それが何であれ、それとそれに続く文字を消費します。区切り文字、別の円記号、またはその他のものである可能性がありますが、問題ではありません。

これらの手順を必要な回数繰り返します。一致することも一致すること[^`\\]\\.できない場合は、次の文字を終了区切り文字にする必要があります。または文字列の終わりですが、入力は整形式であると想定しています。ただし、整形式でない場合、この正規表現はすぐに失敗します。この他のアプローチのために、私は多くのことを目にします。

`(?:[^`\\]+|\\.)*`

これは整形式の入力では正常に機能しますが、サンプル入力から最後のバッククォートを削除するとどうなりますか?

"hello `how\` are you"

RegexBuddyによると、最初のバッククォートに遭遇した後、この正規表現は、失敗をあきらめて報告する前に、9,252の異なる操作(またはステップ)を実行しました。私は10ステップで失敗しました。

編集区切り文字内のパーだけを抽出するには、その部分をキャプチャグループでラップします。それでも、バックスラッシュを手動で削除する必要があります。

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

また、他のグループを非キャプチャに変更しました。これは最初から行う必要がありました。私は宗教的にキャプチャすることを避けませんが、ものをキャプチャするためにそれらを使用している場合、使用する他のグループはキャプチャしない必要があります。

編集私は質問を読みすぎていると思います。StackOverflowでは、インラインコードセグメントまたはコメントにリテラルバックティックを含める場合は、区切り文字として1つだけでなく、3つのバックティックを使用します。バックティックをエスケープする必要がないため、バックスラッシュも無視できます。あなたの正規表現はこれと同じくらい単純であることが判明するかもしれません:

```(.*?)```

誤った区切り文字の可能性に対処するには、同じ基本的な手法を使用します。

```([^`]*(?:`(?!``)[^`]*)*)```

これはあなたが求めているものですか?


ちなみに、この答えは上記の@nneonneoのコメントと矛盾しません。この回答は、試合が行われている状況を考慮していません。プログラムやウェブページのソースコードに含まれていますか?そうである場合、一致はコメントまたは文字列リテラル内で発生しましたか?最初に見つけたバッククォートがエスケープされなかったことをどうやって知ることができますか?正規表現は、それらが動作するコンテキストについて何も知りません。それがパーサーの目的です。

于 2012-10-25T06:10:50.040 に答える
2

ネストが必要ない場合は、正規表現が適切なツールになります。たとえば、プログラミング言語のレクサーは正規表現を使用して文字列をトークン化し、文字列は通常、エスケープされたコンテンツとして独自の区切り文字を許可します。それよりも複雑なものは、おそらく本格的なパーサーが必要になります。

「一般式」は、エスケープ文字(\\.)、またはコンテンツとして有効であるがエスケープする必要のない任意の文字()に一致するものです[^{list of invalid chars}]。「ナイーブ」な解決策は、または|)でそれらを結合することですが、より効率的なバリアントについては、@AlanMooreの回答を参照してください。

完全な例を以下に示します。2つのバリエーションがあります。1つ目は、バックスラッシュは文字列内のエスケープにのみ使用する必要があると想定し、2つ目は、テキスト内の任意の場所でバックスラッシュが次の文字をエスケープすることを前提としています。

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

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

ここここでの実例。ただし、@ nneonneoがコメントしたように(そして私が承認したように)、正規表現は完全な解析を行うことを意図していないため、正しく機能させたい場合は、物事を単純に保つ方がよいでしょう(テキストでトークンを見つけたいですか?または、それがどこから始まるかをすでに知っている状態で区切りたいですか?その質問への答えは、どの戦略があなたのケースに最適であるかを決定するために重要です)。

于 2012-10-25T05:37:59.113 に答える