第四章 正则表达式

 

 

什么是正则表达式

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

 

 

正则表达式的广泛应用

正则表达式在unix/linux系统中得到广泛的应用,强化了工具本身的功能。常见的UNIX下支持正则表达式的工具有:

    》用于匹配文本行的grep工具族:

    》用于改变输入流的sed流编辑器(streamediter);

    》用于处理字符串的语言,如awk,  python, perl,  tcl等语言;

    》文件查看程序,或分页程序,如mare. page,  less等。

》文本编辑器,如ed, vi,emacs, vim等·

 

 

如何学习正则表达式

实例一

匹配当前存在 file单词并且在这个单词后还有一个file的句子

 

\bfile\b.*\bfile\b

 

Copy一段

假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b。

这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。

换行符就是'\n',ASCII编码为10(十六进制0x0A)的字符。

如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:

0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。

这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)。-不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。

为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}。这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)。

 

 

如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。

 

 

如何实践正则表达式

    我们不是机器,总会犯错误。一般情况下,正则表达式不可能一次写正确。这就需要我们不断修改正则表达式,不断通近那个正确的写法,直到恰好达到我们想要的目的。

 

 

 

NOTE

    历史上曾经出现过3种grep,可以用于匹配文本。它们是:

   Grep 最早的文本匹配程序。使用PQSIX支持的基本正则表达式(Basic Regular Expression, BRE )

   Egrep 扩展的grep(Extend grep)。使用扩展正则表达式〔Extended Regular Expression, ERE )

   Fgrep 快速grep( Fast grep)。这个版本用于匹配固定字符串而不是正则表达式。

    在I942年发布的PDS1X标准中,3个prep版本合而为一,POSIX可以通过参数来支持多个正则表达式模式,无论是BRE还是ERE,fgrep和egrep还是可以在所有的unix/inux系统上使用,但是被标记为deprecated(不推荐〕

 

 

正则基础
元字符

正则表达式是描述某种匹配规则的工具。

正则中有两种字符一种基本字符(没有任何意义),一种元字符(赋予了表达匹配的某些含义)

 

POSIXBRE和ERE都支持的meta字符

 

^

BRE,ERE

锚定行或者字符串的开始,如"^grep" 匹配所有以grep开头的行。
BRE:仅在正则表达式结尾处具有特殊含义;
ERE:在正则表达式的任何地方都有特殊含义。

$

BRE,ERE

锚定行或者字符串的结束,如"grep$" 匹配所有以grep结束的行。
BRE:仅在正则表达式结尾处具有特殊含义;
ERE:在正则表达式的任何地方都有特殊含义。

.

BRE,ERE

匹配一个非换行的任何单个字符(除NUL)。
如:‘gr.p'匹配gr后加一个任意字符后,然后p

*

BRE,ERE

 匹配其前的任何数目或没有的单个字符,
例: . 表示任一字符, 则 .* 匹配任一字符的任意长度

[]

BRE,ERE

 匹配方括号内的任一字符,其中可用连字符(-)指的连续字符的范围
;^符号苦出现在方括号的第一个位置,则表示匹配不在列表中的任一字符,

 

 

 

 

POSIXBRE中才有的字符:

\{n,m\}

 区间表达式,匹配在它前面的单个字符重现的次数区别。
\{n\}指重现n次;\{n,m\}指重现n至m次;

\( \)

 保留空间,可以将最多9个独立的子模式存储在单个模式中。
如\(ab\).*\1 : 指匹配ab组合的两次重现,中间可存在任意数目的字符。

\n

 重复在\(与\)方括号内第n个子模式至此点的模式。

 

POSIXERE中才有的字符:

 

{n,m}

与BRE的\{n,m\}功能相同

+

匹配前面正则表达式的一个或多个扩展

?

匹配前面正则表达式的零个或一个扩展

|

匹配|符号前或后的正则表达式

( )

匹配方括号括起来的正则表达式群

 

 

 

Grep程序支持的meta字符plus

 

\<

锚定单词的开始,如:“\<grep” 匹配以grep开头的单词的行

\>

锚定单词的结束,
如:“grep\>”匹配包含以grep结尾的单词的行

\w

匹配文字和数字字符.也就是[A-Za-z0-9],
如’G\w*p‘匹配以G后跟零个或多个文字或数字字符.然后是p

\W

\w的反之形式,匹配一个或多个非单词字符,如点号、句号等

\b

单词锁定符,如:\bgrep\b只匹配grep

 

 

实例

 

//只显示以a开头的文件

[houchangren@ebsdi-23260-oozie shell]$ ls |grep '^a'
add.sh
a.sh
 


 

//显示a开头的文件中包含hadoop的行

[houchangren@ebsdi-23260-oozie shell]$grep  'hadoop'  a*
a.sh:HADOOP_IN_PATH="/usr/lib/hadoop/bin/hadoop"
a.sh:HADOOP=$HADOOP_HOME/bin/hadoop


//显示a.sh文件中包含hadoop的行

[houchangren@ebsdi-23260-oozie shell]$grep  'hadoop'  a.sh
HADOOP_IN_PATH="/usr/lib/hadoop/bin/hadoop"
HADOOP=$HADOOP_HOME/bin/hadoop


 

//显示连续五个a-z的字符在一行的行

[houchangren@ebsdi-23260-oozie shell]$ grep'[a-z]\{5\}' a.sh
HADOOP_IN_PATH="/usr/lib/hadoop/bin/hadoop"
 HADOOP_DIR=`dirname "$HADOOP_IN_PATH"`/..
HADOOP=$HADOOP_HOME/bin/hadoop
HADOOP_VERSION=$($HADOOP version | awk '{if(NR == 1) {print $2;}}');
 

//显示如果test被匹配,es就被存储到内存中,并标记为1.然后搜索任意个字符(.*),这些字符后面跟着另外一个es (\1),找到就显示该行。\1是取占位括号中的值。

[houchangren@ebsdi-23260-oozie shell]$ cata.txt
I'm test; test;
so you don't worry!
[houchangren@ebsdi-23260-oozie shell]$ grep't\(es\)t.*\1' a.txt
I'm test; test;

 

为了在不同国家的字符编码中保持一至,POSIX(The PortableOperating System Interface)增加了特殊的字符类,如[:alnum:]是A-Za-z0-9的另·个写法。要把它们放到[]号内才能成为正则表达式,如[A-Za-z0-9]或[[:alnum: ]] 在Linux下的grep除fgrap外,都支持POSIX的字符类。方括号表达式除了上面提到的字符外,还支持其他的组成形式:

1.       posix字符集


[:alnum:]

文字数字字符

[:alpha:]

文字字符

[:blank:]

空格与定位字符

[:cntrl:]

控制字符

[:digit:]

数字字符

[:graph:]

非空格字符

[:lower:]

小写字母字符

[:print:]

可显示的字符

[:punct:]

标点符号字符

[:space:]

空格字符

[:upper:]

大写字母字符

[:xdigit:]

16进制数字

2.      排序符号
指将多个字符视为一个符号,如[.cn.]即将cn视为一个符号

3.       等价字符集
认为多个字符相等,如[=e=]在法文的locale里,可匹配于多种与e相似的字符

 

 

 

 

 

正则表达式允许将POSIX字符集与其他字符集混用。如[[:alpha:]!]匹配任意一个英文字母或者感叹号(!)。

 

//匹配或者数字或者!

[houchangren@ebsdi-23260-oozie shell]$ grep -E'[[:digit:]!]+' a.txt

so you don't worry!

 

 

单个字符

匹配单个字符的方式有4种:一般字符、转义的meta字符、.(点号)meta字符,和方括号表达式。

1.      一般字符
例如:abc  就是 匹配abc

2.      转义的meta字符
例如:\*匹配 *  \[\] 匹配[]

3.      .(点号)字符
例如: .bc  匹配 abc,dbc等等

4.      方括号表达式
例如:[cC]hina 只匹配 china 和Chnia
      [^abc]d 匹配除了abc之外的任何小写字母

单个表达式匹配多个字符

 

文本的匹配锚点
^ 和& 开头和结尾的表示

 

运算符优先级

 

BRE的运算优先

运算符

          含义

[..][==][::]

方括号符号

\meta

转义的meta字符

[]

方括号表达式

\(\)\n

后向引用表达式

*\{\}

区间表达式和星号表达式

无符号

连续

^&

锚点

 

 

ERE的运算优先

 

运算符

          含义

[..][==][::]

方括号符号

\meta

转义的meta字符

[]

方括号表达式

()

分组

*+?{}

重置前置的正则表达式

无符号

连续

^&

锚点

|

交替

 

更多差异

 

1.      向后引用
    BRE中提供了一种机制名为“后向引用”backreferences,含义是:匹配的是之前正则表达式选定的部分。我们使用\1一\19来引用之前选定的模式,使用\(和\)括起想要之后引用的部分。

例如:

\(go\).*\1  匹配一行中前后出现两个go

2.      交替
    交替是ERE才一有的特性。当使用方括号表达式时,表示可以“匹配这个字符,或者那个字符”,但是无法“匹配这个字符序列或那个字符序列”。当我们需要这个功能时,在ERE中,就用到交替.交替是在不同序列之间用管道符号隔开。例如,you|me匹配you或者me.

           交替字符可以和管道符号一样,在一个正则表达式中使用多个来提供多种选择。因为交替字符的优先级最低,所以会一直扩展到新的交替字符,或正则表达式结束为止。


3.      分组

 

    在BRE中,我们使用一些meta字符修饰前置字符,匹配重复的情况。但是这样的操作仅仅

针对单个字符。在ERE中,分组功能能够计meta字符修饰前置字符串。分组符号就是使用(和)

将式子括起来。例如〔go)+匹配一个或多个连续的go.

    在使用交替时,分组就非常有用。例如:(Lily|Lucy) will visit my house today.在这个正则表达式中,分组限定了访问我家的是Lily或者是Lucy。

 

 

 

 

正则表达式的应用

    正则表达式之所以如此重要,一个原因是许多程序(包括UNIX/linux下的,和Win下的)

都使用正则表达式为自己提供扩展,以支持更强大的功能。

    正则表达式的风格有两种,BRE和ERE,这是历史遗留的产物。egrep风格的正则表达式在

UNIX开发的早期就已经出现,但是Linux的创始人KenThompson觉得不需要在ed编辑器中使

用这样全方位的正则表达式支持,ed的标准后来演化为BRE。

   ed又成为grep和sed的基础,故此。grep和sed支持的正则表达式类型是BRE。在pre-V7

时期,egrep被发明出来,egrep是使用的ERE风格正则。但是,当egrep, grep, fgrep合并成

个grep程序时,grep就同时支持不同风格的正则表达式〔通过一E选项支持ERE)o与此同时,egrep

虽然被从POSIX标准中踢出,但是许多UNIX/LINUX发行版本仍然文持egrep命令,而且许多历

史遗留脚本仍然在使用egrep,虽然标准做法应该是grep -E

    使用ERE风格n则表达式的部分程序有egrep, awk和lex. lex是一个词法分析器构建程序,

除了特殊情况下,很少在shell编程中用到。

扩展

除了前面提到的BRE和ERE这两种标准正则表达式支持外,许多程序根据需求提供正则表

达式的语法扩展。最常见的扩展是\<和\>,分别匹配单词开头和单词结尾。

    单词的开头有两种情况,一种是位于行的起始位置,一种是紧跟在非一单词组成字符后面;同样,单词的结尾也是两种情况;行的末尾和尾随一个非单词组成字符。

 

 

GUN支持额外的正则表达式运算符

 

                                  含义

\w

匹配任何单词组成字符,等同[[:alnum:]]

\W

匹配任何非单组成字符,等同[^[:alnum:]]

\<\>

匹配单词的开头和结尾

\b

匹配单词开头和结尾处所找到的空字符o \bword就等同\<word\>

\B

匹配两个单词组成字符间的空字符串

\’\`

分别匹配emacs缓冲区的开头和结尾。GNU程序通常将它们视为与^和S同义

 

研究罗马数字

有点脑袋大,那天有心情在研究吧。

 

解析电话号码实例

 

 

 

echo '80055512121234' | grep -E"^[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"
echo '800-555-1212' | grep -E"^[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"
echo '800-555-1212-1234' | grep -E"^[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"
echo 'work 1-(800) 555.1212 #1234' | grep-E "[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"


表达式解析一下:

 

[[:digit:]]{3}  三位数字

[^[:digit:]]*  非数字的任何东西(或者没有)

[[:digit:]]{3} 三位数字

[^[:digit:]]* 非数字的任何东西(或者没有)

[[:digit:]]{4} 四位数字

[^[:digit:]]* 非数字的任何东西(或者没有)

[[:digit:]]* 数字(可没有)

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐