Python高级正则表达式

介绍

 

Python高级正则表达式_第1张图片

在我们教程的正则表达式介绍中,我们已经介绍了正则表达式的基本原理。我们已经展示了最简单的正则表达式的样子。我们还学习了如何通过使用 re 模块的 search() 和 match() 方法在 Python 中使用正则表达式。制定和使用字符类的概念,以及预定义的字符类,如 \d、\D、\s、\S 等,现在应该是众所周知的。您一定已经学会了如何使用正则表达式匹配字符串的开头和结尾。您必须知道问号的特殊含义才能使项目可选。我们还引入了量词来任意或在特定范围内重复字符和组。

您还必须熟悉分组的使用以及反向引用的语法和用法。

此外,我们已经解释了 re 模块的匹配对象及其包含的信息,以及如何使用 span()、start()、end() 和 group() 方法检索这些信息。

介绍以一个全面的 Python 示例结束。

在本章中,我们将继续解释正则表达式的语法。我们还将解释 Python 模块 re 的更多方法。例如,如何查找正则表达式的所有匹配子字符串。一项需要使用其他编程语言(如 Perl 或 Java)编程的任务,但可以通过调用 Python 的 re 模块的一个方法来处理。到目前为止,我们只知道如何使用字符类定义字符选择。我们将在教程的本章中演示如何制定子串的交替,

查找所有匹配的子串

Python 模块 re 提供了另一种很棒的方法,这是 Perl 和 Java 等其他语言不提供的。如果要查找字符串中与正则表达式匹配的所有子字符串,则必须在 Perl 和其他语言中使用循环,如下面的 Perl 片段所示:

而 ($string =~ m/regex/g) {
  打印“找到 '$&'。下一次尝试字符”。pos($string)+1 。"\n";
}

在 Python 中要容易得多。无需循环。我们可以使用 re 模块的 findall 方法:

 re.findall(模式,字符串[,标志]) 

findall 返回字符串中模式的所有非重叠匹配项,作为字符串列表。从左到右扫描字符串,并按照找到的顺序返回匹配项。

t = "肥猫不吃燕麦,但老鼠吃蝙蝠。" = 重新findall ( "[force]at" ,  t )
打印( mo )

输出:

['脂肪', '猫', '吃', '燕麦', '老鼠', '吃']

如果模式中存在一个或多个组, findall 将返回一个组列表。如果模式有多个组,这将是一个元组列表。我们将在下一个示例中演示这一点。我们有一个很长的字符串,其中包含各种 Python 培训课程及其日期。第一次调用 findall 时,我们不使用任何分组并因此接收完整的字符串。在下一次调用中,我们使用分组并 findall 返回一个 2 元组列表,每个元组将课程名称作为第一个组成部分,将日期作为第二个组成部分:

进口 重新
课程 =  “Python的训练课程初学者:15/8/2011 - 19/8/2011; Python的培训课程中间体:12 / DEC / 2011 - 16 / DEC / 2011; Python的文本处理场:31月/ 10月/ 2011 - 4/Nov/2011" 
items  =  re findall ( "[^:]*:[^;]*;?" , 课程)
项目

输出:

['Python 初学者培训课程:15/Aug/2011 - 19/Aug/2011;',
 'Python 培训课程中级:12/Dec/2011 - 16/Dec/2011;',
 'Python 文本处理课程:31/Oct/2011 - 4/Nov/2011']
项目 = 重新findall ( "([^:]*):([^;]*;?)" , 课程)
项目

输出:

[('Python 初学者培训课程', '15/Aug/2011 - 19/Aug/2011;'),
 (“Python 培训课程中级”,“2011 年 12 月 12 日 - 2011 年 12 月 16 日;”),
 (“Python 文本处理课程”,“2011 年 10 月 31 日 - 2011 年 11 月 4 日”)]

交替

在我们对正则表达式的介绍中,我们介绍了字符类。字符类提供一组字符中的一个选择。有时我们需要在几个正则表达式之间进行选择。这是一个逻辑“或”,这就是为什么这个结构的符号是“|” 象征。在以下示例中,我们检查伦敦、巴黎、苏黎世、康斯坦茨伯尔尼或斯特拉斯堡中的一个城市是否出现在以“location”一词开头的字符串中:

import  re 
str  =  "课程地点是伦敦或巴黎!" = 重新search ( r "location.*(London|Paris|Zurich|Strasbourg)" , str ) 
if  mo :  print ( mo . group ())

输出:

地点是伦敦或巴黎

如果您认为前面的示例过于人为,那么这里是另一个示例。假设您想过滤电子邮件。您想找到您与 Python 的创建者和设计者 Guido van Rossum 之间的所有通信(对话)。以下正则表达式有助于此目的:

 r"(^To:|^From:) (Guido|van Rossum)" 

此表达式匹配以“To:”或“From:”开头、后跟一个空格、然后是名字“Guido”或姓氏“van Rossum”的所有行。

Python高级正则表达式_第2张图片

编译正则表达式

如果您想在脚本中多次使用相同的正则表达式,使用正则表达式对象可能是个好主意,即编译正则表达式。

一般语法:

 重新编译(模式[,标志]) 

compile 返回一个 regex 对象,可以稍后用于搜索和替换。可以通过指定标志值来修改表达式行为。

缩写 全名 描述
回复 re.ignorecase 使正则表达式不区分大小写
再L 重新定位 某些特殊序列如 \w、\W、\b、\s、\S 的行为将取决于当前的语言环境,即用户的语言、国家等。
再M 重新多线 ^ 和 $ 将在每行的开头和结尾匹配,而不仅仅是在字符串的开头和结尾
re.S 重新打点 点“.” 将匹配每个字符加上换行符
再U 重新.UNICODE 使 \w、\W、\b、\B、\d、\D、\s、\S 依赖于 Unicode 字符属性
雷.X re.VERBOSE 允许“详细的正则表达式”,即忽略空格。这意味着空格、制表符和回车符不匹配。如果要匹配详细正则表达式中的空格,则需要通过在其前面使用反斜杠对其进行转义或将其包含在字符类中来对其进行转义。
# 也被忽略,除非在字符类中或前面有一个非转义的反斜杠。“#”之后的所有内容都将被忽略,直到行尾,因此该字符可用于开始注释。

编译后的常规对象通常不会节省太多时间,因为每当您将它们与 re.search() 或 re.match() 一起使用时,Python 都会在内部编译和缓存正则表达式。未编译的正则表达式唯一需要的额外时间是它需要检查缓存的时间,这是字典的键查找。

使用它们的一个很好的理由是将正则表达式的定义与其使用分开。

例子

我们已经在介绍性章节中引入了匹配英国邮政编码超集的正则表达式:

 r"[Az]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-HJLNP-UW-Z]{2}" 

我们将使用此正则表达式演示如何在以下交互式会话中使用模块 re 的编译功能。正则表达式“regex”用re.compile(regex)编译,编译后的对象保存在对象compiled_re中。现在我们调用对象compiled_re的search()方法:

import  re 
regex  =  r "[Az]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-HJLNP-UW-Z] {2} " 
address  =  "BBC News中心,伦敦,W12 7RJ” 
compiled_re  =  re 编译(正则表达式)
res  =  compiled_re 搜索(地址)
打印(res )

输出:


使用或不使用正则表达式拆分字符串

有一个字符串方法split,可用于将字符串拆分为子字符串列表。

 str.split([sep[, maxsplit]]) 

如您所见,split 方法有两个可选参数。如果没有给出(或者是 None),一个字符串将被分成使用空格作为分隔符的子字符串,即每个纯由空格组成的子字符串都用作分隔符。

Python高级正则表达式_第3张图片

我们用亚伯拉罕·林肯的一句名言来证明这种行为:

law_courses  =  "让每一个美国母亲对趴在她腿上
喋喋不休的婴儿表达对法律的崇敬。让它在学校、神学院和大学里教授。让它写在入门书、拼写书和历书。让它在讲坛上宣讲,在立法大厅里宣扬,并在法庭上执行。总之,让它成为国家的政治宗教。” 法律课程分裂()

输出:

['让',
 '尊敬',
 '为了',
 '这',
 '法律',
 '是',
 '呼吸',
 '经过',
 '每一个',
 '美国人',
 '母亲',
 '到',
 '这',
 '口齿不清',
 '宝贝',
 '那',
 '闲聊',
 '在',
 '她',
 '圈。',
 '让',
 '它',
 '是',
 '教',
 '在',
 '学校',
 '在',
 '神学院,',
 '和',
 '在',
 '大学。',
 '让',
 '它',
 '是',
 '书面',
 '在',
 '引物',
 '拼写',
 '图书,',
 '和',
 '在',
 '年鉴。',
 '让',
 '它',
 '是',
 '传教',
 '从',
 '这',
 '讲坛',
 '宣布',
 '在',
 '立法',
 '大厅,',
 '和',
 '强制',
 '在',
 '这',
 “法院”,
 '的',
 '正义。',
 '和,',
 '在',
 '短的,',
 '让',
 '它',
 '变得',
 '这',
 '政治的',
 '宗教',
 '的',
 '这',
 '国家。']

现在我们来看一个字符串,它可能来自 Excel 或 OpenOffice calc 文件。我们在前面的例子中已经看到 split 将空格作为默认分隔符。我们想在下面的小例子中使用分号作为分隔符来分割字符串。我们唯一要做的就是使用“;” 作为 split() 的参数:

line  =  "James;Miller;teacher;Perl" 
line 拆分(“;” 

输出:

['詹姆斯','米勒','老师','Perl']

split() 方法有另一个可选参数:maxsplit。如果给出了 maxsplit,则最多完成 maxsplit 次分割。这意味着结果列表最多将包含“maxsplit + 1”个元素。我们将在下一个示例中说明 maxsplit 的操作方式:

mammon  =  "世界主要宗教之神。主庙在圣城纽约。" 
金门拆分( " " , 3 )

输出:

['这',
 '上帝',
 '的',
 “世界领先的宗教。主要寺庙在圣城纽约。”]

我们在前面的例子中使用了一个空白作为分隔符字符串,这可能是一个问题:如果连接多个空格或空格,split() 会在每个空格后拆分字符串,这样我们就会得到空字符串和只有我们的结果列表中 ('\t') 内的一个选项卡:

财神 =  “神  \ t世界领先的宗教,行政寺庙是在纽约的圣城。” 
金门拆分( " " , 5 )

输出:

['这',
 '上帝',
 '',
 '\t',
 '的',
 “世界领先的宗教。主要寺庙在圣城纽约。”]

我们可以通过使用 None 作为第一个参数来防止空字符串的分离。现在 split 将使用默认行为,即每个由连接的空白字符组成的子字符串将被视为一个分隔符:

金门拆分(无,5 )

输出:

['这',
 '上帝',
 '的',
 '这',
 "世界的",
 '主导宗教。主庙位于圣城纽约。']

正则表达式拆分

在许多情况下,字符串方法 split() 是正确的工具,但是,如果您想获取文本的裸词,即没有任何特殊字符和空格,该怎么办。如果我们想要这个,我们必须使用 re 模块中的 split 函数。我们用 Ovid 的 Metamorphoses 开头的一段简短文字来说明这种方法:

import  re 
metamorphoses  =  "OF 的身体变成了各种形式,我唱道:Ye Gods,这些奇迹从他们身上涌现,用天体的热度激励我的人数;" 分裂( "\W+" ,变形)

输出:

['的',
 '身体',
 '张',
 'd',
 '到',
 '各种各样的',
 '形式',
 '一世',
 '唱歌',
 '耶',
 '神',
 '从',
 '谁',
 '这些',
 “奇迹”,
 '做过',
 '春天',
 '启发',
 '我的',
 '数字',
 '和',
 '天体',
 '热',
 '']

下面的例子是一个很好的例子,正则表达式确实优于字符串拆分。假设我们有包含姓氏、名字和姓名职业的数据行。我们要清除多余和多余的文本描述的数据行,即“姓氏:”、“姓名:”等,以便我们只有第一列中的姓氏,第二列中的名字和第三栏职业:

import  re 
lines  =  [ “姓氏:奥巴马,前称:巴拉克,职业:总统” “姓氏:默克尔,前称:安吉拉,职业:总理” ] 
for  line  in  lines : 
    print ( re . split ( ",* *\w *: " , 行))    

输出:

['', '奥巴马', '巴拉克', '总统']
['', '默克尔', '安吉拉', '总理']

我们可以使用切片运算符轻松改进脚本,这样我们就不会将空字符串作为结果列表的第一个元素:

import  re 
lines  =  [ “姓氏:奥巴马,前称:巴拉克,职业:总统” “姓氏:默克尔,前称:安吉拉,职业:总理” ] 
for  line  in  lines : 
    print ( re . split ( ",* *\w *: " , 行)[ 1 :])    

输出:

[‘奥巴马’、‘巴拉克’、‘总统’]
[‘默克尔’、‘安吉拉’、‘总理’]

现在来看看完全不同的东西:巴拉克奥巴马和 Python 或更好的蒙蒂 Python 之间存在联系。巨蟒的成员之一约翰·克里斯在 2008 年 4 月告诉西方日报:“我将作为一名演讲撰稿人为他提供服务,因为我认为他是一个聪明人。”

搜索并替换为子

re.sub(正则表达式,替换,主题)

字符串主题中正则表达式 regex 的每个匹配项都将被替换为字符串替换。例子:

” import  re 
str  =  “是的我说是的我会是的。” 
资源 = 重新sub ( "[yY]es" , "no" ,  str )
打印( res )

输出:

不,我说不,我不会。

练习

练习 1

给出了一个名为“order_journal.txt”的文件,格式如下:

%% WriteFile的order_journal.txt
顾客-编号 1289 
T83456
顾客-编号 1289
的客户-编号 1205 
T10032 
B77301
顾客-编号 1205
的客户-编号 1410 
K34001 
T98987
顾客-编号 1410
的客户-编号 1205 
T10786 
C77502
顾客-编号 1205
的客户-编号 1289 
Z22334
客户-编号 1289

输出:

覆盖 order_journal.txt

编写一个新文件'order_journal_regrouped.txt',其中的数据按照如下方式重新分组:

1289,T83456
1289,Z22334
1205,T10032
1205,B77301
1205,T10786
1205,C77502
1410,K34001
1410,T98987

解决方案

练习 1 的解决方案

进口 重新

txt  = 打开“order_journal.txt” 读()

数据 =  {}     #将包含chustomer-id作为键和oders作为值串
 X finditer ( r "customer-id ([\d\n] {4} )(.*?)customer-id \1" ,  txt ,  re . DOTALL ): 
    key ,  values  =  x 组()

    如果 密钥  的数据:
        数据[键]  + =否则:
        数据[键]  = 打开“order_journal_regrouped.txt” “W” 作为 FH :
    用于 密钥  数据:
        用于 art_no  数据[键] 拆分(): 
            fh 写( f " {键} ,{ art_no } \n " )

我们可以检查新创建的文件的内容:

content  =  open ( "order_journal_regrouped.txt" ) 读()
打印(内容)

输出:

1289,T83456
1289,Z22334
1205,T10032
1205,B77301
1205,T10786
1205,C77502
1410,K34001
1410,T98987

你可能感兴趣的:(正则表达式,python,perl)