正規表現は知っていて理解する必要があります - 周りを見回してください

目次

1. サラウンドビューの概要

2. 楽しみにしてください

3. 後ろを振り返る

4. 周囲を否定的に見る


1. サラウンドビューの概要

        まず例を見てみましょう。Web ページのページ タイトルを抽出します。HTML ページのタイトルは、<title> タグと </title> タグの間に表示されるテキストであり、これらのタグは HTML コードの <head> セクションに配置する必要があります。

mysql> set @s:='<head>
    '> <title>Ben Forta\'s Homepage</title>
    '> </head>';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='(?<=<head>[\\s]{0,10}<title>).*(?=</title>[\\s]{0,10}</head>)';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, 'n') c, regexp_extract(@s, @r, 'n') s, regexp_extract_index(@s, @r, 0, 'n') i;
+------+----------------------+------+
| c    | s                    | i    |
+------+----------------------+------+
|    1 | Ben Forta's Homepage | 15   |
+------+----------------------+------+
1 row in set (0.00 sec)

        (?<=<head>[\\s]{0,10}<title>) は後読み操作であり、<title> と一致しますが、消費しません; (?=</title>[\\s] { 0,10}</head>) は先読み操作であり、 </title> と一致しますが、 </title> は消費されません。最終的に返される一致にはヘッダー テキストのみが含まれており、この正規表現で消費されるのはヘッダー テキストだけです。曖昧さを減らすために、この例では < をエスケープする必要があります。つまり、(?<=< を (?<=\< に置き換えます。MySQL 正規表現ルックバックは * をサポートしませんが、間隔範囲はサポートします。

mysql> set @r:='(?<=<head>.*<title>).*(?=</title>.*</head>)';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, 'n') c, regexp_extract(@s, @r, 'n') s, regexp_extract_index(@s, @r, 0, 'n') i;
ERROR 3695 (HY000): The look-behind assertion exceeds the limit in regular expression.

mysql> set @r:='(?<=<head>.{0,}<title>).{0,}(?=</title>.*</head>)';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, 'n') c, regexp_extract(@s, @r, 'n') s, regexp_extract_index(@s, @r, 0, 'n') i;
ERROR 3695 (HY000): The look-behind assertion exceeds the limit in regular expression.

mysql> set @r:='(?<=<head>.{0,100}<title>).*(?=</title>.*</head>)';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, 'n') c, regexp_extract(@s, @r, 'n') s, regexp_extract_index(@s, @r, 0, 'n') i;
+------+----------------------+------+
| c    | s                    | i    |
+------+----------------------+------+
|    1 | Ben Forta's Homepage | 15   |
+------+----------------------+------+
1 row in set (0.00 sec)

2. 楽しみにしてください

        Lookahead は、一致する必要があるが結果として返されないパターンを指定します。Look Above は実際には部分式であり、これは形式の観点から言えば当てはまります。先読みパターンの構文は ?= で始まる部分式で、= の後に照合対象のテキストが続きます。

        一部の正規表現ドキュメントでは、「テキストを照合して返す」という意味で「消費」という用語が使用されています。「消費しない」にマッチしたテキストを楽しみにしています。例を見てみましょう。次のテキストには一連の URL アドレスが含まれており、タスクは各アドレスのプロトコル部分を抽出することです。

mysql> set @s:='http://www.forta.com/ https://mail.forta.com/ ftp://ftp.forta.com/';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='\\w+?(?=:)';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, '') c, regexp_extract(@s, @r, '') s, regexp_extract_index(@s, @r, 0, '') i;
+------+----------------+---------+
| c    | s              | i       |
+------+----------------+---------+
|    3 | http,https,ftp | 1,23,47 |
+------+----------------+---------+
1 row in set (0.00 sec)

        上記の URL アドレスでは、プロトコル名とホスト名が「:」で区切られています。パターン \\w+? は任意の単語に遅延的に一致し、部分表現 (?=:) は: に一致します。ただし、matched: は最終的な一致結果には表示されないことに注意してください。?= は、正規表現エンジンに match: を指示し、この文字を使用せずに前方を参照するだけです。?= の効果をよりよく理解するために、同じ例をもう一度見てみましょう。今回は先読みメタキャラクタを使用しません。

mysql> set @s:='http://www.forta.com/
    '> https://mail.forta.com/
    '> ftp://ftp.forta.com/';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='\\w+?(:)';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, '') c, regexp_extract(@s, @r, '') s, regexp_extract_index(@s, @r, 0, '') i;
+------+-------------------+---------+
| c    | s                 | i       |
+------+-------------------+---------+
|    3 | http:,https:,ftp: | 1,23,47 |
+------+-------------------+---------+
1 row in set (0.00 sec)

        部分式 (:) は正しく一致し、最終的な一致結果の一部として返される : 文字を使用します。これら 2 つの例の違いは、前者では一致するときにパターン (?=:) が使用されるのに対し、後者ではパターン (:) が使用されることです。2 つのパターンは、プロトコル名の直後に続く : という同じものに一致しますが、違いは、一致した : が最終的な一致結果に現れるかどうかです。先読みを使用する場合、正規表現パーサーは先読みして : 一致を処理しますが、最終結果にはそれを含めません。パターン \w+?(:) は : を含むテキストを検索し、パターン \w+?(?=:) は : を含まないテキストを検索します。

        前方検索と後方検索では実際に結果が返されますが、結果は常に長さ 0 の文字列です。したがって、ルックアラウンド操作は、ゼロ幅マッチング操作と呼ばれることもあります。部分式は、先頭に ?= を付けることで先読み式に変換できます。複数の先読み式を同じ検索パターンで使用でき、出現場所に制限はありません。

3. 後ろを振り返る

        先ほど見たように、?= は前方を見て、一致したテキストの後に続くものを調べますが、それを消費しません。したがって、?= は先読み演算子と呼ばれます。前方参照に加えて、多くの正規表現の実装では、後方参照、つまり、一致したテキストの前に表示されるコンテンツの参照もサポートされています。

        ?= と ?<= を区別する 1 つの方法: テキストの後ろを指す矢印 (< 記号) を含む演算子は、後方を向いています。絵柄に対する文字の向き(「前を見る」の「前」に相当)と文字を読む方向が逆になるため、後ろを見たときに<の向きを覚えておくと誤解を招きやすいため、 「?<=」を「look after...」に直接読むことができます。?<= は ?= と同じように使用され、部分式の中に出現し、その後に一致するテキストが続く必要があります。以下に例を示します。製品リストは特定のデータベースから検索されますが、必要なのは製品価格のみです。

mysql> set @s:='ABC01: $23.45
    '> HGG42: $5.31
    '> CFMX1: $899.00
    '> XTC99: $69.96
    '> Total items found: 4';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='\\$[0-9.]+';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, '') c, regexp_extract(@s, @r, '') s, regexp_extract_index(@s, @r, 0, '') i;
+------+-----------------------------+------------+
| c    | s                           | i          |
+------+-----------------------------+------------+
|    4 | $23.45,$5.31,$899.00,$69.96 | 8,22,35,50 |
+------+-----------------------------+------------+
1 row in set (0.00 sec)

        \$ は $ 文字に一致し、[0-9.]+ は価格に一致し、一致結果は正しいです。しかし、最終的な一致結果に $ 文字を表示したくない場合はどうすればよいでしょうか?

mysql> set @r:='(?<=\\$)[0-9.]+';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, '') c, regexp_extract(@s, @r, '') s, regexp_extract_index(@s, @r, 0, '') i;
+------+-------------------------+------------+
| c    | s                       | i          |
+------+-------------------------+------------+
|    4 | 23.45,5.31,899.00,69.96 | 9,23,36,51 |
+------+-------------------------+------------+
1 row in set (0.00 sec)

        問題は解決された。(?<=\$) は $ 文字と一致しますが、それを消費せず、最終的な一致結果には価格番号のみが含まれます。この例の 2 つの式を比較します: \$[0-9.]+ は $ 文字およびドル金額に一致します; (?<=\$)[0-9.]+ は $ 文字およびドル金額にも一致します。2 つのパターンは同じものを検索し、それらの違いが最終的なマッチング結果に反映されます。前者は $ 文字に一致しますが、後者は一致しません。ただし、これらの価格番号を正しく見つけるには $ 文字に一致する必要があります。

        先読みパターンは可変長で、. や + などの量指定子を含めることができるため、非常に柔軟です。Look Backward モードは固定長のみです。ほとんどすべての正規表現の実装にはこの制限があります。

4. 周囲を否定的に見る

        これまでのところ、先読みと後読みは一般にテキストの一致に使用され、主にテキスト内のどこを一致結果として返すかを指定するために使用されてきました (目的の一致の前後のテキストを示します)。この使用法は、ポジティブ先読みおよびポジティブ後読みとして知られています。「肯定」とは、マッチングが行われていることを意味する。あまり一般的ではありませんが、ネガティブルックアラウンドと呼ばれる形式のルックアラウンドもあります。負の先読みでは、指定されたパターンに一致しないテキストを前方に参照し、負の先読みでは、指定されたパターンに一致しないテキストを後方に参照します。参照操作を無効にするには、= の代わりに ! を使用します。次の表に、すべてのルックアラウンド操作を示します。

タイプ

説明する

(?=)

ぜひ楽しみにしていてください

(?!)

否定的な先読み

(?<=)

必ず後ろを向いてください

(?<!)

否定的な検索

        一般に、先読みをサポートする正規表現の実装は、正の先読みと負の先読みもサポートします。同様に、後読みをサポートする正規表現の実装は、正と負の両方の後読みをサポートします。例を見てみましょう。以下は、価格と数量の両方を含む値を含むテキストです。最初に価格を取得しましょう。

mysql> set @s:='I paid $30 for 100 apples,
    '> 50 oranges, and 60 pears.
    '> I saved $5 on this order.';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='(?<=\\$)\\d+';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, '') c, regexp_extract(@s, @r, '') s, regexp_extract_index(@s, @r, 0, '') i;
+------+------+------+
| c    | s    | i    |
+------+------+------+
|    2 | 30,5 | 9,63 |
+------+------+------+
1 row in set (0.00 sec)

        これは前の例と似ています。\d+ は数値に一致します。(?<=\$) は、$ 文字を使用せずに後方検索します (パターンでは \$ としてエスケープされます)。このパターンは、価格を表すために使用される 2 つの数値と正しく一致しましたが、数量を表すために使用された数値は最終的な照合結果には表示されませんでした。次に、その逆を行い、価格ではなく数量のみを見つけます。

mysql> set @r:='\\b(?<!\\$)\\d+\\b';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, '') c, regexp_extract(@s, @r, '') s, regexp_extract_index(@s, @r, 0, '') i;
+------+-----------+----------+
| c    | s         | i        |
+------+-----------+----------+
|    3 | 100,50,60 | 16,28,44 |
+------+-----------+----------+
1 row in set (0.00 sec)

        \d+ は引き続き値と一致しますが、今回は価格ではなく数量のみが一致します。式 (?<!\$) は負の後読みで、数値の前の文字が $ でない場合にのみ一致します。ルックバック演算子の = を ! に変更すると、モードがポジティブ ルックバックからネガティブ ルックバックに変わります。上記のパターンで単語の境界を指定するために \b も使用されるのはなぜですか? 単語の境界を使用していない以下の例を見ると、その理由がわかります。

mysql> set @r:='(?<!\\$)\\d+';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, '') c, regexp_extract(@s, @r, '') s, regexp_extract_index(@s, @r, 0, '') i;
+------+-------------+-------------+
| c    | s           | i           |
+------+-------------+-------------+
|    4 | 0,100,50,60 | 10,16,28,44 |
+------+-------------+-------------+
1 row in set (0.00 sec)

        単語の境界が使用されていないため、$30 の 0 も最終的な一致結果に表示されます。これは、文字 0 の前の文字が $ 文字ではなく 3 であり、パターン (?<!\$)\d+ の一致要件を完全に満たしているためです。パターン全体を単語境界内に配置すると、この問題は解決されます。

        Lookaround を使用すると、最終的な戻り結果をより詳細に制御できます。ルックアラウンド操作では、部分式を使用してテキスト一致操作が行われる場所を指定できますが、同時に、一致したテキストは消費されません (最終的な一致結果には表示されません)。正の先読みは (?=) を使用して定義され、負の先読みは (?!) を使用して定義されます。一部の正規表現実装では、正の後読み (対応する演算子は (?<=)) および負の後読み (対応する演算子は (?<!)) もサポートします。

おすすめ

転載: blog.csdn.net/wzy0623/article/details/130984095