知っておくべき正規表現 - 埋め込み条件

目次

1. 条件を埋め込む理由

次に、正規表現の条件

1. バックリファレンス条件

2. 状況を見てみる


1. 条件を埋め込む理由

        (123)456-7890 および 123-456-7890 は北米で許容される電話番号形式ですが、1234567890、(123)-456-7890、および (123-456-7890) にはすべて正しい数の数字が含まれていますが、形式がすべて間違っています。許容可能な形式のみに一致する正規表現を作成したい場合は、次のような最も簡単な解決策を考えることができます。

mysql> set @s:='123-456-7890
    '> (123)456-7890
    '> (123)-456-7890
    '> (123-456-7890
    '> 1234567890
    '> 123 456 7890';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='\\(?\\d{3}\\)?-?\\d{3}-\\d{4}';
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          |
+------+---------------------------------------------------------+------------+
|    4 | 123-456-7890,(123)456-7890,(123)-456-7890,(123-456-7890 | 1,14,28,43 |
+------+---------------------------------------------------------+------------+
1 row in set (0.00 sec)
    \(? 匹配一个可选的左括号。注意,这里必须对 ( 进行转义,\d{3} 匹配前3位数字,
\)? 匹配一个可选的右括号,-? 匹配一个可选的连字符,\d{3}-\d{4} 匹配剩余的 7 位数字
(中间用一个连字符分隔)。该模式没有匹配到最后两行,这是正确的,但匹配到了第3行和第4行,
这就不正确了(第3行的)后面多了一个-,第4行少了一个配对的))。

        \)?-? を [\)-]? に置き換えると、行 3 (出現のみが許可される) または - (同時に両方は不可) が除外されますが、行 4 は依然として除外できません。正しいパターンは、電話番号にパターンが含まれている場合にのみ一致する必要があります。より正確には、電話番号に () が含まれている場合はパターンが一致する必要があり、そうでない場合はパターンが - と一致する必要があります。このパターンは、条件付き処理を使用せずに記述することはできません。すべての正規表現実装が条件付き処理をサポートしているわけではありません。MySQL の正規表現には条件付き処理がまだ実装されていないため、次のようなエラーが発生します。

ERROR 3690 (HY000): The regular expression contains a feature that is not implemented in this library version.

次に、正規表現の条件

        正規表現の条件は?で定義されます。? 前の文字または式が存在する場合、それと一致します。?= および ?<= は、前後のテキスト (存在する場合) と一致します。埋め込まれた条件構文でも ? が使用されますが、これは驚くべきことではありません。埋め込まれた条件は、後方参照に基づく条件処理と、ルックアラウンドに基づく条件処理の 2 つのケースにすぎないからです。

1. バックリファレンス条件

        後方参照条件を使用すると、前の部分式が一致した場合にのみ別の式を使用できます。わかりにくいように思えますが、例を使って説明します: テキスト内のすべての <img> タグを見つけます。それだけでなく、<img> タグがリンク (<a> と </a の間にある場合) の場合も検出します。 > 間のタグ)、リンク タグ全体も一致する必要があります。このような条件を定義するために使用される構文は (?(backreference)true) です。ここで、? はこれが条件であることを示し、括弧内の後方参照は後方参照であり、式は後方参照がすぐに発生する場合にのみ評価されます。

set @s:='<!-- Nav bar -->
<div>
<a href="/home"><img src="/images/home.gif"></a>
<img src="/images/spacer.gif">
<a href="/search"><img src="/images/search.gif"></a>
<img src="/images/spacer.gif">
<a href="/help"><img src="/images/help.gif"></a>
</div>';
set @r:='(<[Aa]\\s+[^>]+>\\s*)?<[Ii][Mm][Gg]\\s+[^>]+>(?(1)\\s*<\\/[Aa]>)';

        (<[Aa]\s+[^>]+>\s*)? は、<A> または <a> タグ (および存在する可能性のある属性) と一致しますが、これはオプションです (部分式が終わり?)。次に、<[Ii][Mm][Gg]\s+[^>]+> は、<img> タグ (大文字または小文字) およびその属性のいずれかと一致します。(?(1)\s*<\/[Aa]>) の開始部分は条件です: ?(1) は、最初の後方参照 (<A> タグ) が存在する場合にのみ、引き続き \s * と一致することを意味します。 <\/[Aa]>、つまり、最初の <A> タグが正常に一致した場合にのみ、後続の一致が実行されます。(1) が存在する場合、\s*<\/[Aa]> は、終了タグ </A> の前にある任意の空白文字と一致します。

        ?(1) 最初の後方参照が存在するかどうかを確認します。条件では、後方参照番号 (この例では 1) をエスケープする必要はありません。したがって、?(1) は正しく、?(\1) は正しくありません (ただし、通常は後者も機能します)。先ほど使用したパターンは、指定された条件が満たされた場合にのみ式を実行します。条件には、指定された後方参照が存在しない (つまり、条件を満たさない) 場合にのみ実行される else 式を含めることもできます。このような条件を定義するために使用される構文は (?(backreference)true|false) です。この構文は、条件と、条件が満たされた場合または満たされなかった場合に実行される 2 つの式を受け入れます。この構文は、電話番号の問題の解決策を提供します。

set @s:='123-456-7890
(123)456-7890
(123)-456-7890
(123-456-7890
1234567890
123 456 7890';
set @r:='(\\()?\\d{3}(?(1)\\)|-)\\d{3}-\\d{4}';

        (\()? は左括弧のチェックを担当しますが、今回は括弧内に入れているので部分式を取得します。続く \d{3} は 3 桁の市外局番と一致します。条件が満たされるかどうかによって異なります。 , (?(1)\)|-) は ) または - と一致します。(1) が存在する場合 (つまり、左括弧が見つかった場合)、それは \) と一致する必要があり、それ以外の場合は - と一致する必要があります。このように、括弧はペアでのみ使用できます。括弧を使用しない場合は、電話市外局番と残りの数字の間の区切り文字 - が一致する必要があります。4 行目には左括弧 (対応する閉じ括弧がない) があるため、埋め込まれた条件は無関係なテキストとして扱われ、完全に無視されます。

        MySQL 正規表現はまだ埋め込み条件をサポートしていません。埋め込み条件を実現するには、条件を満たすすべての組み合わせを「or」でリストする必要があります。

mysql> set @s:='123-456-7890
    '> (123)456-7890
    '> (123)-456-7890
    '> (123-456-7890
    '> 1234567890
    '> 123 456 7890';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='(\\d{3}-\\d{3}-\\d{4})|([(]\\d{3}[)]\\d{3}-\\d{4})';
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       |
+------+-----------------------------------------+---------+
|    3 | 123-456-7890,(123)456-7890,123-456-7890 | 1,14,44 |
+------+-----------------------------------------+---------+
1 row in set (0.00 sec)

        条件が埋め込まれたパターンは一見非常に複雑に見えるため、デバッグが非常に困難になります。より良いアプローチは、最初にパターン全体の個々のコンポーネントを構築してテストし、それからそれらをまとめることです。

2. 状況を見てみる

        先読み条件を使用すると、先読み操作または後読み操作が成功したかどうかに基づいて式を実行するかどうかを決定できます。ルックアラウンド条件の構文は後方参照条件の構文と似ており、後方参照 (かっこ内の後方参照番号) を完全なルックアラウンド式に置き換えるだけです。

        例として、米国の郵便番号との一致を考えてみましょう。米国の郵便番号には 2 つの形式があり、1 つは 12345 の形式の ZIP コードで、もう 1 つは 12345-6789 の形式の ZIP+4 コードです。ハイフンは ZIP+4 エンコードの場合にのみ必要です。以下に回避策を示します。

mysql> set @s:='11111
    '> 22222
    '> 33333-
    '> 44444-4444';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='\\d{5}(-\\d{4})?';
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         |
+------+------------------------------+-----------+
|    4 | 11111,22222,33333,44444-4444 | 1,7,13,20 |
+------+------------------------------+-----------+
1 row in set (0.00 sec)

        \d{5} は最初の 5 桁と一致します。(-\d{4})? ハイフンと最後の 4 桁 (すべてが表示されるか、まったく表示されない) と一致します。しかし、これらの不正な ZIP エンコードを一致させたくない場合はどうすればよいでしょうか? たとえば、この例の 3 行目には、あってはならないハイフンが末尾にあります。次の例は、ルックアラウンド条件の使用を端的に示しています。

set @r:='\\d{5}(?(?=-)-\\d{4})';

        \d{5} は最初の 5 桁と一致します。次に、(?(?=-)-\d{4}) という形式の条件が続きます。この条件は、先読み ?=- を使用してハイフンを照合します (ただし、消費はしません)。条件が満たされる (ハイフンが存在する) 場合、-\d{4} はハイフンとそれに続く 4 桁の数字に照合します。このようにして、33333- は最終的なマッチング結果から除外されます。ハイフンが含まれているため、指定された基準を満たしていますが、末尾の余分な 4 桁が欠落しています。先読みと後読み (正と負の両方) は、オプションの else 式と同様に条件として使用できます (構文は前に示したものと同じ、つまり |式)。多くの場合、より単純な方法を使用して同様の結果が得られるため、ルックアラウンド条件はあまり使用されません。たとえば、MySQL は次のようにこれを行うことができます。

mysql> set @r:='^\\d{5}(-\\d{4})?$';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_count(@s, @r, 'm') c, regexp_extract(@s, @r, 'm') s, regexp_extract_index(@s, @r, 0, 'm') i; 
+------+------------------------+--------+
| c    | s                      | i      |
+------+------------------------+--------+
|    3 | 11111,22222,44444-4444 | 1,7,20 |
+------+------------------------+--------+
1 row in set (0.00 sec)

おすすめ

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