re模块逐步进阶

Windows 10家庭中文版,Python 3.6.4,

 

正则表达式,自己一直的水平是 知道,但不熟悉,简单的也能写,复杂的就需要看资料了,距离灵活运用还是差那么一些的。

于是,今天(180831)来好好研究下正则表达式模块的使用。

 

教程:

1.菜鸟教程(RUNOOB)之Python 3的正则表达式

2.Python 3官方文档之re — Regular expression operations (最新的3.7)

说明,两者都有丰富的示例可以练习。

3.Regular Expression HOWTO

 

上面的教程2自然要比教程1更丰富,从英文的角度来说,也介绍的更清楚。

但是,教程1是中文的,对于咱们来说,更容易上手,遇到难点了,再去看教程2——需要足够好的英文水平。

不过, 教程2也是不完整的,执行dir(re),发现其中存在template(pattern, flags=0)函数,可教程2中没有提及,只是在Match Objects的expand()函数中有用到(之前看过书——Python的基础教程,好像,这个template功能好像挺有用的,需要dig)。

因此,在完成教程1、2的学习后,更进一步进阶就需要仔细研究模块了。

当然,完成教程1、2的学习且熟悉后,大部分需要使用正则表达式的情景都不会存在问题了。

 

----

 

re模块中有两个主要的对象:Regular Expression Objects、Match Objects,开发者通过使用 模块本身提供的函数,以及 这两个对象各自的方法 来解决问题。

针对三者的函数、方法,下表做了一个简单的整理:

其中,模块本身提供的函数 和 Regular Expression Objects 下的方法存在重叠之处,,猜测:两者的功能应该是相同的,具体使用哪种,取决于自己的使用场景。

还有模块级别常量,主要是flags了,自己目前可能就知道I、M是什么意思吧,其它的还需要dig。

 

180901续

 

教程1要点复习:

1.re.compile用于基于 正则表达式 字符串 来编译生产 正则表达式对象,如上面的表格所示,可以使用 正则表达式对象完成 相关操作;

2.正则表达式对象 也可以作为re.search()、re.match()的参数,和直接使用 正则表达式字符串 具有相同的效果;

3.re.match(pattern, string, flags=0)

尝试从字符串的【起始位置】匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

4.re.search(pattern, string, flags=0)

扫描整个字符串并返回第一个成功的匹配

若找到,返回一个_sre.SRE_Match对象,没有找到返回为空。

>>> m = re.search('\d', '一二三2342', re.U)
>>> type(m)
<class '_sre.SRE_Match'>

_sre.SRE_Match对象有下面的可用函数(标记为红色为常用):

'endpos', 'expand', 'group', 'groupdict', 'groups',

'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string'

5.Quote:re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

6.re.sub(pattern, repl, string, count=0)

替换字符串中的匹配项。repl可以是函数,具体用法需要dig。

教程2还有re.subn(pattern, repl, string, count=0, flags=0)。

7.re.search是查找 第一个匹配,那么,re.findall、re.finditer则是返回 所有匹配子串 的列表,是列表

感觉这两个find*函数更有用吧?是否会将(...)的匹配子串也放到列表中呢?

下面是三组测试:

>>> restr = '(\d+)' # 测试1:末尾无空格
>>> m = re.search(restr, 'this is 123 and this is 456')
>>> m.group()
'123'
>>> m.groups()
('123',)
>>> re.findall(restr, 'this is 123 and this is 456')
['123', '456']
>>> m.group(1)
'123'
>>> 
>>> restr = '(\d+) ' # 测试2:末尾有空格
>>> m = re.search(restr, 'this is 123 and this is 456')
>>> m.group()
'123 '
>>> m.groups()
('123',)
>>> re.findall(restr, 'this is 123 and this is 456')
['123']
>>> 
>>> restr = '\d+ ' # 测试3:末尾有空格,去掉了括号
>>> m = re.search(restr, 'this is 123 and this is 456')
>>> m.group()
'123 '
>>> m.groups()
()
>>> re.findall(restr, 'this is 123 and this is 456')
['123 ']

8.re.split(pattern, string[, maxsplit=0, flags=0])

按照能够匹配的子串将字符串分割后返回列表

 

教程2学习笔记

前面认真看了教程2,其中不少知识点刷新了自己对正则表达式及其使用的认知。

1.re模块中的正则表达式操作 和 Perl中的很类似;

2.字符串的正则表达式操作涉及两个元素:正则表达式、被搜索的字符串。两者都可能是 Unicode strings (str) 或者 8-bit strings (bytes) 模式中的一种,但是 正则表达式 和 被搜索的字符串的 模式必须相同

3.正则表达式中使用 反斜杠(\)、Python也使用反斜杠表达一些特殊字符,因此,两者可能存在冲突。比如,要表示一个 在 正则表达式中用的 反斜杠字符,就需要写为 "\\\\"——四个反斜杠。怎么解决呢?使用Python的原始字符串(raw string),即在字符串前面添加r,比如前面的 "\\\\" 可以简写为 r"\\"。

>>> '\\'
'\\'
>>> '\\\\'
'\\\\'
>>> r'\\'
'\\\\'
>>> len('\\')
1
>>> len(r'\\')
2
>>> len('\n')
1
>>> len(r'\n')
2

4.在上文,孤猜测 模块级别的re.functions()函数 和 Regular Expression Objects的函数功能差不多,可 教程2 中提到,这些模块级别的函数只是一些快捷方法,省去了编译正则表达式对象的步骤,可同时还缺失了一些微调参数(fine-tuning parameters)。

5.教程2还提到,re是Python官方的标准模块,还有一个第三方的regex模块,完全兼容re模块,更强大,有更多功能,对Unicode 字符串提供了更彻底的支持,其PYPI地址。预计又要花费大量精时了,短期不会考虑它,毕竟,re模块足够自己用了。

 

----0901 1121----lunch time----

 

0901 1241继续

 

教程2分为5部分,其中,第一节Regular Expression Syntax是其中的重难点;

Module Contents、Regular Expression Objects、Match Objects三小节的内容在教程1中有所介绍,只是教程1比较简明,没有讲完全,也有些地方没有讲透;

最后一节Regular Expression Examples讲使用re模块的使用实例了,需要好好看看。

 

下面介绍第一节Regular Expression Syntax的学习笔记,其它四部分,读者请阅读教程2——更详细的官文。

 

6.正则表达式可以简单地 连接起来(be concatenated),re A、re B,可以连接为re AB,但这是有条件的:A、B必须是 简单的re。什么情况下是不简单呢?

1)不包含低优先级操作(孤不太理解这个);2)在A、B之间不包含边界条件;3)不包含 \numbered 组应用(numbered group references);

抱歉翻译的不很准确,原文如下:

This holds unless A or B contain low precedence operations; boundary conditions between A and B; or have numbered group references.

7.正则表达式 包含 普通字符、特殊字符。特殊字符有两大作用:

1)表示一类普通字符(class);2)影响正则表达式如何被解释(interpreted);

8.重复限定符 (*+?{m,n}等) 不能被直接嵌套使用,,在重复限定符 的 末尾 添加问号(?)可以防止贪婪模式(greedy),,还可以在其它实现中添加其它调试语,,在内部重复限定符添加圆括号包含后,可以再次使用重复限定符,比如,(?:a{6})*;

9.简单的特殊字符在很多地方都有介绍,若是有正则表达式基础的话,应该会了解,下面对其中一些做特别介绍

1)点号(.)

也是英文句号,表示 除换行符外的任意字符,进阶:和标记re.DOTALL组合时表示 包括换行符在内的所有字符,简言之,代表 所有字符。

2)脱字符(^)

表示字符串的开始,进阶:和标记re.MULTILINE组合时,表示每一个换行符后的开始位置。

3)美元符号($)

表示字符串的结尾,进阶:和标记re.MULTILINE组合时,表示每一个换行符的位置。更多特性请参考官文的介绍——教程2、3,在写复杂re时很重要

4)重复限定符:星号(*)匹配0个或更多,加号(+)匹配1个或更多,问号(?)比匹配0个或1个,,注意,默认都是贪婪模式,在限定符后添加一个问号,可以变更为非贪婪模式,即,*?、+?、??;

5)重复限定符:{m}匹配m个,{m,n}匹配不少于m个,不多于n个,同样,后面添加问号(?)就变为了非贪婪模式,,应该还有一个{m,}的用法,表示匹配至少m个,教程2中没有讲这个。

6)反斜杠(\)

两个作用:转义特殊字符,比如前面介绍的特殊字符;指明特殊序列(原文:signal a special sequence,比如,\1表示第一个匹配的子串,后面会有介绍)。

另外,前面提到过,反斜杠在Python和正则表达式中的冲突,官文再次强烈建议使用 原始字符串 写正则表达式。不要纠结了,正则表达式前面一律添加 r 字符吧。

7)一对中括号([])

表示一个字符集合(a set of characters),大家应该都很熟悉。

进阶:

-特殊字符在中括号中是没有特殊意义的——Special characters lose their special meaning inside sets.,这样所!

-字符类(Character classes)还是可以在中括号中使用,比如\w、\S。

-脱字符(^)求反,脱字符^是前中括号后第一个字符。

-匹配后中括号,两种方式:放在前中括号后面第一个位置,或者,使用反斜杠,,孤推荐使用反斜杠。

-暂不支持嵌套集合,

8)竖号(|,Vertical sign)

表示匹配其前后的 正则表达式 中任意一个都是 匹配的,用法:A|B,正则表达式可以多个(arbitrary number of REs)。

进阶:

-从左往右 查找,找到匹配后就不再查找了;

-匹配竖号的方式:下划线(\|),添加到中括号中([|]);

9)圆括号在正则表达式中的使用——重难点啊!自己从来没搞清楚过!趁现在吧

教程2中的各种用法:

(...)

括号中是一个正则表达式,指明了一个 组(group)的开始和结尾。

一个 组 的内容可以在 匹配执行后 重用,并在其后的字符串中 使用 \number 特殊序列(\number special sequence) 进行匹配。

圆括号本身可以使用反斜杠、中括号进行匹配。

 

什么是 组?圆括号里面是 组 的正则表达式,出去下面介绍的情况外,一个圆括号就代表一个组,,执行search()函数后,可以使用返回对象的groups()函数查看组匹配的信息。groups()函数中的匹配字符串的顺序,和圆括号在整个正则表达式中出现的顺序相关,先出现的会出现在groups()函数结果的前面。~~不好意思,好像没有解释的很清楚,还需dig,也欢迎大家补充~


(?...)

这是一个 扩展标记(extension notation),?号后第一个字符决定了其进一步的语法。

扩展标记 通常不会创建新的组,即合上面的(...)不一样。

唯一的例外:(?P<name>...)。


(?aiLmsux)

匹配空字符串,用于 为整个正则表达式设置标记。必须出现在正则表达式的第一位置(Flags should be used first in the expression string),有了它,就不需要单独设置flags了,感觉很有用。


(?:...) 注意,问号有有 冒号(:)

A non-capturing version of regular parentheses.

但是,被这个组匹配的子串不能在稍后被 重用


(?aiLmsux-imsx:...)

短横线前面是 设置set标记,后面是取消remove标记。有些复杂,请看教程2。


(?P<name>...)

这个见到的比较多,也是很有用的。

和圆括号的组 不同的是 匹配整个用法的组的字符串 可以使用 符号化组名(the symbolic group name name) 来访问。

符号化组名 要求:是Python标识符,在一个正则表达式中唯一。

这个也叫做 符号化组、命名组,它同时也是一个数字化组。

这个组可以在三种上下文中引用(下为官文截图):


(?P=name)

A backreference to a named group。上面的例子中大家有见到了。


(?#...)

注释。哦。有什么意义?


(?=...) a lookahead assertion

(?!...) a negative lookahead assertion

上面两个是一对,具备相反的功能。

孤有些说不清楚,看示例:

For example, 'Isaac (?=Asimov)' will match 'Isaac ' only if it’s followed by 'Asimov'.

若字符串仅为'Isaac ',则不匹配,除非其后有'Asimov',但是,和?=后re匹配的'Asimov'不会出现在匹配结果中

For example, 'Isaac (?!Asimov)' will match 'Isaac ' only if it’s not followed by 'Asimov'.

字符串'Isaac '如果没有跟着'Asimov'则匹配成功,否则,匹配失败。


(?<=...) a positive lookbehind assertion

(?<!...) a negative lookbehind assertion

上面两个是一对,具备相反的功能,这一对上上面一对可以算是组合对:前面一对 检查字符串末尾的是否匹配,这一对检查字符串开头是否匹配

以上四个的英文名都有assertion,就是所谓的断言了,断言是否存在什么,然后决定是否匹配。

官文示例如下:

>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'

>>> m = re.search(r'(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'

 

(?(id/name)yes-pattern|no-pattern)

给定id或名称的组匹配成功,那么,就和yes-pattern进行匹配,否则,和no-pattern进行匹配。no-pattern是可选的,可以忽略掉。

官文示例:

For example, '(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)' is a poor email matching pattern, 
which will match with '<user@host.com>' as well as 'user@host.com', 
but not with '<user@host.com' nor 'user@host.com>'.

解释,如果(<)?匹配成功,那么,就使用>来匹配,否则为字符串结束符。就是实现 前后尖括号 要么同时有、要么都没有 的检测。

10)专用序列(The special sequences)

最后一个 重点 了!

由一个反斜杠(\)和一个字符组成(找个说法不准确,在 \number 时,会有最多两个数字)。

若第二个字符不是普通的ASCII数字或字母,那么,正则表达式将匹配第二个字符。下面的示例匹配了 鲜 字 花 字。

>>> import re
>>> re.findall('\鲜\花', '今天来到花市,发现了好多鲜花,最后还买了一束鲜花——各种混搭')
['鲜花', '鲜花']

下面选择一些进行介绍——其实不少大家都是知道的,或许有些进阶内容要补充:

\number

匹配 组1号 和number相同的匹配了的组的内容。组号 从1开始,这个序列仅用于匹配前面99个组的内容。

进阶:

-反斜杠后第一个数字为0,或number是三个八进制数字,将不会被解释为 组匹配,而是匹配八进制数所代表的字符;

-在正则中括号中,所有的数字转义被视为字符,比如 [\97] 不是匹配第97个组的内容,而是匹配字母a。

 原文:Inside the '[' and ']' of a character class, all numeric escapes are treated as characters.

 上面这条解释有误,测试发生异常,还需要dig:

>>> re.findall('[\97]', 'an apple on the tree')
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    re.findall('[\97]', 'an apple on the tree')
  File "C:\Python36\lib\re.py", line 222, in findall
    return _compile(pattern, flags).findall(string)
  File "C:\Python36\lib\re.py", line 301, in _compile
    p = sre_compile.compile(pattern, flags)
  File "C:\Python36\lib\sre_compile.py", line 562, in compile
    p = sre_parse.parse(p, flags)
  File "C:\Python36\lib\sre_parse.py", line 855, in parse
    p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)
  File "C:\Python36\lib\sre_parse.py", line 416, in _parse_sub
    not nested and not items))
  File "C:\Python36\lib\sre_parse.py", line 527, in _parse
    code1 = _class_escape(source, this)
  File "C:\Python36\lib\sre_parse.py", line 340, in _class_escape
    raise source.error("bad escape %s" % escape, len(escape))
sre_constants.error: bad escape \9 at position 1
>>> re.findall(r'[\97]', 'an apple on the tree')
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    re.findall(r'[\97]', 'an apple on the tree')
  File "C:\Python36\lib\re.py", line 222, in findall
    return _compile(pattern, flags).findall(string)
  File "C:\Python36\lib\re.py", line 301, in _compile
    p = sre_compile.compile(pattern, flags)
  File "C:\Python36\lib\sre_compile.py", line 562, in compile
    p = sre_parse.parse(p, flags)
  File "C:\Python36\lib\sre_parse.py", line 855, in parse
    p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)
  File "C:\Python36\lib\sre_parse.py", line 416, in _parse_sub
    not nested and not items))
  File "C:\Python36\lib\sre_parse.py", line 527, in _parse
    code1 = _class_escape(source, this)
  File "C:\Python36\lib\sre_parse.py", line 340, in _class_escape
    raise source.error("bad escape %s" % escape, len(escape))
sre_constants.error: bad escape \9 at position 1
试验1:失败

 

\A

\Z

上面两个是一对,分别表示匹配字符串的开始、结尾。

和^、$有什么区别呢?

 

\b

\B

匹配空字符串,两者作用相反。涉及到ASCII、LOCALE两个标记的使用。详见教程2。

 

\d

\D

匹配数字,但又分为Unicode模式、ASCII模式,前者包含更多,多了哪些,还需dig。

Unicode模式下,如果标记ASCII设置了,则只有 [0-9] 匹配。

 

\s

\S

匹配空白字符,\t\n\r\f\v],也分为Unicode、ASCII模式,同上一对。

 

\w

\W

匹配alphanumeric字符和下划线,即[a-zA-Z0-9_],也分为Unicode、ASCII模式,同上一对。

 

另外,Python字符串量支持的一些标准转义字符 同样可以被 正则表达式解析,比如:

\a      \b      \f      \n

\r      \t      \u      \U

\v      \x      \\

注意,正则表达式中 \b 表示 单词边界,除非它出现在字符类(character classes)才表示 退格。

注意,\u、\U可以在Unicode模式下被识别,但在bytes模式下报错。

注意,八进制转义(不能准确翻译下面的原文,就暂时不翻译中文了)

Octal escapes are included in a limited form. If the first digit is a 0, or if there are three octal digits, it is considered an octal escape. Otherwise, it is a group reference. As for string literals, octal escapes are always at most three digits in length.

 

好了,专有序列也讲完了,仔细算的话,Python的正则表达式中的re模块自己是学了一遍了,可是不熟悉啊!

对于教程2,自己只剩下第五节示例没有研读了,文章先发表了,孤先去看第五节啦!若有所得,再来补充!

感觉自己很棒嘛!

 

posted @ 2018-09-01 16:43  快乐的欧阳天美1114  阅读(369)  评论(0编辑  收藏  举报