本文介绍了Python对于正则表达式的支持,包括正则表达式基础以及Python正则表达式标准库的完整介绍及使用示例。本文的内容不包括如何编写高效的正则表达式、如何优化正则表达式,这些主题请查看其他教程。
注意:本文基于Python2.4完成;如果看到不明白的词汇请记得百度谷歌或维基,whatever。
尊重作者的劳动,转载请注明作者及原文地址 >.<html
最短匹
正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。
下图展示了使用正则表达式进行匹配的流程:
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。
下图列出了Python支持的正则表达式元字符和语法:
正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式"ab*"如果用于查找"abbbc",将找到"abbb"。而如果使用非贪婪的数量词"ab*?",将找到"a"。
Jeffrey: 我加一点自己做项目过程中遇到的这种匹配方式的理解:
最短匹配:.*? http://deerchao.net/tutorials/regex/regex.htm
假如,我们有以下的代码存放在Test_1.xml中(注意:这里不仅限于xml文件,同样的也适应于其它的任何的文件):我们的
要匹配到原文件中的下列内容
<pre name="code" class="html"><span style="color:#FF0000;"><Response Status="OKAY" CongLvl="LEVEL0" OverallProvTime="4026852" TimeInReqQueue="228" DbCommitTime="6371" RequestId="100000">
<CapacityParms>
<Category>RESIDENTIALSUBSCRIBER_R2</Category>
<FeatureSetName>R1 FEATURE SET</FeatureSetName>
<OfficeId>ylvJrPbcGgHE</OfficeId>
<CurrentCnt>0</CurrentCnt>
<LimitCnt>0000050</LimitCnt>
<SpareCapacity>0</SpareCapacity>
<TasUnequalDistribution>0</TasUnequalDistribution>
</CapacityParms>
</Response></span>
这个是我的re_test.py代码文件,这个文件中到了两种匹配方式,当然,只有def re_testsearch()才可以满足我们的要求,因为它是实现的是段落匹配(因为这个里面用了
re.S)
#-*- coding:utf-8 -*-
#最上一句如果不加上的话不能输入汉字,不然编译不过
#/usr/bin/python
import os
import sys
import re
####################################################
# 一行一行的匹配
# 比如Test_2.xml中的内容如下:
# <Response Staus="OKAY" CongLvl="jeffrey">
# <a>test1</a>
# <b>test2</b>
# </Response>
# <Response Staus="OKAY" CongLvl="guan">
# <a>test1</a>
# <b>test2</b>
# </Response>
# <Response Staus="OKAY" CongLvl="zenghui">
# <a>test1</a>
# <b>test2</b>
# </Response>
# <Response Staus="OKAY" CongLvl="jeguan">
# <a>test1</a>
# <b>test2</b>
# </Response>
# 那么匹配后的得到结果为
# <Response Staus="OKAY" CongLvl="jeffrey">
# <Response Staus="OKAY" CongLvl="guan">
# <Response Staus="OKAY" CongLvl="zenghui">
# <Response Staus="OKAY" CongLvl="jeguan">
#
####################################################
def re_test():
# 原文件
filename = r'C:\Users\jeguan\Desktop\Test_2.xml'
# 匹配得到的内容存储在Test_2_bk.xml中
new_file = r'C:\Users\jeguan\Desktop\Test_2_bk.xml'
# 打开原文件
open_file = open(filename, 'r')
read_file = open_file.readlines()
# 打开目标文件,即:存放匹配结果的文件
newfile = open(new_file, 'wb')
# 匹配以<Response开头并且有CongLvl字符串的行,注意,
# 这里是非lazzy匹配,并且是一行一行匹配,即,遇到
# '\n'就会结束
patt = re.compile(r'^<Response.*CongLvl.*')
# 遍历原文件的所有行,如果找到就会存盘
for line in read_file:
match = patt.search(line)
if match:
m = match.group(0)
newfile.write(m)
open_file.close()
newfile.close()
#################################################################################
# 多行匹配,即:可以匹配一个文本中的特定段落。这里主要是要用到re模块中的re.S
# 它表示当用'.'来进行匹配的时候,可以忽略掉'\n',这一点与'.'正常的规则是不一样的
#
# 另外一个要注意的地方是这里使用了Lazzy匹配的方式。当有多个Response>出现的时候,
# 它只会匹配第一次出现的地方。比如:
# <Response Staus="OKAY" CongLvl="jeguan">
# <a>test1</a>
# <b>test2</b>
# </Response>
# <Response Staus="OKAY" CongLvl="zenghuiguan">
# <a>test3</a>
# <b>test4</b>
# </Response>
# 当用lazzy方式的时候,只会匹配到第一次出现Response>的地方
# 本文中匹配得到的结果为:
# <Response Staus="OKAY" CongLvl="jeguan">
# <a>test1</a>
# <b>test2</b>
# </Response>
#
################################################################################
def re_testsearch():
#
filename = r'C:\Users\jeguan\Desktop\Test_2.xml'
new_file = r'C:\Users\jeguan\Desktop\Test_2_bk.xml'
open_file = open(filename, 'r')
read_file = open_file.readlines()
newfile = open(new_file, 'wb')
# re.S means: Make the '.' special character match any character at all,
# including a newline; without this flag, '.' will match anything except a newline.
# '(.+?)' means: this is a greedy match. When the fist 'Response>' is found, then
# it will not try to match the next 'Response>'
<span style="color:#CC0000;">patt = re.compile(r'<Response Status="OKAY" CongLvl="LEVEL0"*(.+?)Response>', <strong>re.S</strong>)</span>
str1 = ""
# 把读出的行放在str1中
for line in read_file:
str1 = str1 + line
match1 = patt.search(str1)
newfile.write(match1.group(0))
print(match1.group())
if __name__ == "__main__":
re_testsearch()
我们用re_testsearch()这个函数来匹配最终的结果如下所示,得到了我们的要求:
<Response Status="OKAY" CongLvl="LEVEL0" OverallProvTime="4026852" TimeInReqQueue="228" DbCommitTime="6371" RequestId="100
<CapacityParms>
<Category>RESIDENTIALSUBSCRIBER_R2</Category>
<FeatureSetName>R1 FEATURE SET</FeatureSetName>
<OfficeId>ylvJrPbcGgHE</OfficeId>
<CurrentCnt>0</CurrentCnt>
<LimitCnt>0000050</LimitCnt>
<SpareCapacity>0</SpareCapacity>
<TasUnequalDistribution>0</TasUnequalDistribution>
</CapacityParms>
</Response>
但是,如果我们使用re_test()来试图匹配的话,不会得到上面的这个结果,相反,只会找到
<Response Status="OKAY" CongLvl="LEVEL0" OverallProvTime="4026852" TimeInReqQueue="228" DbCommitTime="6371" RequestId="100000">
与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\\"表示。同样,匹配一个数字的"\\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
正则表达式提供了一些可用的匹配模式,比如忽略大小写、多行匹配等,这部分内容将在Pattern类的工厂方法re.compile(pattern[, flags])中一起介绍。
Python通过re模块提供对正则表达式的支持。使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# encoding: UTF-8
import
re
# 将正则表达式编译成Pattern对象
pattern
=
re.
compile
(r
'hello'
)
# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
match
=
pattern.match(
'hello world!'
)
if
match:
# 使用Match获得分组信息
print
match.group()
### 输出 ###
# hello
|
re.compile(strPattern[, flag]):
这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
可选值有:
1
2
3
4
|
a
=
re.
compile
(r
"""\d + # the integral part
\. # the decimal point
\d * # some fractional digits"""
, re.X)
b
=
re.
compile
(r
"\d+\.\d*"
)
|
re提供了众多模块方法用于完成正则表达式的功能。这些方法可以使用Pattern实例的相应方法替代,唯一的好处是少写一行re.compile()代码,但同时也无法复用编译后的Pattern对象。这些方法将在Pattern类的实例方法部分一起介绍。如上面这个例子可以简写为:
1
2
|
m
=
re.match(r
'hello'
,
'hello world!'
)
print
m.group()
|
re模块还提供了一个方法escape(string),用于将string中的正则表达式元字符如*/+/?等之前加上转义符再返回,在需要大量匹配元字符时有那么一点用。
Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。
属性:
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import
re
m
=
re.match(r
'(\w+) (\w+)(?P<sign>.*)'
,
'hello world!'
)
print
"m.string:"
, m.string
print
"m.re:"
, m.re
print
"m.pos:"
, m.pos
print
"m.endpos:"
, m.endpos
print
"m.lastindex:"
, m.lastindex
print
"m.lastgroup:"
, m.lastgroup
print
"m.group(1,2):"
, m.group(
1
,
2
)
print
"m.groups():"
, m.groups()
print
"m.groupdict():"
, m.groupdict()
print
"m.start(2):"
, m.start(
2
)
print
"m.end(2):"
, m.end(
2
)
print
"m.span(2):"
, m.span(
2
)
print
r
"m.expand(r'\2 \1\3'):"
, m.expand(r
'\2 \1\3'
)
### output ###
# m.string: hello world!
# m.re: <_sre.SRE_Pattern object at 0x016E1A38>
# m.pos: 0
# m.endpos: 12
# m.lastindex: 3
# m.lastgroup: sign
# m.group(1,2): ('hello', 'world')
# m.groups(): ('hello', 'world', '!')
# m.groupdict(): {'sign': '!'}
# m.start(2): 6
# m.end(2): 11
# m.span(2): (6, 11)
# m.expand(r'\2 \1\3'): world hello!
|
Pattern对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。
Pattern不能直接实例化,必须使用re.compile()进行构造。
Pattern提供了几个可读属性用于获取表达式的相关信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
re
p
=
re.
compile
(r
'(\w+) (\w+)(?P<sign>.*)'
, re.DOTALL)
print
"p.pattern:"
, p.pattern
print
"p.flags:"
, p.flags
print
"p.groups:"
, p.groups
print
"p.groupindex:"
, p.groupindex
### output ###
# p.pattern: (\w+) (\w+)(?P<sign>.*)
# p.flags: 16
# p.groups: 3
# p.groupindex: {'sign': 3}
|
实例方法[ | re模块方法]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# encoding: UTF-8
import
re
# 将正则表达式编译成Pattern对象
pattern
=
re.
compile
(r
'world'
)
# 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
# 这个例子中使用match()无法成功匹配
match
=
pattern.search(
'hello world!'
)
if
match:
# 使用Match获得分组信息
print
match.group()
### 输出 ###
# world
|
1
2
3
4
5
6
7
|
import
re
p
=
re.
compile
(r
'\d+'
)
print
p.split(
'one1two2three3four4'
)
### output ###
# ['one', 'two', 'three', 'four', '']
|
1
2
3
4
5
6
7
|
import
re
p
=
re.
compile
(r
'\d+'
)
print
p.findall(
'one1two2three3four4'
)
### output ###
# ['1', '2', '3', '4']
|
1
2
3
4
5
6
7
8
|
import
re
p
=
re.
compile
(r
'\d+'
)
for
m
in
p.finditer(
'one1two2three3four4'
):
print
m.group(),
### output ###
# 1 2 3 4
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import
re
p
=
re.
compile
(r
'(\w+) (\w+)'
)
s
=
'i say, hello world!'
print
p.sub(r
'\2 \1'
, s)
def
func(m):
return
m.group(
1
).title()
+
' '
+
m.group(
2
).title()
print
p.sub(func, s)
### output ###
# say i, world hello!
# I Say, Hello World!
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import
re
p
=
re.
compile
(r
'(\w+) (\w+)'
)
s
=
'i say, hello world!'
print
p.subn(r
'\2 \1'
, s)
def
func(m):
return
m.group(
1
).title()
+
' '
+
m.group(
2
).title()
print
p.subn(func, s)
### output ###
# ('say i, world hello!', 2)
# ('I Say, Hello World!', 2)
|
以上就是Python对于正则表达式的支持。熟练掌握正则表达式是每一个程序员必须具备的技能,这年头没有不与字符串打交道的程序了。笔者也处于初级阶段,与君共勉,^_^
另外,图中的特殊构造部分没有举出例子,用到这些的正则表达式是具有一定难度的。有兴趣可以思考一下,如何匹配不是以abc开头的单词,^_^
全文结束
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland