骚气十足的正则表达式(二)

1. 零宽断言

无论是零宽还是断言,听起来都古古怪怪的,
那先解释一下这两个词。

  1. 断言:俗话的断言就是“我断定什么什么”,而正则中的断言,就是说正则可以指明在指定的内容的前面或后面会出现满足指定规则的内容,
    意思正则也可以像人类那样断定什么什么,比如"ss1aa2bb3",正则可以用断言找出aa2前面有ss1,也可以找出aa2后面有bb3.
  2. 零宽:就是没有宽度,在正则中,断言只是匹配位置,不占字符,也就是说,匹配结果里是不会返回断言本身。

意思是讲明白了,那他有什么用呢?
我们来举个栗子:
假设我们要用爬虫抓取csdn里的文章阅读量。通过查看源代码可以看到文章阅读量这个内容是这样的结构

1"<span class="read-count">阅读数:641</span>"

其中也就‘641’这个是变量,也就是说不同文章不同的值,当我们拿到这个字符串时,需要获得这里边的‘641’有很多种办法,但如果正则应该怎么匹配呢?

下面先来讲几种类型的断言:

  1. 正向先行断言(正前瞻):
  • 语法:(?=pattern)
  • 作用:匹配pattern表达式的前面内容,不返回本身。

这样子说,还是一脸懵逼,好吧,回归刚才那个栗子,要取到阅读量,在正则表达式中就意味着要能匹配到‘</span>’前面的数字内容
按照上所说的正向先行断言可以匹配表达式前面的内容,那意思就是:(?=</span>) 就可以匹配到前面的内容了。
匹配什么内容呢?如果要所有内容那就是:

 REG = 1String "+(= </スパン>?)。";
 2
 テスト= "<スパンクラス= \"リードカウント\ ">が3String読み込み:641 </スパン>";
 4Pattern Pattern.compileパターン=( REG);
 5Matcher Pattern.matcher MC =(試験);
 6while(mc.find()){
 7のSystem.out.println:。( "照合結果")
 ); 8のSystem.out.println(mc.group(。)
 9。 }
10
。マッチング結果@ 11:
641:12数読取<= "読み取りCOUNTを"スパンクラス> //であります

しかし、我々はちょうどええ、それは簡単な戦略、D \一致する数字だデジタル、それはに変更することができるの前で兄をしたいです:

1String reg="\\d+(?=</span>)";
2String test = "<span class=\"read-count\">阅读数:641</span>";
3Pattern pattern = Pattern.compile(reg);
4Matcher mc=    pattern.matcher(test);
5while(mc.find()){
6  System.out.println(mc.group());
7}
8//匹配结果:
9//641

大功告成!

  1. 正向后行断言(正后顾):
  • 语法:(?<=pattern)
  • 作用:匹配pattern表达式的后面的内容,不返回本身。

有先行就有后行,先行是匹配前面的内容,那后行就是匹配后面的内容啦。
上面的栗子,我们也可以用后行断言来处理.

 1 //(?<= <スパンクラス= "読み取り回数を">阅读数:)\ D +
 2StringのREG = "(?<= <スパンクラス= \"読み取り回数\ ">阅读数:)\\ D + 「;
 3
 4Stringテスト= "<スパンクラス= \"読み取り回数を\ ">阅读数:641 </ span>の";
 5Patternパターン= Pattern.compile(REG)。
 6Matcher MC = pattern.matcher(テスト)。
 7一方(mc.find()){
 8のSystem.out.println(mc.group())。
 9}
10 //匹配结果:
11 // 641

それは簡単です。

  1. 負の最初のアサーション(負プレビュー)
  • 構文:(?!パターン)
  • 役割:表現の内容の前に非マッチングパターンは、それ自体を返しません。

そこ負のは、ここでは、正、負にもあり、実際に非意味です。
栗の場合:例えば、「私は、私は祖国の花よ、祖国を愛する」がある
今、祖国の前で「花」を見つけることではありません
定期的に書くことができますと:

1つの国(?!花)

  1. 後列アサーション負(負Hougu)
  • 構文:(?<!パターン)
  • 役割:表現の内容の背後にある非マッチングパターンは、それが自分自身を返しません。

2.キャプチャおよび非キャプチャ

それは、単純なキャプチャに来るとき、彼は表現に一致するものではなく、通常のキャプチャとは一緒にグループ化された、または「キャプチャグループ」

その後、あなたはシリアル番号または名前でのマッチング、これらの結果を使用することができ、コンテンツマッチングサブ式、深さ優先の数を実行するためにメモリに一致する数字またはグループのデジタル表示名で結果を保存します。グループをキャプチャします。

そして、命名に依存するだけでなく、それは2つのグループに分けることができます:

  1. 番号キャプチャグループ:
    構文:(式)
    説明:左からの発現、パケット内のパケットの各出現に対応する左右の括弧の間にコンテンツ、グループ0のための全発現、開始パケットの最初のセット。
    020-85653333:そのような固定電話のように
    :自分の正規表現(0 \ D {2} - (\ D {8}))
    :左括弧のために、式は以下の基を有しています

いいえ。

番号

パケット

コンテンツ

0

0

(0 \ D {2}) - (\ D {8})

020-85653333

1

1

(0 \ D {2})

020

2

2

(\ D {8})

85653333

私たちは、検証するためにJavaを使用します。

 1Stringテスト=「020から85653333」。
 2文字列REG = "(0 \\ D {2}) - (\\ D {8})"。
 3パターンのパターン= Pattern.compile(REG)。
 4マッチャMC = pattern.matcher(テスト)。
 5 IF(mc.find()){
 6のSystem.out.println( "分组的个数有:" + mc.groupCount())。
 {(; I <= mc.groupCount()は、i ++は、I = 0の整数)7
 8のSystem.out.println( "第" + I + "个分组为:" + mc.group(I))。
 9}
10}

出力:

パケットの数があります:2
に分類2ページ0:020から85653333
3最初のパケットである:020
4(2)パケットである:85653333

見ること、パケットの数は2であるが、なぜなら全体の発現自体共に0、従って出力されます。

  1. 命名捕捉基:
    構文:(<名前> EXP?)
    説明:指定の名前の式で指定されたパケットを
    書き込むことができる、そのようなコードのように:(<Quhao> \ 0? \ D {2}) - (<? ハオマ> \ D {8})
    左括弧のために、式は以下の基を有します。

いいえ。

名前

パケット

コンテンツ

0

0

(0 \ D {2}) - (\ D {8})

020-85653333

1

quhao

(0 \ D {2})

020

2

ハオマ

(\ D {8})

85653333

確認するために、コードを使用します。

1Stringテスト=「020から85653333」。
2文字列REG = "(?<quhao> 0 \\ D {2}) - (?<ハオマ> \\ D {8})"。
3パターンのパターン= Pattern.compile(REG)。
4マッチャMC = pattern.matcher(テスト)。
5 IF(mc.find()){
6のSystem.out.println( "分组的个数有:" + mc.groupCount())。
7のSystem.out.println(mc.group( "quhao"));
8のSystem.out.println(mc.group( "ハオマ"));
9}

出力:

2:1パケット番号有する
第2のパケット名:quhao、コンテンツマッチ:020
3パケット名:ハオマ、コンテンツをマッチング:85653333

  1. 非キャプチャグループ:
    文法:(:?EXP)
    説明:ちょうど反対し、これらのグループは、キャプチャする必要はありません識別するために使用されているグループを、キャプチャし、彼が人気のポイントは、あなたが必要に応じて、あなたのグループ化を保存するために行くことができるということであると述べました。

例えば、上記の正規表現は、最初のパケットを使用する必要はありません、それは次のように書くことができます。

1(?:\ 0 \ D {2}) - (\ dの{8})

いいえ。

番号

パケット

コンテンツ

0

0

(0 \ D {2}) - (\ D {8})

020-85653333

1

1

(\ D {8})

85653333

確認します。

 1String test = "020-85653333";
 2        String reg="(?:0\\d{2})-(\\d{8})";
 3        Pattern pattern = Pattern.compile(reg);
 4        Matcher mc= pattern.matcher(test);
 5        if(mc.find()){
 6                System.out.println("分组的个数有:"+mc.groupCount());
 7                for(int i=0;i<=mc.groupCount();i++){
 8                    System.out.println("第"+i+"个分组为:"+mc.group(i));
 9                }
10        }

输出结果:

1分组的个数有:1
2第0个分组为:020-85653333
3第1个分组为:85653333

3. 反向引用

上面讲到捕获,我们知道:捕获会返回一个捕获组,这个分组是保存在内存中,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用方式就是反向引用

根据捕获组的命名规则,反向引用可分为:

  1. 数字编号组反向引用:\k

或\number

  1. 命名编号组反向引用:\k

或者\'name'

好了 讲完了,懂吗?不懂!!!
可能连前面讲的捕获有什么用都还不懂吧?
其实只是看完捕获不懂不会用是很正常的!
因为捕获组通常是和反向引用一起使用的

上面说到捕获组是匹配子表达式的内容按序号或者命名保存起来以便使用
注意两个字眼:“内容” 和 “使用”
这里所说的“内容”,是匹配结果,而不是子表达式本身,强调这个有什么用?嗯,先记住
那这里所说的“使用”是怎样使用呢?

因为它的作用主要是用来查找一些重复的内容或者做替换指定字符。

还是举栗子吧:
比如要查找一串字母"aabbbbgbddesddfiid"里成对的字母
如果按照我们之前学到的正则,什么区间啊限定啊断言啊可能是办不到的,
现在我们先用程序思维理一下思路:

  • 1)匹配到一个字母
  • 2)匹配第下一个字母,检查是否和上一个字母是否一样
  • 3)如果一样,则匹配成功,否则失败

这里的思路2中匹配下一个字母时,需要用到上一个字母,那怎么记住上一个字母呢???
这下子捕获就有用处啦,我们可以利用捕获把上一个匹配成功的内容用来作为本次匹配的条件
好了,有思路就要实践
首先匹配一个字母:\w
我们需要做成分组才能捕获,因此写成这样:(\w)

那这个表达式就有一个捕获组:(\w)
然后我们要用这个捕获组作为条件,那就可以:(\w)\1
这样就大功告成了
可能有人不明白了,\1是什么意思呢?
还记得捕获组有两种命名方式吗,一种是是根据捕获分组顺序命名,一种是自定义命名来作为捕获组的命名
在默认情况下都是以数字来命名,而且数字命名的顺序是从1开始的
因此要引用第一个捕获组,根据反向引用的数字命名规则 就需要 \k<1>或者\1
当然,通常都是是后者。
我们来测试一下:

1String test = "aabbbbgbddesddfiid";
2        Pattern pattern = Pattern.compile("(\\w)\\1");
3        Matcher mc= pattern.matcher(test);
4        while(mc.find()){
5            System.out.println(mc.group());
6
7        }

输出结果:

1aa
2bb
3bb
4dd
5dd
6ii

嗯,这就是我们想要的了。
在举个替换的例子,假如想要把字符串中abc换成a

1Stringテスト= "abcbbabcbcgbddesddfiid"。
2String REG = "(A)(B)C"。
3System.out.println(test.replaceAll(REG、 "$ 1"));;

出力:

1abbabcgbddesddfiid

4.貪欲と非欲張り

1. グリード

私たちは皆、同じくらいあなたが望むように、欲を満たしていないことを知っています。
定期的に、貪欲は、ほぼ同じ意味です。

欲の試合:正規表現が重複修飾子が含まれ、貪欲な方法の一致が一致するために呼び出され、通常動作(式全体で前提を一致させることができる)などの多くの文字にマッチし、受け入れることができます。
特徴:一回は文字列全体のマッチングを読んで、彼らは右端の文字を放棄一致しない時はいつでも、一致し続け、試合や廃棄(この試合-また戻って道を放棄と呼ばれる)に続いて、マッチが成功するか、文字全体まで文字列まで廃棄されたので、データは劣らず、より多くのことができる最大限ないためのリターンで終わり。

以前の私たちが繰り返し修飾子の話、実際には、これらの修飾子は、そのような表現として貪欲数量詞は、次のとおりです。

1つの\ D {3,6}

この場合には、数字3-6を一致させるために使用、それは文字列内のすべての6つの数字が一致した場合つまり、それがすべてに一致する貪欲なパターンマッチングは、あります。
として

REG = 1String "\\ D {3,6}";        
2Stringテスト= "61762828 176 871 2991";
3System.out.println( "テキスト:"テスト+);
4System.out.println( "貪欲" + REG);
Pattern.compile P1の5Pattern =(REG);
6Matcher p1.matcher M1 =(試験);
7一方(m1.find()){
8のSystem.out.println( "マッチング結果:" + m1.group( 0));
9}

出力:

テキスト1:44 871 617,628,281,762,991ある
\ D {3,6}:2貪欲
3マッチング:617 628
及び第四のマッチング結果:176件の
5個検索結果:2991件の
6個検索結果:871

これは、結果から見ている:「61762828」この段落は、実際には、唯一正常に一致したが、彼は満足しなかったが、最大値を一致させることができます文字に一致するようにされている3(617)登場の元の文字列は、それが6でありますA。
貪欲な場合に数量詞、そして
より多くの貪欲数量詞が一緒に取得する場合、誰かが、聞いてきます、そしてそれは、彼らはそれに合わせて、自分の権利を過ごすどのようになりますか?

ストリングが互いに妨害することなく、試合のそれぞれの最大次数を満たすために、しかし満たされない場合は、左からそれぞれの右側に、すなわち、深さ優先の原則に基づいてされる場合、そのような場合に貪欲の複数が一緒にすることです貪欲数量は、優先度の最大数、残りの数量詞のマッチの再配分を満たすために。

REG = 1String "(\\ D {1,2})({3,4- \\ D})";        
2Stringテスト= "61762828 87321 176 2991";
3System.out.println( "テキスト:" +試験)。
4System.out.println( "貪欲" + REG);
Pattern.compile P1 =(REG)の5Pattern;
6Matcher p1.matcher M1 =(試験)
。7一方(m1.find()){
8のSystem.out。 println( "マッチング結果:" + m1.group(0));
9}。

出力:

テキスト1:617,628,281,762,991 87321
2貪欲:(\ D {1,2})(\ 3,4- {D})
3マッチング:617の628
及び第四のマッチング結果:2991件の
5個検索結果:87321

  1. \ dの{1,2}一致61、7628に一致するバックの前に "617628"
  2. \ dを{1,2}に一致する29、91と一致するバックの前に "2991"
  3. \ dの{1,2}一致87、321に一致するバックの前に "87321"

2. 怠惰(非欲張り)

レイジー試合:正規表現を受け入れることができ、重複する修飾子が含まれている場合は、通常の動作と一致するように怠惰な方法での試合と呼ばれる数文字としてマッチ(式全体では前提を一致させることができます)。
文字列マッチングの一番左から始まるたびに文字を読んでいないしようとしているが、マッチングが成功すると、左から右へ、マッチングが完了しているから、それ以外の文字を読み取り、一致し、かつそのサイクル(一致、文字を読んで):特長試合まで成功するか、文字列が最後にマッチします。

レイジー数量詞は、「?」背中で貪欲な数量詞を追加することです

コード

説明

*?

何回繰り返して、任意の数が、あまり重複可能な限り

+?

1回以上繰り返し、あまり重複可能な限り

??

可能な限り0または1を繰り返しますが、あまり重複

{N、M}?

m倍にN繰り返したが、以下のように繰り返します

{N}?

N回以上、あまり重複可能な限り

REG = 1String "({3,4- \\ D})(\\ D {1,2}?)";        
2テスト文字列= "61762828 87321 176 2991";
3のSystem.out.println( "テキスト:" +試験);
4のSystem.out.println( "貪欲" + REG)
。Pattern.compileの5パターンP1 =(REG);
6マッチャp1.matcher M1 =(テスト)
。7一方(m1.find()) {
8のSystem.out.println( "マッチング結果:" + m1.group(0))、。
。9}

出力:

テキスト1:617,628,281,762,991 87321
2貪欲:(\ D {1,2}?)(\ 3,4- {D})
3マッチング:61762の
第四のマッチング結果:2991件の
5個検索結果:87321

回答:

「61762」は6一致遅延残され、1762年に一致した貪欲権利は
「2991」が991マッチ2マッチ遅延左、右貪欲である
7321に一致「87321」8一致遅延左、右貪欲を

5.アンチセンス

もちろん、あるものと一致するように、フロントメタ文字で言えば、あなたはいくつかの文字にマッチしたくない、の反対になりたい場合は、定期的にも、いくつかの一般的なアンチセンスメタ文字を提供します。

メタキャラクタ

説明

\ W

任意の文字、数字、アンダースコア、中国語の文字と一致しません

\ S

任意の文字が空白ではないマッチします

\ D

任意の数字以外の文字にマッチします

\ B

位置合わせは、単語の先頭や末尾ではありません

[^ X]

X以外の任意の文字に一致します

[^ AEIOU]

AEIOUの文字以外の任意の文字に一致します

おすすめ

転載: www.cnblogs.com/z1201-x/p/11422070.html