17

私が見た正規表現を使用するコードは、それらをブラックボックスとして使用する傾向があります。

  1. 文字列に入れる
  2. 魔法の正規表現
  3. ひもを出す

小さな変更でも完全に異なる正規表現になることが多いため、これを実稼働コードで使用するのは特に良い考えではないようです。

標準が永続的で不変である場合を除いて、正規表現は物事を行う方法ですか、それとも別の方法を試す方が良いですか?

4

20 に答える 20

27

正規表現が長くて侵入できないため、維持が困難な場合は、コメントする必要があります。

多くの正規表現の実装では、正規表現に空白とコメントを埋め込むことができます。https://www.regular-expressions.info/freespacing.html#parenscomment およびコーディングホラー:正規表現:2つの問題が
ありますを 参照してください。

私が見た正規表現を使用するコードは、ブラックボックスとして使用する傾向があります。

ブラックボックスが抽象化を意味する場合、それがすべてのプログラミングであり、問​​題のあるドメイン(どの種類の文字列を一致させたいか)に集中できるように、難しい部分(文字列の解析)を抽象化しようとします。

小さな変更でも、完全に異なる正規表現になることがよくあります。

これはどのコードにも当てはまります。正規表現をテストして、期待する文字列と一致することを確認している限り、理想的には単体テストで、それらを変更することに自信を持っている必要があります。

編集:本番コードに関するこの回答に対するJeffのコメントもお読みください。

于 2008-09-29T21:33:47.833 に答える
14

必須。

それは本当に正規表現に帰着します。もしそれがこの巨大なモノリシックな表現なら、そうです、保守性の問題です。それらを簡潔に (おそらく分割して) 表現できる場合、またはそれらを理解するのに役立つ優れたコメントやツールがある場合、それらは強力なツールになる可能性があります。

于 2008-09-29T21:31:37.043 に答える
8

使用している言語はわかりませんが、たとえばPerlはxフラグをサポートしているため、エスケープしない限り正規表現ではスペースが無視されるため、複数行に分割してすべてをインラインでコメント化できます。

$foo =~ m{
    (some-thing)          # matches something
    \s*                   # matches any amount of spaces
    (match another thing) # matches something else
}x;

これは、長い正規表現を読みやすくするのに役立ちます。

于 2008-09-29T21:35:19.107 に答える
7

任意の言語の任意のコードに小さな変更を加えると、まったく異なる結果になる可能性があります。それらのいくつかはコンパイルさえ妨げます。

正規表現を「C」、「C#」、「Java」、「Python」、「Perl」、「SQL」、「Ruby」、「awk」などに置き換えてください。実際、同じ質問が表示されます。

正規表現は単なる別の言語であり、ハフマンは文字列照合で効率的になるようにコーディングされています。Java、Perl、PHP、または特にSQLと同様に、各言語には長所と短所があります。生産性を高めるには、作成する(または維持する)ときに、作成している言語を知っておく必要があります。

編集:マイク、正規表現はハフマン符号化されており、一般的なことはまれなことよりも短いです。テキストのリテラル一致は、通常、単一の文字(一致させたい文字)です。特殊文字が存在します-一般的な文字は短いです。(?:)などの特殊な構成は長くなります。これらは、Perl、C ++などの汎用言語で一般的なものと同じではないため、ハフマンコーディングはこの専門分野を対象としていました。

于 2008-09-29T21:34:12.123 に答える
7

正規表現を理解していない場合にのみ、魔法のように思えます。本番コードに小さな変更を加えるだけで大​​きな問題が発生する可能性があるため、私の意見では、正規表現を使用しないのは適切な理由ではありません。徹底的なテストにより、問題が指摘されるはずです。

于 2008-09-29T21:32:45.340 に答える
6

複雑な正規表現は、私にとって忘れられないものです。書いて、テストして、動作したらコメントを書いてください。

ただし、多くの場合、正規表現をより小さな部分に分割して、これらの正規表現を組み合わせた十分に文書化されたコードを作成することができます。しかし、コードで複数行の正規表現を見つけた場合は、それを維持しなければならない人ではないほうがよいでしょう :)

おなじみですね。それは多かれ少なかれ、どのコードにも当てはまります。メソッドやクラスははるかに簡単にリファクタリングできますが、非常に長いメソッド、非常に長いクラス、および非常に長い正規表現は望ましくありません。しかし、本質的には、それは同じ概念です。

于 2008-09-29T21:31:52.253 に答える
3

正規表現は、何かを行う唯一の方法ではありません。正規表現でできることはすべて、コードで論理的に行うことができます。正規表現はただ

  1. 速い
  2. テスト済みで実証済み
  3. パワフル
于 2008-09-29T21:31:45.810 に答える
3

によって導入された新しい機能を利用すれば、正規表現は非常にPerl 5.10保守しやすくなります。私が参照する機能は、 からバックポートされた機能ですPerl 6

perlretutから直接コピーした例。

名前付きパターンの定義

一部の正規表現では、いくつかの場所で同一のサブパターンが使用されています。Perl 5.10 から、パターンのセクションで名前付きサブパターンを定義して、パターン内のどこでも名前で呼び出すことができるようになりました。この定義グループの構文パターンは です(?(DEFINE)(?<name>pattern)...)。名前付きパターンの挿入は のように記述され(?&name)ます。

以下の例は、前述の浮動小数点数のパターンを使用してこの機能を示しています。複数回使用される 3 つのサブパターンは、オプションの符号、整数の数字列、および小数です。DEFINEパターンの最後のグループには、それらの定義が含まれています。小数パターンは、整数パターンを再利用できる最初の場所であることに注意してください。

/^
  (?&osg)\ * ( (?&int)(?&dec)? | (?&dec) )
        (?: [eE](?&osg)(?&int) )?
 $
 (?(DEFINE)
     (?<osg>[-+]?)         # optional sign
     (?<int>\d++)          # integer
     (?<dec>\.(?&int))     # decimal fraction
 )
/x
于 2008-10-16T04:23:25.377 に答える
2

意識的に使用する場合、正規表現は、テキスト解析の可能性のある行や行からあなたを救う強力なメカニズムです。もちろん、最初の仮定がまだ有効であるかどうかを検証し、それ以外の場合はそれに応じて更新するために、正しく文書化して効率的に追跡する必要があります。メンテナンスに関して、IMHOは、コードの解析行や行、または正規表現の目的を理解するよりも、コードの1行(正規表現パターン)を変更する方が適切です。

于 2008-09-29T21:33:40.703 に答える
2

正規表現は物事を行う方法ですか?タスクによって異なります。

プログラミングのすべてのものと同様に、難しい答えや速い答えはありません。

正規表現が特定のタスクをすばやく簡単に解決する場合は、より詳細な解決策よりも優れている可能性があります。

正規表現が複雑なタスクを実行しようとしている場合は、より冗長なものを理解して維持する方が簡単な場合があります。

于 2008-09-29T21:35:36.173 に答える
2

正規表現に関する有名な引用:

「問題に直面したとき、「分かった、正規表現を使用する」と考える人がいます。今、彼らは2つの問題を抱えています。」-- ジェイミー・ザウィンスキー

正規表現を使用する場合、それらは保守可能であることがわかりますが、それらは特別な場合に使用されます。通常、ほとんどすべてを行うには、正規表現以外のより優れた方法があります。

于 2008-09-29T21:32:13.593 に答える
2

RegEx をより保守しやすくする可能性はたくさんあります。結局のところ、これは (良い?) プログラマーが大きな (場合によっては小さな) 変更を行うときに学ばなければならないテクニックにすぎません。本当に優れたプロがいなければ、構文が複雑なため、誰も気にしませんでした。しかし、彼らは仕事をする上で速く、コンパクトで、非常に柔軟です.

.NET People の場合は、" Linq to RegEx " ライブラリよりも悪い外観または " Readable Regular Expressions Library " が存在する可能性があります。それはそれらをより維持しやすくし、さらに書きやすくします。私はそれらの両方を自分のプロジェクトで使用しました。私が分析したhtmlソースコードはいつでも変更される可能性があることを知っていました。

しかし、私を信じてください。あなたがそれらに夢中になると、それらは書いたり読んだりするのを楽しくすることさえできます. :)

于 2008-09-29T21:57:33.347 に答える
1

私は、重要な正規表現に徹底的にコメントするという方針を持っています。それは、それ自体と一致しない各原子を記述して正当化することを意味します。一部の言語(たとえばPython)は、空白を無視してコメントを許可する「詳細な」正規表現を提供します。可能な限りこれを使用してください。それ以外の場合は、正規表現の上のコメントでアトムごとに移動します。

于 2008-09-29T21:37:13.707 に答える
1

あなたの質問は正規表現自体には関係ないようですが、正規表現を表現するため一般的に使用される構文のみです。多くの筋金入りのコーダーの間では、この構文は非常に簡潔で強力なものとして受け入れられるようになりましたが、長い正規表現の場合、実際には非常に読みにくく、保守も困難です。

一部の人々は、Perl の「x」フラグについてすでに言及していますが、これは少しは役に立ちますが、あまり役に立ちません。

私は正規表現が大好きですが、構文は好きではありません。読みやすく意味のあるメソッド名から正規表現を構築できるとよいでしょう。たとえば、次の C# コードの代わりに:

foreach (var match in Regex.Matches(input, @"-?(?<number>\d+)"))
{
    Console.WriteLine(match.Groups["number"].Value);
}

はるかに冗長ですが、読みやすく保守しやすいものを作成できます。

int number = 0;
Regex r = Regex.Char('-').Optional().Then(
    Regex.Digit().OneOrMore().Capture(c => number = int.Parse(c))
);
foreach (var match in r.Matches(input))
{
    Console.WriteLine(number);
}

これは簡単なアイデアです。私は、これとは無関係の保守性の問題が他にもあることを知っています (ただし、それらは少なく、よりマイナーなものであると主張します)。これの追加の利点は、コンパイル時の検証です。

もちろん、これが行き過ぎで冗長すぎると思われる場合は、おそらくその中間の正規表現構文を使用することもできます...

instead of:   -?(?<number>\d+)
could have:   ("-" or "") + (number = digit * [1..])

これでも 100 万倍は読みやすく、長さは 2 倍しかありません。このような構文は、通常の正規表現と同じ表現力を持つように簡単に作成でき、静的解析のためにプログラミング言語のコンパイラに確実に統合できます。

プログラミング言語全体が再考されたとしても (たとえば、Perl 6 や C# が新しくなったとき)、正規表現の構文を再考することに反対する人が多い理由はよくわかりません。さらに、上記の非常に冗長な考え方は、「古い」正規表現と互換性がないわけではありません。この API は、内部で古いスタイルの正規表現を構築する API として簡単に実装できます。

于 2010-04-19T10:57:41.550 に答える
1

問題は正規表現そのものではなく、ブラック ボックスとしての扱いにあります。他のプログラミング言語と同様に、保守性は、言語そのものよりも、それを書いた人や読む人に関係があります。

仕事に適したツールを使用することについても、多くのことが言えます。元の投稿へのコメントで言及した例では、PerlMonks で頻繁に言及されているように、HTML の解析に使用する正規表現は間違ったツールです。正規表現のみを使用して、一般的な方法に似た方法で HTML を解析しようとすると、正しくない壊れやすい方法で実行するか、恐ろしく保守不可能な正規表現を作成するか、または (ほとんどの場合)両方。

于 2008-09-29T21:54:08.533 に答える
0

私は常にこの問題にビルディングブロックの問題として取り組んできました。

3000文字の正規表現を書くだけでなく、最高のものを期待します。一緒に追加する小さなチャンクの束を書きます。

たとえば、URIを照合するには、プロトコル、権限、サブドメイン、ドメイン、tld、パス、引数(少なくとも)があります。そして、これらのいくつかはオプションです!

あなたはそれを処理するために1つのモンスターを書くことができると確信していますが、チャンクを書いてそれらを一緒に追加する方が簡単です。

于 2008-10-10T21:02:04.677 に答える
0

私はアプリでそれらを使用していますが、実際の正規表現を構成ファイルに保持しているため、解析しているソーステキスト(電子メールなど)が何らかの理由で形式を変更した場合、構成をすばやく更新して、変更を再処理せずに処理できます。アプリの構築。

于 2008-09-29T21:34:24.977 に答える
0

正規表現は確かに「書き込み専用」プログラミング言語と呼ばれています。しかし、それはあなたがそれらを避けるべきだという意味ではないと思います。私はあなたが彼らの意図から地獄をコメントするべきだと思います。私は通常、行の機能を説明するコメントの大ファンではありませんそのためのコードを読むことはできますが、正規表現は例外です。すべてコメントしてください!

于 2008-09-29T21:35:23.693 に答える
0

私は通常、スキャナの仕様ファイルを作成する程度に行きます。スキャナー、または「スキャナー ジェネレーター」は、本質的に最適化されたテキスト パーサーです。私は通常 Java を使用しているので、JFlex ( http://www.jflex.de ) を好む方法ですが、Lex や YACC などもいくつかあります。

スキャナーは、マクロとして定義できる正規表現で動作します。次に、正規表現がテキストの一部と一致したときにコールバックを実装します。

コードに関して言えば、すべての解析ロジックを含む仕様ファイルがあります。選択したスキャナー ジェネレーター ツールを使用して実行し、選択した言語でソース コードを生成します。次に、それらすべてを何らかのパーサー関数またはクラスにラップするだけです。この抽象化により、すべての正規表現ロジックの管理が容易になり、非常に優れたパフォーマンスが得られます。もちろん、正規表現を 1 つまたは 2 つしか使用していない場合はやり過ぎです。何が起こっているのかを理解するのに少なくとも 2 ~ 3 日はかかりますが、たとえば 5 つまたは 6 つまたは 30 の正規表現を使用する場合は、それらのうち、それは非常に優れた機能になり、解析ロジックの実装は数分で開始され、保守と文書化が容易になります.

于 2008-09-29T21:45:37.273 に答える
0

私は通常、正規表現をコメント付きの断片に分割し、最終的なプッシュのためにそれらをすべてまとめます。ピースは部分文字列または配列要素のいずれかです

2 つの PHP PCRE の例 (詳細や特定の用途は重要ではありません):

1)
  $dktpat = '/^[^a-z0-9]*'. // skip any initial non-digits
    '([a-z0-9]:)?'. // division within the district
    '(\d+)'. // year
    '((-)|-?([a-z][a-z])-?)'. // type of court if any - cv, bk, etc.
    '(\d+)'. // docket sequence number
    '[^0-9]*$/i'; // ignore anything after the sequence number
  if (preg_match($dktpat,$DocketID,$m)) {

2)
    $pat= array (
      'Row'        => '\s*(\d*)',
      'Parties'    => '(.*)',
      'CourtID'    => '<a[^>]*>([a-z]*)</a>',
      'CaseNo'     => '<a[^>]*>([a-z0-9:\-]*)</a>',
      'FirstFiled' => '([0-9\/]*)',
      'NOS'        => '(\d*)',
      'CaseClosed' => '([0-9\/]*)',
      'CaseTitle'  => '(.*)',
    );
    // wrap terms in table syntax
    $pat = '#<tr>(<td[^>]*>'.
      implode('</td>)(</tr><tr>)?(<td[^>]*>',$pat).
      '</td>)</tr>#iUx';
    if (preg_match_all ($pat,$this->DocketText,$matches, PREG_PATTERN_ORDER))
于 2008-11-11T20:19:03.303 に答える