正規表現の代替 (流暢な?) インターフェイスの設計
-
21-09-2019 - |
質問
Java の巨大な正規表現を見て、正規表現一般の保守性について少し考えさせられました。一部の悪質な Perl 屋を除いて、ほとんどの人は正規表現が保守しにくいということに同意すると思います。
この状況をどうすれば解決できるかを考えていました。これまでのところ、私が持っている最も有望なアイデアは、 流暢なインターフェース. 。例を挙げると、次のようになります。
Pattern pattern = Pattern.compile("a*|b{2,5}");
このようなことを書くこともできます
import static util.PatternBuilder.*
Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();
Pattern alternative =
or(
string("a").anyTimes(),
string("b").times(2,5)
)
.compile();
この非常に短い例では、正規表現を作成する一般的な方法は、凡庸な才能のある開発者にとっても非常に読みやすいものです。ただし、それぞれ 80 文字で 2 行以上に及ぶ不気味な表現について考えてみましょう。確かに、(冗長な)流暢なインターフェイスには 2 行ではなく数行が必要になりますが、はるかに読みやすい(つまり保守しやすい)ものになると確信しています。
さて、私の質問は次のとおりです。
正規表現に対する同様のアプローチをご存知ですか?
このアプローチは単純な文字列を使用するよりも優れている可能性があることに同意しますか?
API はどのように設計しますか?
あなたのプロジェクトでこのような優れたユーティリティを使用したいと思いますか?
これを実装すると面白いと思いますか?;)
編集:誰もが正規表現から除外する単純な構造よりも高いレベルにあるメソッドが存在する可能性があることを想像してください。
// matches aaaab@example.com - think of it as reusable expressions
Pattern p = string{"a").anyTimes().string("b@").domain().compile();
編集 - コメントの短い要約:
正規表現バディ - コードを読みやすくするために 30 ユーロを費やします (なんと?!このような製品が純粋に存在するということは、私の持論が正しいことを証明しています。今日私たちが知っている正規表現は悪いことです (tm))
マーティン・ファウラーのアプローチ (まだ完璧には程遠いですが)
正規表現を読み取るにはツールが必要であり、正規表現を保守しやすくする方法を考えるには賢い人が必要ですが、ほとんどの人が正規表現が今後も存続すると考えているということは興味深いことです。流暢なインターフェイスが最善の方法であるかどうかはわかりませんが、賢いエンジニアの中には、そう考えている人もいると思います。;) - 正規表現を過去のものにするために時間を費やす必要があります - 彼らが 50 年間私たちと一緒にいるだけで十分だと思いませんか?
オープンバウンティ
報奨金は、正規表現への新しいアプローチに関する最高のアイデア (コードは必要ありません) に授与されます。
編集 - 良い例:
私が話しているのはこのようなパターンです - 最初に翻訳できた人に特別な称賛を送ります - RegexBuddies は許可されています (ちなみにこれは Apache プロジェクトからのものです) 特別な栄誉を与えられたのは ちぃ そして メズ:これは RFC に準拠した電子メール アドレス検証パターンですが、 RFC822 (見る 元parrot.com)、 ない 5322 - 違いがあるかどうかはわかりませんが、違いがある場合は、5322 に適合するパッチを次回追加で賞賛します ;)
private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
+ ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
+ "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
+ "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
+ "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
+ "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
+ "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
+ "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
+ "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
+ "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
+ "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
+ "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
+ " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
+ "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
+ ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
+ " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
+ ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
+ ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
+ "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
+ "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
+ "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
+ "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
+ "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
+ "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
+ "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
+ ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
+ ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
+ ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
+ "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
+ "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
+ "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
+ "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
+ "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
+ ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
+ "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
+ ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
+ "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
+ "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
+ "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
+ ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
+ "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
+ ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
+ "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
+ "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
+ "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
+ "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
+ "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
+ "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
+ ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
+ ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
+ ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
+ "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
+ "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
+ "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
+ "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
+ "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
+ "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
+ "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
+ "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
+ "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
+ "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
+ ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
+ "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
+ "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
+ "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
+ "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
+ "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
+ "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
+ "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
+ " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
+ ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
+ ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
+ "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
+ "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
+ "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
+ "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
+ "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
+ "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
+ "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
+ ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
+ "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
+ "?:\\r\\n)?[ \\t])*))*)?;\\s*)";
解決
API はどのように設計しますか?
Hibernate criteria API からページを借用します。代わりに以下を使用します。
string("a").anyTimes().or().string("b").times(2,5).compile()
次のようなパターンを使用します。
Pattern.or(Pattern.anyTimes("a"), Pattern.times("b", 2, 5)).compile()
この表記の方がもう少し簡潔で、パターンの階層・構造が理解しやすい気がします。各メソッドは、最初の引数として文字列またはパターンのフラグメントを受け入れることができます。
正規表現に対する同様のアプローチをご存知ですか?
いいえ、不謹慎ではありません。
このアプローチは単純な文字列を使用するよりも優れている可能性があることに同意しますか?
はい、絶対に...リモートで複雑な何かに正規表現を使用している場合。非常に短く単純な場合には、文字列の方が便利です。
あなたのプロジェクトでこのような優れたユーティリティを使用したいと思いますか?
おそらく、それが証明され、安定したものになったので...Apache Commons のような大規模なユーティリティ プロジェクトにそれを組み込むことはプラスになるかもしれません。
これを実装すると面白いと思いますか?;)
+1
他のヒント
Martin Fowler氏は、別の戦略を示唆しています。すなわち、正規表現の意味のある部分を取り、変数によってそれらを交換。彼は、次の例を使用します:
"^score\s+(\d+)\s+for\s+(\d+)\s+nights?\s+at\s+(.*)"
タグになり
String scoreKeyword = "^score\s+";
String numberOfPoints = "(\d+)";
String forKeyword = "\s+for\s+";
String numberOfNights = "(\d+)";
String nightsAtKeyword = "\s+nights?\s+at\s+";
String hotelName = "(.*)";
String pattern = scoreKeyword + numberOfPoints +
forKeyword + numberOfNights + nightsAtKeyword + hotelName;
はるかに読みやすく、保守している前の例の目標は、のような文字列のリストのうちnumberOfPoints、numberOfNightsとhotelNameを解析するために、
だっます:
score 400 for 2 nights at Minas Tirith Airport
これは任意の正規表現経験のない人のため、わずかに簡単かもしれないが、誰かがあなたのシステムを学習した後、彼はまだ他の場所で、通常の正規表現を読み取ることができなくなります。
また、私はあなたのバージョンが正規表現の専門家のために読み取ることが難しいと思います。
私はこのような正規表現に注釈をお勧めします。
Pattern pattern = Pattern.compile(
"a* # Find 0 or more a \n" +
"| # ... or ... \n" +
"b{2,5} # Find between 2 and 5 b \n",
Pattern.COMMENTS);
これは、任意の経験レベルのために読みやすいですし、経験の浅いのために、それは同時に、正規表現を教えています。また、コメントではなく、単に構造よりも、正規表現の背後にあるビジネスルールを説明するために、状況に合わせて調整することができます。
また、 RegexBuddy のようなツールは、あなたの正規表現を取り、にそれを翻訳することができます:
Match either the regular expression below (attempting the next alternative only if this one fails) «a*» Match the character "a" literally «a*» Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*» Or match regular expression number 2 below (the entire match attempt fails if this one fails to match) «b{2,5}» Match the character "b" literally «b{2,5}» Between 2 and 5 times, as many times as possible, giving back as needed (greedy) «{2,5}»
これは興味深い概念ですが、提示されている時点ではいくつかの欠陥があります。
しかし、尋ねられる重要な質問に対する最初の答えは次のとおりです。
さて、私の質問は次のとおりです。
1.正規表現に対する同様のアプローチをご存知ですか?
まだ言及されていないものはありません。そして、私が質問と回答を読んで知ったものです。
2.このアプローチは単純な文字列を使用するよりも優れている可能性があることに同意しますか?
宣伝どおりに機能すれば、デバッグがはるかに簡単になることは間違いありません。
3.API はどのように設計しますか?
次のセクションの私のメモを参照してください。あなたの例とリンクされた .NET ライブラリを出発点として取り上げます。
4.あなたのプロジェクトでこのような優れたユーティリティを使用したいと思いますか?
未定。現在のバージョンの不可解な正規表現を問題なく使用できます。また、既存の正規表現を流暢な言語バージョンに変換するツールも必要になります。
5.これを実装すると面白いと思いますか?;)
実際のコードを書くよりも、より高度なレベルの方法を検討することを楽しみます。これは、この回答というテキストの壁を説明しています。
ここでは、私が気づいたいくつかの問題と、それに対処する方法を紹介します。
不明瞭な構造。
あなたの例は、文字列に連結することによって正規表現を作成しているようです。これはあまり堅牢ではありません。私は、これらのメソッドを String オブジェクトと Patern/Regex オブジェクトに追加する必要があると考えています。これにより、実装とコードがよりクリーンになるからです。さらに、これは正規表現が古典的に定義される方法と似ています。
他の方法で動作することがわからないという理由だけで、提案されたスキームに対する残りのアノテーションは、すべてのメソッドが動作し、Pattern オブジェクトを返すと想定します。
編集: 全体を通して次の規則を使用しているようです。そこで、それらを明確にしてここに移動しました。
インスタンスメソッド:パターンの増強。例えば:アサーションをキャプチャし、繰り返し、見回します。
オペレーター:操作の順序。交互、連結
定数:文字クラス、境界 (\w、$、\b などの代わり)
キャプチャ/クラスタリングはどのように処理されますか?
キャプチャは正規表現の大部分を占めます。
各 Pattern オブジェクトが内部的にクラスターとして保存されていることがわかります。(?:パターン)Perl用語で。他のピースに干渉することなく、パターン トークンを簡単に混合したり混ぜ合わせることができます。
キャプチャはパターンのインスタンス メソッドとして実行されることを期待しています。一致する文字列を格納する変数を取得します。
pattern.capture(variable)
パターンを変数に格納します。キャプチャが複数回一致する式の一部である場合、変数にはパターンに一致するすべての文字列の配列が含まれている必要があります。
流暢な言語は非常に曖昧な場合があります。
Fluent 言語は、正規表現の再帰的な性質にはあまり適していません。したがって、操作の順序を考慮する必要があります。メソッドを連結するだけでは、非常に複雑な正規表現を使用できません。まさにこのようなツールが役立つ状況です。
する
Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();
生産する /a*|b{2,5}/
または /(a*|b){2,5}/
?
このようなスキームは入れ子になった変更をどのように処理するのでしょうか?例えば: /a*|b(c|d)|e/
正規表現で代替を処理する 3 つの方法がわかります
- オペレーターとして:
pattern1 or pattern2 => pattern # /pattern1|pattern2/
- クラスメソッドとして:
Pattern.or( pattern1, pattern2[, pattern3]*) => pattern # /pattern1|patern2|patern3|...|/
- インスタンスメソッドとして:
pattern1.or(pattern2) => pattern # /pattern1|patern2/
連結も同じように処理します。
- オペレーターとして:
pattern1 + pattern2 => pattern # /pattern1pattern2/
- クラスメソッドとして:
Pattern.concatenate( pattern1, pattern2[, pattern3]*) => pattern # /pattern1patern2patern3.../
- インスタンスメソッドとして:
pattern1.then(pattern2) => pattern # /pattern1patern2/
よく使用されるパターンに拡張する方法
使用された提案スキーム .domain()
これは一般的な正規表現のようです。ユーザー定義のパターンをメソッドとして扱うと、新しいパターンを追加するのが簡単になりません。Java のような言語では、ライブラリのユーザーはクラスをオーバーライドして、よく使用されるパターンのメソッドを追加する必要があります。
これは、すべてのピースをオブジェクトとして扱うという私の提案によって解決されます。パターン オブジェクトは、ドメインの一致など、一般的に使用される正規表現ごとに作成できます。キャプチャに関するこれまでの考えを考慮すると、キャプチャされたセクションを含む同じ共通パターンの複数のコピーに対してキャプチャが機能することを確認することは、それほど難しくありません。
さまざまな文字クラスに一致するパターンの定数も必要です。
ゼロ幅ルックアラウンドアサーション
すべての部分が暗黙的にクラスター化されるべきであるという私の考えを拡張します。アサーションを見て回るのは、インスタンス メソッドを使用する場合と同様にそれほど難しくありません。
pattern.zeroWidthLookBehind()
生み出すだろう (?<patten)
.
まだまだ検討が必要なこと。
- 後方参照:うまくいけば、前に説明した名前付きキャプチャはそれほど難しくありません
- 実際に実装する方法。内部のことはあまり考えていません。ここで本当の魔法が起こります。
- 翻訳:古典的な正規表現 (Perl 方言など) と新しいスキームを相互に変換するツールが実際にあるはずです。新しいスキームからの翻訳はパッケージの一部になる可能性があります
これらすべてをまとめると、電子メール アドレスに一致するパターンの私の提案バージョンは次のようになります。
Pattern domain_label = LETTER_CHARACTER + (LETTER_CHARACTER or "-" or DIGIT_CHARACTER).anyTimes()
Pattern domain = domain_label + ("." + domain_label).anyTimes()
Pattern pattern = (LETTER_CHARACTER + ALPHANUMERIC_CHARACTER + "@" + domain).compile
今にして思えば、私の計画は、Martin Fowler が使用しているアプローチから大きく借用しています。私は物事がこのように進むことを意図していませんでしたが、このようなシステムを使用することで保守性が向上することは間違いありません。また、ファウラーのアプローチ (順序の捕捉) に関する 1 つか 2 つの問題にも対処します。
私自身のささやかな試みは、次のサイトでご覧いただけます。 GitHub. 。単純な式に使用する価値はないと思いますが、読みやすさの向上とは別に、いくつかの利点があります。
- ブラケットのマッチングを処理します
- バックスラッシュ地獄にすぐにつながる可能性のあるすべての「特殊」文字のエスケープを処理します。
いくつかの簡単な例:
// Matches a single digit
RegExBuilder.build(anyDigit()); // "[0-9]"
// Matches exactly 2 digits
RegExBuilder.build(exactly(2).of(anyDigit())); // "[0-9]{2}"
// Matches between 2 and 4 letters
RegExBuilder.build(between(2,4).of(anyLetter())); // "[a-zA-Z]{2,4}"
そして、より複雑なもの (多かれ少なかれ電子メール アドレスを検証します):
final Token ALPHA_NUM = anyOneOf(range('A','Z'), range('a','z'), range('0','9'));
final Token ALPHA_NUM_HYPEN_UNDERSCORE = anyOneOf(characters('_','-'), range('A','Z'), range('a','z'), range('0','9'));
String regexText = RegExBuilder.build(
// Before the '@' symbol we can have letters, numbers, underscores and hyphens anywhere
oneOrMore().of(
ALPHA_NUM_HYPEN_UNDERSCORE
),
zeroOrMore().of(
text("."), // Periods are also allowed in the name, but not as the initial character
oneOrMore().of(
ALPHA_NUM_HYPEN_UNDERSCORE
)
),
text("@"),
// Everything else is the domain name - only letters, numbers and periods here
oneOrMore().of(
ALPHA_NUM
),
zeroOrMore().of(
text("."), // Periods must not be the first character in the domain
oneOrMore().of(
ALPHA_NUM
)
),
text("."), // At least one period is required
atLeast(2).of( // Period must be followed by at least 2 letters (this is the TLD)
anyLetter()
)
);
流暢は、.NET用のライブラリに正規表現があります。
短い答えは:。私はそれは私がこれのために考慮すべきものだと思う毛羽立ちやコンパイル角度からアプローチし見てきました。
長い答え: 以下のための会社のIワークは、エンタープライズコンテンツフィルタリングアプリケーションのためのハードウェアベースの正規表現エンジンを作ります。右のネットワークルータに20ギガバイト/秒の速度でアンチウイルスやファイアウォールのアプリケーションを実行しているのではなく、貴重なサーバーまたはプロセッササイクルを取って考えてみてください。ほとんどのアンチウイルス、アンチスパムやファイアウォールアプリの中核で正規表現式の集まりです。
とにかく、正規表現のが書かれている方法は、スキャンのパフォーマンスに大きな影響を与えます。あなたは同じことを行うには、いくつかの異なる方法でregexsを書くことができ、そしていくつかは大幅に高速なパフォーマンスを持っています。私たちは、彼らの表現を維持し、チューニングを支援するために、当社の顧客のためのコンパイラやリンターを書きました。
誰かが理解するために、OPの質問に戻る、むしろまったく新しい構文を定義するよりも、私はリンターを書く(申し訳ありませんが、我々は独自のものです)を切断し、そのに正規表現のを貼り付けますが、従来の正規表現の出力「流暢な英語」を打破しますより良いです。私も一般的な修飾のための相対的なパフォーマンスのチェックや提案を追加したい。
短い答えは、私にとって、それは、ある...あなたはおそらく考慮されなければなりません最初の場所での作業に適したツールます。
それは、標準正規表現よりも読みにくくなりますように正直なところ、いずれの流れるようなインターフェイスを思われます。本当に短い式の場合、流暢なバージョンは冗長ですが、長すぎません。それは読みやすいです。しかし、そう何かのための正規表現が長いということです。
中規模の正規表現、流れるようなインターフェイスは、扱いにくくなるために、。それは読み、難しい、不可能ではないということに十分な長ます。
正規表現は読みにくい(不可能ではないにしても)実際に長い正規表現(すなわち、電子メールアドレス1)については、流暢なバージョンは、10ページ前に読むことができなくなった。
正規表現に対する同様のアプローチをご存知ですか?
いいえ、前の答えを除いて
このアプローチは単純な文字列を使用するよりも優れている可能性があることに同意しますか?
ある意味、構成を表す単一の文字の代わりに、より説明的なマークアップを使用できると思いますが、それで複雑なロジックがより明確になるとは思えません。
API はどのように設計しますか?
各構成要素をメソッド名に変換し、ネストされた関数呼び出しを許可することで、文字列を取得してメソッド名をその文字列に置き換えるのが非常に簡単になります。
私は、その価値の大部分は、「電子メール」、「電話番号」、「X を含まない行」の照合などのユーティリティ関数の堅牢なライブラリを定義することにあると思います。それはカスタマイズ可能です。
あなたのプロジェクトでこのような優れたユーティリティを使用したいと思いますか?
おそらく - ただし、文字列編集をデバッグするよりも関数呼び出しをデバッグする方が簡単な場合、またはすぐに使用できる優れたユーティリティ関数がある場合など、長いものに限ります。
これを実装すると面白いと思いますか?;)
もちろん!
private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
+ ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
+ "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
+ "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
+ "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
+ "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
+ "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
+ "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
+ "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
+ "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
+ "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
+ "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
+ " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
+ "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
+ ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
+ " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
+ ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
+ ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
+ "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
+ "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
+ "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
+ "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
+ "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
+ "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
+ "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
+ ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
+ ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
+ ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
+ "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
+ "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
+ "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
+ "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
+ "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
+ ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
+ "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
+ ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
+ "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
+ "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
+ "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
+ ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
+ "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
+ ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
+ "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
+ "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
+ "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
+ "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
+ "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
+ "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
+ ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
+ ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
+ ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
+ "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
+ "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
+ "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
+ "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
+ "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
+ "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
+ "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
+ "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
+ "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
+ "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
+ ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
+ "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
+ "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
+ "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
+ "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
+ "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
+ "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
+ "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
+ " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
+ ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
+ ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
+ "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
+ "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
+ "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
+ "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
+ "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
+ "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
+ "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
+ ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
+ "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
+ "?:\\r\\n)?[ \\t])*))*)?;\\s*)";
はRFC準拠の電子メールアドレスと一致した:D
正規表現は、有限状態マシンの記述です。古典的なテキスト表現が必ずしも悪いわけではありません。これはコンパクトで、比較的明確であり、かなりよく採用されています。
より適切な表現は状態遷移図である可能性がありますが、ソース コードで使用するのはおそらく難しいでしょう。
1 つの可能性は、さまざまなコンテナーおよびコンバイナー オブジェクトから構築することです。
次のようなものです (これを疑似コードから選択した言語に変えることは、熱心な人のための演習として残されています)。
domainlabel = oneormore(characterclass("a-zA-Z0-9-")) separator = literal(".") domain = sequence(oneormore(sequence(domainlabel, separator)), domainlabel) localpart = oneormore(characterclassnot("@")) emailaddress = sequence(localpart, literal("@"), domain)
上記では、完全な解析には単純な FSM 以上の文法が必要なため、従う必要がある文法に準拠していない多くの電子メール アドレスが有効であると誤って分類されることに注意してください。ただし、有効なアドレスを無効なものとして誤って分類するとは思えません。
[^@]+@([a-zA-Z0-9-]+.)+.([a-zA-Z0-9-]+) に対応する必要があります。
4.あなたのプロジェクトでこのような優れたユーティリティを使用したいと思いますか?
おそらくそうではないでしょう。これは仕事に適したツールを使用しているケースだと思います。ここには次のような素晴らしい答えがいくつかあります。 1579202 ジェレミー・スタインより。私は最近、最近のプロジェクトで正規表現を「取得」しましたが、そのままでも非常に便利であり、適切にコメントされていれば、構文を知っていれば理解できるものであることがわかりました。
「構文を知る」という部分が人々を正規表現に興味を持たせる原因だと思います。理解していない人にとっては正規表現は難解で不可解に見えますが、正規表現は応用コンピュータ サイエンスにおいて強力なツールです (例:ソフトウェアを書いて生計を立てている) 知的な専門家は、ソフトウェアの使い方やソフトウェアの適切な使い方を学ぶべきであり、そうすべきだと私は感じています。
彼らが言うように、「大きな力で大きな責任があります」。私は人々がすべてのためにどこでも正規表現を使用しているのを見てきましたが、構文を徹底的に学ぶのに時間をかけた人によって慎重に使用されています。彼らは非常に役立ちます。私にとって、別のレイヤーを追加することは、ある意味でその目的を無効にするか、少なくともその力を奪うことになると思います。
これは単なる私個人の意見であり、このようなフレームワークを望む人や正規表現を避ける人がどこから来ているかは理解できますが、時間をかけていない人から「正規表現は悪い」という声を聞くのは難しいです。それらを学び、情報に基づいた決定を下すためです。
は、みんなの幸せを作るために(正規表現の巨匠と流体界面の支持者)は、必ず流体インタフェースを出力することができる適切な生の正規表現パターン作り、また、ファクトリメソッドを使用して定期的な正規表現を取り、それのために流体コードを生成します。
何を探してるんですが、見つけることができますここに:を。これは、ウィザードデザインパターンを次の正規表現のbuillderです>
私は最近、これと同じ考え方を持っています。
それを実現するための思想私自身が、その後私が見つけた VerbalExpressionsするます。
レッツ・比較:私はSQLにの流暢のマッピングを考えることができる(N)にHibernate ICriteriaクエリ、としばしば働いています。私がいた(と、まだ午前)、それらについての熱狂が、彼らは、SQLクエリを読みやすくしましたか?いや、もっと反対に、しかし別の利点は、バラ:それはそれらのサブクラスを作成し、独自の抽象化などを作成するために、プログラムで文を構築する方がはるかに簡単になりました。
。私は何になってること済権利た場合、指定した言語の新しいインタフェースを使用していること、やりがいのあることを証明することができますが、それがあまりにも高度とは思いません。 (流暢に結合するのは難しいだろう、いくつかの高度な概念に名前を付けるために分岐する場合-、ルックビハインドで、ネストされた減算文字クラスをキャプチャ)多くの場合、それが読みやすくなることはありません。しかし、同じように、多くの場合、より高い柔軟性の利点は、オーバーヘッド、構文の複雑さの追加上回ります。
可能な代替のリストに追加するには、が近づき、唯一のJavaの文脈のこのアウトを取るために、LINQ構文を考えます。
:ここでは、(ビットが考案)(from
、where
とselect
はLINQのキーワードいる)ように見えることができるものです
// for ^str(aa|bb){3}
from part in mystring
where part startswith "str"
and part hasgroups "aa" or "bb" as first /* "aa" or "bb" in group 'first' */
and part repeats first 3 /* repeat group 'first' 3 times */
select part + "extra" /* can contain complete statement block */
ちょうどラフなアイデアは、私が知っています。 LINQの良いところは、それは、コンパイラ、種類-の言語の言語でによってチェックされていることです。デフォルトでは、LINQは、他のオブジェクト指向言語との互換性がうまく設計された場合、それを作る流暢連鎖構文、と表現することができます。
私は、私は確信して実装することの楽しさだそれのために行くと言っています。
私は、各関数はクエリオブジェクトを返し、(jQueryの、ジャンゴORMに類似)クエリモデルを使用することをお勧め、あなたが一緒にチェーンにそれらをできるようにします。
any("a").some("b").one("@").some(chars).one(".").some(chars) //a*b+@\w+\.\w+
chars
は、任意の文字に合わせてあらかじめ定義された
or
の選択肢を使用することによって達成することができます:
any("a").choice("x", "z") // a(x|z)
各関数の引数は、文字列または別のクエリすることができます。例えば、変数は上記chars
クエリとして定義することができます。
//this one is ascii only
chars = raw("a-zA-Z0-9")
だから、あなたはそれが流暢なクエリシステムを使用することが面倒に感じている場合は、入力として、正規表現文字列を受け取り、「生」の機能を持つことができます。
実際には、これらの機能は、単に生の正規表現を返し、そしてそれらは、単にこれらのRAW正規表現の文字列を連結されて連鎖することができます。
any("a") ---> "a*"
raw("b+") ----> "b+"
one(".") ---> "\."
choice("a", "b") ----> (a|b)
正規表現を流暢な API に置き換えることで大きな効果が得られるかどうかはわかりません。
私は正規表現ウィザードではないことに注意してください (正規表現を作成する必要があるときは、ほぼ毎回ドキュメントを読み直す必要があります)。
流暢な API を使用すると、中程度の複雑さの正規表現 (たとえば、約 50 文字) が必要以上に複雑になり、最終的には読みにくくなります。ただし、 創造 コード補完のおかげで、IDE で正規表現を認識できます。ただし、コードのメンテナンスは一般的にコード開発よりもコストが高くなります。
実際、前の回答で述べたように、あいまいなケースについて話すことなく、新しい正規表現を作成するときに開発者に十分なガイダンスを実際に提供できるほど賢い API を作成できるかどうかさえわかりません。
RFC の正規表現の例について言及しました。私は 99% 確信しています (まだ 1% の希望があります;-))、どの API を使用してもこの例はこれ以上単純にはならないでしょう。逆に、それは読むのがより複雑になるだけです。これは、とにかく正規表現を使用したくない典型的な例です。
正規表現の作成に関してさえ、あいまいなパターンの問題により、流暢な API を使用すると、最初から適切な式が得られることはなく、本当に必要なものが得られるまで何度も変更する必要がある可能性があります。
誤解しないでください。私は流暢なインターフェイスが大好きです。私はそれらを使用するライブラリをいくつか開発し、それらに基づいたいくつかのサードパーティ ライブラリを使用しています (例:Java テスト用の FEST)。しかし、それらがあらゆる問題に対する金槌にはならないと思います。
Java のみを考慮した場合、正規表現に関する主な問題は次のとおりだと思います。 必要なバックスラッシュのエスケープ Java 文字列定数の場合。これが、Java で正規表現を作成して理解することを非常に困難にしている 1 つの点です。したがって、Java 正規表現を強化するための最初のステップは、私にとっては言語の変更です。 グルービーな, 、文字列定数はバックスラッシュをエスケープする必要はありません。
今のところ、これが Java の正規表現を改善するための私の唯一の提案です。