1

PHPでこの正規表現をpreg_match_allで使用しようとしています

/\d+ (?:<[^>]+>)(?:<[^>]+>)(\S+.*\S+)(?:<[^>]+>)\s*(\S+) (?:L|R)\s*\w* \w*\s*(?:\w+\s*){14}(\d+)\s*(\d)\s*(\d*\xA0*\d{3}\xA0*\d{3})/is

いくつかのデータサンプルがあります:

38 <A NAME="Philip McRae"><A HREF="xtrastats.html#Philip McRae">Philip McRae</A>            C L  OK    58 71 69 49 33 89 71 45 48 69 50 35 32 61   21   3    787 000
43 <A NAME="Alexander Nikulin"><A HREF="xtrastats.html#Alexander Nikulin">Alexander Nikulin</A>       C L  OK    41 68 71 40 28 90 67 29 31 60 31 37 34 50   26   0      0 000 <a href="http://www.hockeydb.com/ihdb/stats/pdisplay.php?pid=78680" target="_blank">HDB</a>
20 <A NAME="Christian Hanson"><A HREF="xtrastats.html#Christian Hanson">Christian Hanson</A>        C R  OK    57 72 71 54 33 79 70 42 45 71 46 40 36 60   25   1    875 000 <a href="http://www.hockeydb.com/ihdb/stats/pdisplay.php?pid=73824" target="_blank">HDB</a>

約1500行になりました。

私はこれを一致させる必要があります:

Philip McRae, C, 21, 3, 787 000 (Name, Position, Age, Contract Lenght, Salary)

コードを実行するたびに、致命的なエラーが発生しました: 30 秒の最大実行時間がエラーを超えました。

いくつかの検索の後、この行をスクリプトの先頭に追加しましたが、それで問題は解決しません

ini_set("pcre.backtrack_limit",10000000);

最適化のためにこの正規表現を手伝ってくれる人はいますか?

よろしく。

パトリック

4

4 に答える 4

3

要件がないため、正規表現を書き直すつもりはありませんが、ここでの主な問題は名前グループです。

(\S+.*\S+)

.*貪欲です。つまり、式の残りの部分が一致することを期待しているものを含め、可能な限り多くを消費し、そこで止まりません。パターン修飾子がある/sため、ドットは改行にも一致.*し、一致を試みる前にファイル全体を消費し\S、長い後戻りの旅を始めることができます。

1 つの解決策は、 を使用して.*遅延を作成することです?。つまり.*?、名前が要素内に含まれていることがわかっているため、グループ全体に対して単純に否定文字クラスを使用できます。

([^<]*)

これで問題は解決するはずですが、この場合はおそらくパターン修飾子を使用したくない/sか、少なくともパターンに行頭アンカーと行末アンカーを追加する必要があります。の使用も制限するようにしてください*

参照してください: 壊滅的な後戻り貪欲に気をつけてください

于 2012-07-04T07:19:48.917 に答える
1

@hakre と @bodhizero

あなたの入力とあなたの助けを借りて、正規表現を次のように変更しました。

\d{1,2}+ (?:<[^>]++>)(?:<[^>]++>)([^<]*+)(?:<[^>]++>)\s*+(\S{1,2}+) (?:L|R)\s*+\w*+ \w*+\s*+(?:\w++\s*+){14}(\d{1,2}+)\s*+(\d)\s*(\d*+.*?\d{0,3}+.*?\d{3}+)(?: <[^>]++>[^<]*+<[^>]++>)*?

結果: 約 2 秒でファイル全体を解析!!!

私はRegexbuddyプログラムを使用しており、大いに役立っています。

2つの答えを出せたらよかったのですが、できません

于 2012-07-05T00:11:30.377 に答える
1

約 1,500 行あるとしても、解決したい問題は各行ごとです。

入力を 1行ずつ処理できれば、問題はすでにかなり軽減されています。

$file = new SplFileObject($path);
foreach ($file as $i => $line) {
    printf("#%'0-4d: %s\n", $i, $line);
}

これは単なる例です。当然のことながら、正規表現エンジン自体は、複数行の修飾子 (m) を使用して同様のことを行うことができます。breakただし、上記の foreach を実行すると、最初の行で直接テストできます。

foreach ($file as $i => $line) {
    printf("#%'0-4d: %s\n", $i, $line);
    $pattern = '(^\d++ <A NAME="([^"]++)"><A HREF="xtrastats.html#Philip McRae">Philip McRae</A>            C L  OK    58 71 69 49 33 89 71 45 48 69 50 35 32 61   21   3    787 000)$';
    $r = preg_match($pattern, $line, $matches);
    if (FALSE === $r) {
        throw new Exception(sprintf("Regex failed (%d)", preg_last_error());
    }
    if (!$r) {
        throw new Exception(sprintf("Pattern does not match."));
    }
    var_dump($matches);
    if ($i > 0) break; # exit foreach after X lines.
}
echo "Done.\n";

この例でわかるように、パターンはまだ完全ではありませんが、完全な行を段階的に置き換えていきます。

また、文字列の先頭 ( ^) と文字列の末尾( ) にアンカーを使用します$

また、所有量指定子 ( +) を使用して、それらが一致しない場合にバックトラックが発生しないようにします (アトミック グループ化に似ていますが、書きやすいです)。

正規表現パターンを段階的に改善し続けます。正規表現がコンパイルされない場合、例外がスローされます。行が一致しない場合も同様です。

しばらくしてから作業を完了し、エラー処理を改善し、長期的には安定した効率的なコードを作成する必要があります。

于 2012-07-04T08:28:08.690 に答える
0

正規表現一致に使用されるデータの量を制限するか、制限を変更する必要がset_time_limitありmemory_limitます。

preg_match_all()は非常に CPU を集中的に使用するため、サーバーの CPU の能力によっては、実行時間とメモリの問題が発生する可能性があります。

1 つの解決策は、これをコードの先頭に追加することです。

set_time_limit(0);
ini_set('memory_limit', '128M');

他のオプションは、スクリプトを制限して、preg_match_all()ページの読み込みごとの一致を少なくすることです。

于 2012-07-04T03:30:42.870 に答える