正则表达式教程一 —— 特殊字符(元字符)
2017-11-28 16:44 浏览(1502

概述:

正则表达式,又称规则表达式。是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。

正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

它最初是科学家对人类神经系统工作原理的早期研究(一种用数学方式来描述神经网络的新方法)。

Unix的主要发明人 Ken Thompson 将它运用到了计算搜索算法、编辑器qed、编辑器ed(太古老了),从此被广泛应用


那么它有啥用?

它能帮你在一大堆杂乱无章的内容中,找到符合特定模式的内容

比如,爬取其它网站的内容并提取有用信息;内容关键字审核;特定文本内容整体替换


这一节将介绍一下正则表达式的一些特殊字符和简单用法

在讲这些字符/符号的时候,我会用 php 来举一些例子,基本上所有语言的正则表达式都大同小异,若你使用的是其它语言,也可直接把表达式拿去使用试试


一、特殊字符(元字符)

特殊字符即在正则表达式中有特定含义的内定字符。就好比 java,php 等语言中的关键字


1、$ 

匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r' 

例子1

$s = 'abcd
efd';

preg_match('#[a-z]+?d$#s',$s,$m);//# # 之间的内容为正则表达式,s为修饰符,# 为php中的边界符号,还有 / 也是边界符
print_r($m);//$m 就是我们匹配到的信息
/*
Array
(
    [0] => efd
)
*/

匹配以 d结尾(d$) 的一串字符,前面可以是任意a到z 的小写字母([a-z]+?), 其中 s 为修饰符(后面会讲),就是多行模式(Multiline),

所以匹配到了第二行的 efd(因为[a-z]+?不匹配换行),若要匹配整个换行的字符串,则:#([a-z]|\r\n)+?d$#s


2、( ) 

 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用

例子2

$s = 'abc123def456';

preg_match_all('#(\d+)#',$s,$m);
print_r($m[1]);
/*
Array
(
    [0] => 123
    [1] => 456
)
*/

匹配并捕获字符串中的所有数字,$m[1] 就是捕获到的内容,关于捕获后的使用,将在下面的 \ 符号介绍

\d 等价于 [0-9],关于 [] 字符集下面会讲,只要知道 \d 是表示匹配一个数字就行了


3、+

匹配前面的子表达式一次或多次

见例子2


4、*

匹配前面的子表达式零次或多次

见例子2


5、.

匹配除换行符 \n之外的任何单字符

例子3

$s = <<<'TEXT'
test!@#$
TEXT;


preg_match_all('#.#s',$s,$m);
print_r($m);
/*
Array
(
    [0] => Array
        (
            [0] => t
            [1] => e
            [2] => s
            [3] => t
            [4] => !
            [5] => @
            [6] => #
            [7] => $
        )

)
*/


6、[ ]

标记一个中括号表达式字符集合

请看例子1,[a-z] 就是一个字符集合,可以是任意字符,比如我只要匹配 【abc,.】 这个5个字符——[abc,.]

这里有一个特殊的符号 - ,代表一个范围,常用的有 A-Z(大写的字母A到Z),a-z(小写的字母a到z) ,0-9(数字0到9)


7、?

匹配前面的子表达式零次或一次,或指明一个非贪婪限定符

例子4

$s = <<<'TEXT'
a1a2abbb
TEXT;
//匹配以a开头后面紧跟一个数字或没有数字(用了 ? )的字符串
preg_match_all('#a\d?#',$s,$m);
print_r($m);

/*
Array
(
    [0] => Array
        (
            [0] => a1
            [1] => a2
            [2] => a
        )

)
*/


8、\

将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。

比如匹配在正则中有特殊含义的字符,如 . 、 \ 、 $ 、 ( 、 [ 等等,这里就讲一下 向后引用(反向引用)

例子5

$s = <<<'TEXT'
<b>this is b</b><i>this is i</i>
TEXT;

preg_match_all('#<([a-z]+)>(.+?)</\1>#i',$s,$m);
echo "<pre>";
print_r($m);
echo "</pre>";

/*
Array
(
    [0] => Array
        (
            [0] => <b>this is b</b>
            [1] => <i>this is i</i>
        )

    [1] => Array
        (
            [0] => b
            [1] => i
        )

    [2] => Array
        (
            [0] => this is b
            [1] => this is i
        )

)
*/

我想匹配标签 b 和 i (或者其它标签)中的内容,总不能为了每一个标签都写一个表达式吧,比如这个:#<b>(.+?)</b>|<i>(.+?)</i>#i;那么多标签不得写死啊

而标签开头和结尾都是一样的,那么把匹配到的开始标签引用到结束位置不就行了?!


还记得例子2中 () 的作用吗,捕获匹配到的内容,供后面使用。而 \1 就是使用第一个 ( 开始所匹配到并被捕获的内容。在这个例子里 \1 就是匹配到的 b 或 i

那么想一想,若是在 () 中还有 (),如何使用反向引用?

好吧,不用你们想了,我来公布答案。

不管表达式中有多少个 (),都是以 ( 为顺序(从1开始),放在捕获的区域以供后面使用,最多好像是支持 99 个,没试过

在正则表达式中使用之前匹配并捕获(反向引用)的内容就是用 \1 \2 \3 ....各语言可能不同,比如 js 是 $1 ,java 是 \\1

例子6

$s = <<<'TEXT'
<div><b>this is b</b></div>
<div><span>this is span</span></div>
<span><i>this is i</i></span>
TEXT;

preg_match_all('#<([a-z]+)><([a-z])>(.+?)</\2></\1>#i',$s,$m);
print_r($m);

/*
Array
(
    [0] => Array
        (
            [0] => <div><b>this is b</b></div>
            [1] => <span><i>this is i</i></span>
        )

    [1] => Array
        (
            [0] => div
            [1] => span
        )

    [2] => Array
        (
            [0] => b
            [1] => i
        )

    [3] => Array
        (
            [0] => this is b
            [1] => this is i
        )

)
*/

匹配外层是多字符标签(div span 等)包裹,内层紧跟一个字符的标签(b i 等),因为 ([a-z]) 后面没有跟 + ,说明是匹配一个字符

由于<div><span>this is span</span></div> 不满足内层一个字符的标签,所以没有被匹配

例子7 java 版

import java.util.regex.*;

public class Test {
    public static void main(String[] args) throws Exception {
        String str = "<div><b>this is b</b></div><div><span>this is span</span></div><span><i>this is i</i></span>";
        
        Matcher m = Pattern.compile("<([a-z]+)><([a-z])>(.+?)</\\2></\\1>").matcher(str);
        while(m.find()){
            System.out.println(m.group()+"\n"+m.group(1)+"\n"+m.group(2)+"\n开始位置 : "+m.start()+" \n结束位置 : "+m.end()+"\n\n");
        }
    }
}

输出

<div><b>this is b</b></div>
div
b
开始位置 : 0 
结束位置 : 27


<span><i>this is i</i></span>
span
i
开始位置 : 63 
结束位置 : 92


9、^

匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合

例子8

$s = <<<'TEXT'
1abc
TEXT;
//2def
//ght
//34xyz

preg_match_all('#^\d[^\d]+#',$s,$m);
echo "<pre>";
print_r($m);
echo "</pre>";

/*
Array
(
    [0] => Array
        (
            [0] => 1abc
        )

)
*/

匹配以一个数字开头后面紧跟非数字的字符串,这个例子就把 ^ 的两个概念都讲了,例子的结果就是能匹配 1abc,2def,不能匹配 ght,34xyz

这里讲讲为什么不能匹配 34xyz,因为 3 被 \d 匹配了,而 4 就只能被 [^\d]+ 匹配,但是这个集合只匹配非数字字符串,所以不满足要求,整个字符串都不能被匹配

在一个字符集中用 ^ (一定要在字符集的开始位置),如 [^abc] ,那么就表示,不匹配abc这三个字母


10、{ }

标记限定符表达式的开始

这个将在限定符一节中将,简单来说就是表示一个范围


11、|

指明两项(多项)之间的一个选择

例子9

$s = <<<'TEXT'
<b>this is b</b><i>this is i</i><span>this is span</span>
TEXT;

preg_match_all('#<(b|i)>(.+?)</\1>#',$s,$m);
print_r($m);

/*
Array
(
    [0] => Array
        (
            [0] => <b>this is b</b>
            [1] => <i>this is i</i>
        )

    [1] => Array
        (
            [0] => b
            [1] => i
        )

    [2] => Array
        (
            [0] => this is b
            [1] => this is i
        )

)
*/

匹配 b 或者 i 标签包裹的内容,若也要匹配 span, 加上即可,#<(b|i|span)>(.+?)</\1>#

特殊字符暂时就介绍这些,单个的字符都很好理解,但想写出复杂的正则表达式却无从下手。

那么如何构造复杂的表达式?

简单来说就是化整为零,正则表达式可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些符号的任意组合。

用多种元字符(特殊字符)与操作符将小的表达式结合在一起来创建更复杂的表达式。

比如 例子5 就用了很多元字符

一定要多练,才能把所有符号牢记于心,都是熟能生巧。


布置一个练习题

待匹配内容

<div>123abc,</div>
<span>456def</span>
<m>a1b2c3</m>

请匹配出 456def

分析下:

1、外面包裹的标签不一定是这几个(div span m)

2、内容是以 数字开头的,后面跟个几个字母


很简单,看上面的几个例子,动动手就可以完成



下一节讲 限定符和符号的运算优先级

评论(3)

lililala6868

xiexie

2020-11-06 18:32 0 回复
发布评论
回复X
聊天室(0