多くのプログラマーは、最近のWebサービスの助けを借りて、またはより伝統的にはインタラクティブプロンプトで、または開発中の正規表現を含む小さなスクリプトを記述して、テストケースのコレクションを作成することで、迅速な正規表現を作成する喜びを知っています。 。どちらの場合も、プロセスは反復的でかなり迅速です。暗号化された文字列をハッキングし続けて、一致する文字列が見つかり、必要なものをキャプチャして不要なものを拒否します。
単純な場合の結果は、Java regexp:
Pattern re = Pattern.compile(
"^\\s*(?:(?:([\\d]+)\\s*:\\s*)?(?:([\\d]+)\\s*:\\s*))?([\\d]+)(?:\\s*[.,]\\s*([0-9]+))?\\s*$"
);
多くのプログラマーは、正規表現を編集したり、レガシーコードベースで正規表現をコード化したりする必要があることの痛みも知っています。少し編集して分割すると、正規表現をかなり理解している人であれば、正規表現よりもはるかに簡単に理解できます。また、正規表現のベテランは、それが何をするかをすぐに確認する必要があります(誰かがエクササイズをしたい場合に備えて、投稿の最後に回答してください)自分でそれを理解するのです)。
ただし、正規表現が本当に書き込み専用になるために、物事をもっと複雑にする必要はありません。また、勤勉なドキュメント(誰もがもちろん )彼らが書くすべての複雑な正規表現に対して行う...)、正規表現の変更は困難な作業になります。正規表現が慎重に単体テストされていない場合(ただし、全員もちろんは、すべての複雑な正規表現に対して包括的な単体テストを行っています。正負...)。
ですから、簡単に言えば、力を失うことなく正規表現の書き込み/読み取りソリューション/代替手段はありますか?上記の正規表現は代替アプローチでどのように見えますか?どの言語でも問題ありませんが、多言語のソリューションが最適ですが、正規表現は多言語です。
次に、以前の正規表現が行うことは、これです。1:2:3.4
の形式で数値の文字列を解析し、各数値をキャプチャします。スペースが許可され、3
のみが必要です。
多くの人が小さな部分から構成することについて言及しましたが、まだ例を挙げていないので、これは私のものです:
string number = "(\\d+)";
string unit = "(?:" + number + "\\s*:\\s*)";
string optionalDecimal = "(?:\\s*[.,]\\s*" + number + ")?";
Pattern re = Pattern.compile(
"^\\s*(?:" + unit + "?" + unit + ")?" + number + optionalDecimal + "\\s*$"
);
最も読みやすいとは言えませんが、オリジナルよりもはっきりしているように感じます。
また、C#には@
演算子があり、これを文字列の前に追加して、文字どおりに解釈されることを示すことができます(エスケープ文字なし)。したがって、number
は@"([\d]+)";
になります。
正規表現を文書化する鍵は、それを文書化することです。多くの場合、人々はラインノイズのように見えるものを投げて、そのままにしておきます。
Perl 内で、正規表現の末尾にある/x
演算子は空白を抑制し、正規表現を文書化できるようにします。
上記の正規表現は次のようになります。
$re = qr/
^\s*
(?:
(?:
([\d]+)\s*:\s*
)?
(?:
([\d]+)\s*:\s*
)
)?
([\d]+)
(?:
\s*[.,]\s*([\d]+)
)?
\s*$
/x;
はい、縦の空白を少し消費しますが、読みやすさを犠牲にすることなく短縮できます。
そして、以前の正規表現は次のとおりです。1:2:3.4の形式で数値の文字列を解析し、各数値をキャプチャします。スペースが許可され、必要なのは3つだけです。
この正規表現を見ると、それがどのように機能するか(そして機能しないか)がわかります。この場合、この正規表現は文字列1
に一致します。
他の言語でも同様のアプローチをとることができます。 python re.VERBOSE オプションがそこで機能します。
Perl6(上記の例はPerl5の場合)は、これを rules の概念でさらに進め、PCREよりも強力な構造につながります(それは他の文法(コンテキストフリーおよびコンテキスト依存)へのアクセスを提供します)定期的なものと拡張された通常のもの)。
Java(この例の由来))では、文字列連結を使用して正規表現を作成できます。
Pattern re = Pattern.compile(
"^\\s*"+
"(?:"+
"(?:"+
"([\\d]+)\\s*:\\s*"+ // Capture group #1
")?"+
"(?:"+
"([\\d]+)\\s*:\\s*"+ // Capture group #2
")"+
")?"+ // First groups match 0 or 1 times
"([\\d]+)"+ // Capture group #3
"(?:\\s*[.,]\\s*([0-9]+))?"+ // Capture group #4 (0 or 1 times)
"\\s*$"
);
確かに、これにより文字列にさらに多くの"
が作成され、混乱を招く可能性があり、より簡単に(特にほとんどのIDEで構文の強調表示を使用して)文書化できます。
重要なのは、正規表現がしばしば陥るパワーと「1度書き」の性質を認識することです。これを防御的に回避して正規表現を明確に理解できるようにするコードを書くことが重要です。 Java明確にするためにコードをフォーマットします-正規表現は、言語がそうするためのオプションを提供する場合、違いはありません。
一部の言語とライブラリが提供する「冗長」モードは、これらの懸念に対する答えの1つです。このモードでは、正規表現文字列の空白が取り除かれ(そのため、\s
を使用する必要があります)、コメントが可能です。これは、デフォルトでこれをサポートする Python の短い例です。
email_regex = re.compile(r"""
([\w\.\+]+) # username (captured)
@
\w+ # minimal viable domain part
(?:\.w+) # rest of the domain, after first dot
""", re.VERBOSE)
ない言語では、冗長モードから「通常」モードへのトランスレータを実装するのは簡単な作業です。正規表現の読みやすさが気になる場合は、この時間への投資をかなり簡単に正当化できます。
正規表現を使用するすべての言語では、簡単なブロックからそれらを作成して読みやすくすることができます。例よりも複雑な(または例と同じくらい複雑な)ものであれば、必ずそのオプションを利用する必要があります。 Java=およびその他の多くの言語の特定の問題は、正規表現を「ファーストクラス」の市民として扱わず、代わりに文字列リテラルを介して言語に忍び込むことを要求することです。これは多くのことを意味します引用符とバックスラッシュは、実際には正規表現構文の一部ではないため読みづらくなります。また、独自のミニ言語とインタープリターを効果的に定義しないと、それよりも読みやすくなることはありません。
正規表現を統合するプロトタイプのより良い方法は、もちろんPerlで、そのホワイトスペースオプションと正規表現の引用演算子を使用しました。 Perl 6は、正規表現をパーツから実際の再帰的文法に構築するという概念を拡張します。これは、使用するのに比べてはるかに優れており、比較はまったくできません。言語は適時性のボートを逃したかもしれませんが、その正規表現のサポートはThe Good Stuff(tm)でした。
Expressoを使用したい: http://www.ultrapico.com/Expresso.htm
この無料のアプリケーションには、次のような便利な機能があります。
たとえば、送信した正規表現では、次のようになります。
もちろん、それを試してみるのは、それを説明する千の言葉の価値があります。また、私はこのアプリケーションのエディターと何らかの関係があることに注意してください。
場合によっては、BNFのような文法を使用すると役立つ場合があります。これらは正規表現よりもはるかに読みやすい場合があります。次に、GoldParser Builderなどのツールを使用して、文法をパーサーに変換します。
BNF、EBNFなどの文法は、複雑な正規表現よりも読みやすく、作成しやすい場合があります。 GOLDはそのようなものの1つのツールです。
以下のc2 wikiリンクには、グーグル化できる代替案のリストがあり、それらに関するいくつかの議論が含まれています。これは基本的に、私の文法エンジンの推奨事項を補完するための「参照」リンクです。
「代替」は「構文が異なる意味的に同等の機能」を意味し、RegularExpressionsの代わりに、またはこれらの代わりに、RegularExpressionsを使用できます。
- 基本的な正規表現
- 「拡張」正規表現
- Perl互換の正規表現
- ...および他の多くのバリアント...
- SNOBOLスタイルのRE構文(SnobolLanguage、IconLanguage)
- SRE構文(REはEssExpressionsとして)
- さまざまなFSMシンタックス
- 有限状態交差文法(かなり表現力がある)
- OMetaLanguageおよびLuaLanguageと同様のParsingExpressionGrammars( http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html )
- RebolLanguageの解析モード
- 確率ベースの解析...
これは古い質問であり、 Verbal Expressions についての言及はありませんでしたので、将来の求職者のためにその情報をここに追加すると思いました。言語表現は、正規表現の記号の意味を学ぶ必要なく、正規表現を人間が理解できるようにするために特別に設計されました。次の例を参照してください。私はこれがあなたが求めていることを最もうまくやると思います。
// Create an example of how to test for correctly formed URLs
var tester = VerEx()
.startOfLine()
.then('http')
.maybe('s')
.then('://')
.maybe('www.')
.anythingBut(' ')
.endOfLine();
// Create an example URL
var testMe = 'https://www.google.com';
// Use RegExp object's native test() function
if (tester.test(testMe)) {
alert('We have a correct URL '); // This output will fire}
} else {
alert('The URL is incorrect');
}
console.log(tester); // Outputs the actual expression used: /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/
この例はJavaScriptの例であり、多くの プログラミング言語でこのライブラリを見つけることができます。
最も簡単な方法は、正規表現を使用することですが、説明的な名前を使用して、より簡単な表現を作成して式を作成します。 http://www.martinfowler.com/bliki/ComposedRegex.html (そして、これは文字列連結からのものです)
ただし、別の方法として、パーサーコンビネーターライブラリを使用することもできます。 http://jparsec.codehaus.org/ これは、完全な再帰的な適切なパーサーを提供します。ここでも、本当の力はコンポジション(今回は機能的コンポジション)に由来しています。
Logstashの grok 式について言及する価値があると思いました。 Grokは、短い解析式から長い解析式を作成するという考えに基づいています。これは、これらのビルディングブロックの便利なテストを可能にし、100以上の 一般的に使用されるパターン がプリパッケージされています。これらのパターン以外に、すべての正規表現構文を使用できます。
上記のgrokで表現されたパターンは次のとおりです(私は debugger app でテストしましたが、失敗した可能性があります)。
"(( *%{NUMBER:a} *:)? *%{NUMBER:b} *:)? *%{NUMBER:c} *(. *%{NUMBER:d} *)?"
オプションのパーツとスペースは通常よりも少し醜く見えますが、ここでも他の場合でも、grokを使用すると、生活がはるかに良くなります。
F#には FsVerbalExpressions モジュールがあります。言語表現から正規表現を作成できます。また、事前に作成された正規表現(URLなど)もあります。
この構文の例の1つは次のとおりです。
let groupName = "GroupNumber"
VerbEx()
|> add "COD"
|> beginCaptureNamed groupName
|> any "0-9"
|> repeatPrevious 3
|> endCapture
|> then' "END"
|> capture "COD123END" groupName
|> printfn "%s"
// 123
F#構文に慣れていない場合、groupNameは文字列「GroupNumber」です。
次に、「COD(?<GroupNumber> [0-9] {3})END」として構成する言語表現(VerbEx)を作成します。次に、文字列「COD123END」でテストし、名前付きのキャプチャグループ「GroupNumber」を取得します。これは123になります。
正直に言うと、通常の正規表現の方がはるかに理解しやすいと思います。