正则表达式
单字符匹配
字符 | 功能 |
---|---|
. | 匹配任意一个字符(除了\r和\n) |
[ ] | 匹配[ ]中列举的字符 |
\d | 匹配数字(0-9) |
\D | 匹配非数字 |
\s | 匹配空白,即空格、tab键、换行符、\r |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_、中文字符 |
\W | 匹配非单词字符 |
[^x] | 匹配除了x以外的任意字符 |
其中,
\d 等同于[0-9],
\D等同于[^0-9],
\w 等同于[a-zA-Z0-9_],
\W等同于[^a-zA-Z0-9_]。
数量表示
字符 | 功能 |
---|---|
* | 匹配前一个字符0次或无限次 |
+ | 匹配前一个字符1次或无限次 |
? | 匹配前一个字符0次或1次,即要么有要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,} | 匹配前一个字符至少出现m次 |
{m,n} | 匹配前一个字符至少出现m次,至多出现n次 |
其中,
*等同于{0,},
+等同于{1,},
?等同于{0,1}
边界表示
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配单词的边界 |
\B | 匹配非单词边界 |
匹配分组
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 把括号中字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?P<name>) | 给分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
正则表达式测试
了解了正则表达式的匹配规则,怎么测试编写的正则表达式是否正确呢?一般来说,有三种方式:
(1)下载测试工具测试
这里推荐RegexBuddy。RegexBuddy是一款正则表达式测试工具,支持多种编程语言的正则式测试,匹配结果在原文本中高亮。根据编写的正则式,RegexBuddy还提供相应语言的代码自动生成功能。不过网上下到的破解版该功能可能缺失。
除此之外RegExBuilder也是一款正则表达式测试工具。
(2)编写代码测试
也可以用编程语言,调用相应的函数库编写正则表达式测试。比如python的re模块等等。
(3)使用在线正则表达式网站测试
http://tool.chinaz.com/regex
RegexBuddy工具
打开工具是这样的:
Match模块下,可以选择多种语言(此处选了python)。
单字符匹配
示例1:使用 “.” 进行任意字符匹配
Test下输入要匹配的文本内容。Highlight选中表示在原文中高亮匹配结果(黄蓝均是匹配结果,交错显示),List All 表示在下方列出所有匹配结果。
“.” 匹配任意一个字符(除了\r和\n)
示例2:使用“ [ ]” 匹配[ ]中列举的字符
示例3:使用“ \d” 匹配数字0-9
示例4:使用“ \D” 匹配非数字
可以看到,第一行与第二行之间的换行符也被当成非数字匹配,出现了多个空结果(4个空行)。
选择第二个List All模式查看匹配结果,发现四个空行变成了两行。一行为“\r”符,一行为“\n”符。(\n为换行,相当于按了向下的方向键;\r为回车,相当于光标移至当前行行首)
两种不同查看模式下的行数差异可能是RegexBuddy工具自身格式的原因,这里我们知道“\D”模式下,回车换行符\r、\n都会当成非数字被匹配就可以了。
除此之外,\t(一个tab键)也会当成非数字被匹配。
示例5:使用“ \s” 匹配空白
空白包括空格、\t(一个tab键)、\n、\r。
示例6:使用“ \S” 匹配非空白
示例7:使用“ \w” 匹配单词字符
单词字符包括a-z、A-Z、0-9、下划线_、中文字符。
示例8:使用“ \W” 匹配非单词字符
非单词字符即单词字符以外的所有字符,包括空格、\t、\n、\r。
数量表示
示例1:使用“ *” 匹配前一个字符0次或无限次
这里“ w* ”匹配w出现0次或无限次。虽然待匹配文本“we”中只有一个w,但是匹配结果有两个:一个w,一个为空。
这是因为空结果匹配了w出现了0次的情况,我们可以把每个字符看成是自身和空字符的拼接,使用“ x* ”对字符匹配,若x与字符相同,则返回其本身;若不同,返回一个匹配的空结果。(这里与python里使用re匹配的结果稍有不同,re是即使x与字符相同,对其本身也要进行空字符匹配,即会返回两个结果)。
python的re模块,对文本“e”匹配正则表达式“e*”:
>>> import re
>>> s = re.findall('e*', 'e')
>>> print(s)
['e', '']
可以看到,RegexBuddy对匹配到的字符不再进行空字符匹配,对未匹配到的字符才进行空字符匹配。python的re模块对匹配到的字符和未匹配的字符都会进行空字符匹配。
示例2:使用“ +” 匹配前一个字符1次或无限次
示例3:使用“ ?” 匹配前一个字符0次或1次,即要么有要么没有
涉及到可以0次,所以和“ * ”的匹配非常相似。只是“ * ”可以匹配无限次,“ ?” 最多一次。所以连续出现的字符会被拆开匹配。
示例4:使用“ {m}” 匹配前一个字符出现m次
示例5:使用“ {m,}” 匹配前一个字符至少出现m次
示例6:使用“ {m,n}” 匹配前一个字符至少出现m次,至多出现n次
边界表示
示例1: 使用“^” 匹配字符串开头
示例2:使用 “$” 匹配字符串结尾
复合使用“^”和“$”,匹配以“一”开头,以“三”结尾的句子
示例3:使用 “\b” 匹配单词的边界
“\b”匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”(来自维基百科)。
网上有这样一种说法, \b匹配这样的位置:它的前一个字符和后一个字符不是或不存在\w(单词字符,即a-z、A-Z、0-9、_、中文字符)。
示例4:使用 “\B” 匹配非单词边界
“\B”匹配非单词边界。例如,“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
匹配分组
示例1:使用 “| ” 匹配左右任意一个表达式
示例2:使用 “(ab)” 把括号中的字符作为一个分组
python中的“*”默认贪婪匹配,即有多种字符串序列符合匹配模式时,默认选择最长的序列作为匹配结果。若希望选择最短的序列,需要使用“*?”,开启非贪婪模式。
使用“()”匹配出的结果会作为一个分组,正则表达式中有几个括号对,就会对应几个分组。这里,gropu2对应第二次出现括号对标签所对应的分组,显示匹配结果,则只将第二个分组中匹配的结果展示出来。
示例3:使用 “(?P<name>)” 给分组起别名
Python re模块
可参考:
https://www.runoob.com/python/python-reg-expressions.html
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import re
text_str = '南充高坪机场(Nanchong Gaoping Airport ICAO:ZUNC;IATA:NAO)位于南充市主城区正东方向,距离南充市中心直线' \
'距离7.5公里,交通便利,飞行区等级属于4C级预留4D机场。机场属浅丘地形,机场标高365米,全年平均雾日61.7天,净空条件良' \
'。目前已经成为川渝地区航空客货集散枢纽之一。'
pattern = re.compile(r'\w+')
result = pattern.match(text_str)
print('match结果:{}'.format(result.group()))
result = pattern.search(text_str)
print('search结果:{}'.format(result.group()))
result = pattern.findall(text_str)
print('findall结果:{}'.format(result))
注意,match 和 search 是匹配一次,findall 匹配所有。
输出:
提取文本的省市区地理位置信息
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import re
s = '南充高坪机场(Nanchong Gaoping Airport ICAO:ZUNC;IATA:NAO)位于南充市主城区正东方向,距离南充市中心直线' \
'距离7.5公里,交通便利,飞行区等级属于4C级预留4D机场。机场属浅丘地形,机场标高365米,全年平均雾日61.7天,净空条件良' \
'。目前已经成为川渝地区航空客货集散枢纽之一。'
print(' {}'.format(s))
pattern = r'([0-9\d\a-z\d\u4e00-\u9fa5]{2,15}?(?:省|自治区)){0,1}' \
r'([0-9\d\a-z\d\u4e00-\u9fa5]{2,15}?(?:自治州)){0,1}' \
r'([0-9\d\a-z\d\u4e00-\u9fa5]{2,20}?(?:市)){0,1}' \
r'([0-9\d\a-z\d\u4e00-\u9fa5]{2,20}?(?:区|县|旗)){0,1}' \
r'([0-9\d\a-z\d\u4e00-\u9fa5]{2,10}?(?:州)){0,1}'
location = re.findall(r"位于(.+?)[,|。]", s)
print('location:{}'.format(location))
result = re.search(pattern, location[0])
print(result.groups())
针对pattern的多个正则表达式挨个匹配,结果:
题外话,python的translate() 方法,可以根据参数table给出的表转换字符。
参考:https://www.runoob.com/python3/python3-string-translate.html
import string
s = '南充高坪机场(Nanchong Gaoping Airport ICAO:ZUNC;IATA:NAO)位于南充市主城区正东方向,距离南充市中心直线' \
'距离7.5公里,交通便利,飞行区等级属于4C级预留4D机场。机场属浅丘地形,机场标高365米,全年平均雾日61.7天,净空条件良' \
'。目前已经成为川渝地区航空客货集散枢纽之一。'
print(' {}'.format(s))
# 将原句子中的字母大写转小写
s = s.translate(str.maketrans(string.ascii_lowercase, string.ascii_uppercase, string.punctuation))
print('1.{}'.format(s))
# 抛弃句子中的一些标点符号
s = s.translate(str.maketrans('', '', string.punctuation))
print('2.{}'.format(s))
# 抛弃句子中的数字
s = s.translate(str.maketrans('', '', string.digits))
print('3.{}'.format(s))
# 抛弃句子中的字母
s = s.translate(str.maketrans('', '', string.ascii_uppercase+string.ascii_lowercase))
print('4.{}'.format(s))
# 抛弃句子中的指定字符
s = s.translate(str.maketrans('', '', ';[]:()'))
print('5.{}'.format(s))
输出:
Python 2.0中抛弃标点符号和数字:
text = text.translate(None, string.punctuation)
text = text.translate(None, '1234567890')
Python 3.0等价物:
text = text.translate(str.maketrans('','',string.punctuation))
text = text.translate(str.maketrans('','','1234567890'))
maketrans前两个参数表示“不做任何事情”,第三个参数表示将任何标点或数字转换为None(即删除它们)。