刘嵩的小站 神様は乗り越えられる試練しか与えない。

正则表达式学习笔记

2017-06-02

Introducing Regular Expression–学习正则表达式

前言

正则表达式就是一种工具

这个东西在很多环境中都有用,比如vim、grep、sed等等

不懂UNIX的人注定还要重新发明一个蹩脚的UNIX

所以有一句话叫做,“不要重新造轮子“

从零开始学习

正则表达式是一种特殊的字符串模式,用于匹配一组字符串,他最早出现于20世纪40年代。

第一章-什么是正则表达式

从Regexpal开始

这就是一个网站,用来测试正则表达式的。

通过网上查到的,比如touch {1..9} 这种指令,其实不是正则表达式,这是一种touch命令的语法而已。

匹配北美电话号码

直接输入数字,他们够匹配的就是他的本身,这种叫做“字符串字面值”,它能够匹配的东西是固定的。

向比如这个Regexpal这个网站能够匹配的,就会显示出黄色高亮。

用字符组来匹配数字

通用的字符匹配方式中:

[0-9] 就表示从0到9的数字。

正则表达式中酱方括号视为特殊的元字符,因此方括号[]不参与匹配。

元字符是正则表达式中有特殊含义的字符,也是保留字符,[0-9]这种形式的正则表达式称作字符组,有时也叫字符集。

可以给出镜一步限定的方式[012789],这个字符只能用来匹配列出的数字。

要匹配任意10位数的电话号码,可以使用:

[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]

使用字符组简写式

就像上边说的那样,其实\d,这种表达,可以像[0-9]那样匹配任意阿拉伯数字,这种正则表达式叫做字符组简写式,也叫转义字符,但后裔中成为容易造成误解。

刚才提到的电弧号码可以用\d\d\d-\d\d\d-\d\d\d\d,这种表达方式,中间的-连字符是一个字面值,因此会被原样匹配。

\D的含义是任意一个非数字的字符,在没有其他连字符干扰的情况下,可以使用\D,来代替连字符-。

匹配任意字符

其实还可以用点.,来匹配那些连字符,\d\d\d.\d\d\d.\d\d\d\d,点号是一个通配符,可以匹配任意字符,但是某些情况不能够匹配行起始符,以上实例中的正则表达式其实还可以匹配707%827%7091中的%百分号,也可以匹配竖线|,

捕获分组和后向引用

这里我们讲的是捕获分组,来匹配电话号码中的某一部分,然后使用向后引用对分组中的内容进行引用,要创建捕获分组,先将一个\d放在一对圆括号中,这样就将它放进了一个分组中,后面可以用\1对捕获的内容进行引用。

(\d)\d\1,这种表达式,就是说,在匹配\d\d的同时,对第一个字符进行了捕获,然后在第三个字符中,引用了。就是表示第一位和第三位是一样的。

使用量词

现在使用另一种匹配的方法,\d{3}-?\d{3}-?\d{4}

花括号中的数字表示带查找的数字出现的次数,包含数字的花括号是一种量词,花括号本身做元字符。

问号?是另一种量词,在以上的表达式中表示连字符是可选的,也就是说连字符可以不出现或自出现一次,还有其他量词,比如一个或多个+,任意个#。

这样就多了一个表达的方法:(\d{3,4}[.-]?)+,这个表达式也不完全对,因为它只能匹配3位或者4位的数字而不管是否符合电话号码的格式,所以改进一下:

(\d{3}[.-]?){2}\d{4}

理解一下,括号表示分组,\d表示数字,表示有三个数字,后面可能会接一个.或者-的符号,也可能不接,这样的模式在出现一次,然后连接一个四位的数字。

括选文字符

最后这个正则白哦大师表示第一个3位数,可以带也可以不带括号,即区号是可选的:

^((\d{3})|^\d{3}[.-]?)?\d{3}[.-]?\d{4}$

为了便于理解,做如下分析:

出现在正则表达式的起始位置,或者竖线|后面的^,叫做脱字符,表示电话号码会出现在一行的起始位置;

左括号(,为捕获分组的起始符;

( 表示左括号本身;

\d表示一位数字;

{3}表示紧挨着的\d是三个相连的,即匹配三位数字;

)表示右括号本身;

竖线|表示选择,也就是从多个可选项中选择一个,换句话说,他表示“匹配一个不带括号的区号活着一个带括号的区号”

脱字符表示匹配起始行,注这里需要加上^,是因为,作为|的另一种选择,不同于前面带括号的三位数,只有可能中间的三位数字是数字,两边都是括号。后面这种选择,因为有可能是这样的:-999,这样的格式起始是满足第二种选择的,但是它不符合我们设置正则的要求,所以要要求,假如没有括号,那么数字必须顶格在行首。

事实上,经过测试第二个^就算不写的话,影响不大。

第二章-简单的模式匹配

匹配字符串字面值

正则表达式最为直接和明显的功能个就是用一个或多个字符字面的值来匹配字符串。

匹配字符串的字面值的方法就是直接使用普通的字符。

匹配数字

输入\d来匹配数字,假如有global的复选框,代表全局匹配,这将会匹配下方所有的数字,如果没有选择,则匹配第一个出现的数字。

[0-9]的功能与\d等效,其实也可以这样[0123456789],也是代表所有的数字。

假如只想匹配0和1两个数字,那么[01],就可以了。

匹配非数字字符

通常可以将简写式取反,取反的结果就是排除。

比如要匹配非数字字符,可使用包含以下大写字母的简写式\D.

关于字符组的取反,就是指,不匹配这些或者匹配除去这些以外的内容。[^0-9]或者[^\d]

匹配单词和非单词字符

在正则匹配中,将\w就表示匹配所有的单词字符,前提是勾选了global选项,\D与\w的区别就是,前者会匹配空格,标点符号(引号,连字符,反斜杠,方括号)等字符,而后者不会,他只匹配字母,数字,下划线。

在英语环境中,与\w匹配等效的字符组为[_0-9a-zA-Z].

\W匹配非单词字符,匹配空格,标点以及其他非字母,非数字字符,与它等效的字符组[^_0-9a-zA-Z]或者[^\w]

匹配空白符

可以使用\s匹配空白符号。

与它等效的字符组[\t\n\r]

也就是说他会匹配:1,空格;2,制表符\t;3,换行符\n;4,回车符\r。

一般来说,字母的大写,就是取反的,比如这里,非空格字符就用\S,来表示。

再谈匹配任意字符

用正则表达式匹配任意字符的一种发发就是使用点号,点号可以匹配除行结束符之外的所有字符,个别情况除外,要匹配连续的8个任意字符,可以使用八个点号……..。或者用量词表达模式,.{8}。

\bA.{5}T\b 这个表达式有更强的特指性

\b在这里面只单词的边界,不消耗任何字符空间;

字符A和T限定了单词的首尾字母;

.{5}任意五个字符

\b.{7}\b 这个表达式能够匹配任意7个字母的单词。

.# 这个表达式表示0个或者多个字符,等效的[^\n]没换行之前的所有内容,[^\n\r]在换行和回车之前的所有内容。

.+ 这个表达式表示1个或者多个字符。

给文本加上标签

比如在一个文本中,有一行标题“The Rime of the Ancient Martime”,这个是一首诗的标题,可以通过(^T.#$),表示匹配本行内以开头的一直到本行末尾几个字符都行。用工具他这个替换成<\1>$1<\h1>,这里的$1就表示(^T.#$)匹配到的内容。其实这就是一个捕获和引用的动作。

通过这样一个替换,就把目标内容嵌入了h1标签中。

其实在包括perl在内的多数实现程序中,还可以使用\1表示向后引用,但是regexr这个软件只能实现$1、$2、$3这种表达。

用sed为文本加上标签

在命令行中,使用sed也可以为文本添加标签,sed是unix流编辑器,它支持用正则表达式转换文本,sed最初在20世纪70年代早期有Lee McMaon于贝尔实验室开发,如果用户使用的是Mac或者Linux,那就已经有sed了。

在shell中测试一下内容

echo hello | sed s/hello/goodbye/

程序会执行回显命令显示hello,通过管道把hello传给sed,sed接到以后,把hello换成goodbye,并显示出来。

这个就是seb的使用的基本用法,具体需要在做一个学习笔记来重点学习sed。

用perl为文本加上标签

最后,来学习一下perl,用perl来做类似的事情,perl是有larry wall 在1987年创立的一种通用的程序设计语言,他以正则表的事的强大支持和文本处理能力而闻名。

perl -v 来查看,系统中是不是已经装了perl软件。

perl -ne ‘if ($. ==1){s/^/; s/$/<\/hi>/m; print;}' rime txt

就可以把第一行的标题加上<h1>标签。

perl调用了perl程序

-n表示输出全部输入内容

-e选项表示允许在命令行(而不是在文件中提交程序代码)

if语句检车是否在第一行,$.是一个特殊的变量,它匹配当前行。

用perl语言实现相同任务的方式很多,也许这不是为了文本添加标记的最有效的方式。就是演示而已。

第三章-边界

行的起始与结束

^表示行的起点,根据上下文^会匹配行或者字符串的起始位置,有时还会匹配整个文档的起始位置,而上下文这一句应用程序和在应用程序用所使用的选项。

$表示行的终点。

multiline功能,如果不勾选multiline功能,那么整个文档将作为一个字符串处理。如果勾选,则表示,文本具有多行处理的属性。

dotall功能,这个选项表示点号除了匹配其他字符外,还会匹配换行符,取消勾选dotall选项,则类似这种 ^THE.#\?$什么也不匹配。

单词的边界与非单词的边界

我们已经多次看到\b的情况了,它代表单词的边界。

我们还可以匹配非单词边界,当然啦,就是用它的大写\B,比如\Be\B这样的表达式,可以匹配一个单词的中的字母e,要求这个e的两边都不能是单词的边界,也就是e不能是单词的开头或者结尾。

在有些程序中,指定单词的边界有另一种方法:

<和>这两个来制定单词的开头和结尾。

vim就支持这种特殊的正则模式。

这种语法也可以用于grep。

其他锚位符

与锚位符^相似,以下下简写式匹配主题词的起始

\A 表示字符的字符串的开头;\Z表示字符串的结尾,在有些上下文中还可以使用\z这种表达方式,但是这样的写法不是在所有正则表达式的程序中都适用的,但是在Perl和pcre中是能够使用的。

使用元字符的字面值

可以使用\Q和\E直接的字符集匹配字符串的字面值。

为了展示这一点,我们可以输入这样的元字符,.^$#+?|{}[]-,这15个字符在正则表达式中,有特殊的含义,用来编写匹配模式,连字符在在字符组的方括号中能够表示范围,但在其他情况下则并无特殊含义。

输入之后,程序不会匹配任何东西,因为程序会认为,这是一个正则表达式,而不是字符串的字面值。

想要把这些特殊字符体现为字面值,需要在首尾分别加上\Q和\E,这两个符号间的任意字符都会被解释为普通字符。

添加标签

使用sed添加标签

使用perl添加标签

第四章-选择、分组和向后引用

选择操作

我们已经在前面的学习中见过分组了,分组对文本加括号以帮助执行某种操作,比如

在两种或者更多的可选模式中选择一个

创建子模式

捕获一个分组一遍以后进行下后引用

对不和的模式使用某项操作,如量词

使用非捕获分组

院子分组(高级)

简单的说,选择操作可在多个可选模式中匹配一个,例如:你想在“the rime of the ancyent mariner”中找出“the”的出现的次数,包括各种大小写形式,为了就可以使用选择操作。

(THE|the|The)

你会看到,文字中所有的the的大小写模式的字符都被匹配了。

可以使用一个选线来使得分组更加简短,(?i).

前面的表达模式可以用这个来替换(?i)the

子模式

多数情况下,提到正则表达式中的子模式,就是指分组红的一个或者多个分组,子模式就是模式中的模式,多数情况下,子模式中的条件竜得担匹配的前提是前面的模式得到匹配,但也有例外,自模式的写法可以有很多种,这里我们主要关注括号中的子模式。

从某种意义上说(THE|the|The)有三个字模式,the是一个子模式,THE是另一个,The又是一个。

但这种情况下,第二个子模式中的匹配不会依赖第一个,(通常最左边的模式会首先匹配)

在这种情况下,(t|T)h(e|eir),这种,后面的子模式就会依赖前面的子模式。

捕获分组和向后引用

当一个模式的全部或者部分内容由一对括号分组的时候,他就对内容改进型了捕获并临时村处于内存中,可以通过后弦一用重获捕获的内容,形式为:

\1或者$1

这里的1表示引用的是第一个捕获的分组,而当数字改成2的时候就是引用第二个捕获的分组,以此类推。

最开始的时候seb仅支持从1到9的后向引用,但是现在已经没有这样的限制了。

\l把后面紧挨着的字母转换成小写

\u把后面紧挨着的字母转换成大写

\L把后面紧挨着的文本字符串转换成小写

\U把后面紧挨着的文本字符串转换成大写

命令分组,就是有名字的分组,这样就可以通过名字而不是数字来引用分组,在括号中添加?<名字>,就可以直接用$名字,来引用前面的捕获的变量了。

非捕获分组

还有一种分组就是非捕获分组,非捕获分组不会将其内容存储于内存中,在你并不想引用分组的时候,可以使用它,由于不储存内容,非捕获分组就带了较高的性能,但是简单的匹配模式很难感受到性能的提升

(THE|the|The)这个分组改写成非捕获分组就可以使用(?:THE|the|The).

当然还可以这样(?i)(?:the),或者(?:(?i)the),还有一种最推荐(?i:the)。

第五章-字符组

字符组取反

对字符组的取反已经用过许多次了,就是加一个^脱字符。

它字符组起始位置的脱字符的意义是“不,我不想匹配这些字符组”,这就是说脱字符必须出现在起始的位置。

注意,这里面的脱字符,只有在[]里面的时候,才表示取反,否则是表示在首位的意思。

并集与差集

字符组可以项集合那样操作,事实上,字符组的另一个名称就是字符集,不是所有的实现程序都支持这项功能,java是支持这个功能的。

如果你想要两个字符集的并集,就是把两个字符集合并到一起,可以这样做:[0-3[6-9]];

如果你想要两个字符的差集,就是在一个字符集的匹配范围内减去另一个字符集,就可以这样做:[a-z&&[^m-r]]

posix字符组

portable operating system interface是一系列的标准接口,其中包含了一个正则表达是标准,该标准提供了一套命名的字符组。

其形式为:[[]],两个括号,中间嵌入两个冒号::,然后里面写上内容,[[:XXXX:]]

这个平时并不常用,基本上平常的简写式可以代替它的全部功能,但是如果想要查寻他究竟支持那些字符组,可以百度一下关键字“posix字符组”,会得到一个表格,对照着用就行了。

第六章-匹配unicode和其他字符

匹配unicode字符

用八进制数匹配字符

匹配unicode字符属性

匹配控制字符

第七章-量词

贪心、懒惰和占有

这里面说的是量词,量词本身是贪心的,当尝试匹配是,他会选定尽可能多的内容,也就是整个输入,量词首次尝试匹配整个字符串,如果失败则会退一个字符后在次尝试,这个过程叫做回溯,他会每次会退一个字符,直到找到匹配的内容或者没有字符可以尝试为止。

此外,他还会记录所有的行为,因此,香蕉另两种方式他对资源的消耗最大,他先吃进所有的字符,然后每次吐出一点。

懒惰,有的时候也会说勉强,的量词则是使用另一种策略,他从目标的起始位置开始尝试搜寻,找匹配字符,每次查找字符串的一个字符,寻找他要匹配的内容,最后她会尝试匹配整个字符串,要是一个量词成为懒惰的,需要在普通量词后买呢添加?。

这样,他每次就只会“吃”一点。

占有量词会覆盖整个慕白哦然后尝试寻找匹配内容,但他只尝试一次,不会回溯。占有量词就是在不同的量词后面加一个加号,他不咀嚼,而是直接吞咽,然后才想知道吃的是什么。

用#、+和?进行匹配

如果像这样的的使用星号,.# 他将会贪心的方式匹配住文本中的所有字符数字,如前面所述,.# 会匹配任何字符零次或多次。

#表示前面紧挨的字符匹配零次或者更多次

+表示前面紧挨的字符匹配一次或者更多次

?表示前面紧挨的字符匹配零次或一次

匹配特定次数

一个量词,在没有修饰的情况下就是贪心的

范围语法的总结:

{n}精确匹配n次

{n,}匹配n次或者更多次

{m,n}匹配m次到n次

{0,1}与?相同,零次或者一次

{1,0}与+相同,一次或者更多

{0,}与#相同,零次或者更多

懒惰量词

现在让我们把贪心特性放到一边,来看看懒惰量词,理解懒惰特性的最好的方式就是看看效果。

假如待匹配的内容里面有个555555,我们输入5?,就会匹配这个字符串,但是如果加上修饰,让他懒惰匹配,就是5??,我们就会发现他不匹配任何内容了。

也就是说,他不会强制匹配第一个5,懒惰的基本特性就是匹配尽可能少的字符,他就是个懒虫。

如果改成5+?,他就会默认匹配最少的情况5.就是在正则表达式的允许范围呢,尽可能匹配到最短的复合条件的字符串。

占有量词

占有式匹配很想贪心式匹配,她会选定尽可能多的内容,耽于贪心式匹配不同的是,她不进行回溯,他不会放弃所找到的内容,他很自私,也是把它称为占有式的原因,他仅仅抱住自己所选的内容一点也不放弃,但占有量词的优点是速度快,她无需回溯,当然,匹配失败的话,也很快。

占有式的量词就是在量词后面加上一个加号+。

第八章-环视(了解即可)

环视是一种非捕获分组,他根据某个模式之前后之后的内容匹配其他模式,环视也成为零宽度断言。

正前瞻

反前瞻

正后顾

反后顾

第九章-用html标记文档(了解即可)

匹配标签

用sed转换普通文本

用sed进行替换

用sed处理罗马数字

用sed处理特定段落

用sed处理多行诗文

第十章-初级班毕业了(了解即可)


相似文章

内容导航