python核心编程--第十五章

15.1 介绍/动机

正则表达式的用处就是:处理文本和数据。

15.1.1 您的第一个正则表达式

实际上我在书上找不到我的第一个正则表达式的例子。。。。。。

于是翻了《python标准库》,找到第一个例子:

import re
pattern = "this"
text = "does this text match the pattern?"
match = re.search(pattern, text)
s = match.start()
e = match.end()
print "found %s\nin %s\nfrom %d to %d(%s)" %\
      (match.re.pattern, match.string, s, e, text[s:e])
程序输出:
>>> 
found this
in does this text match the pattern?
from 5 to 9(this)
15.2 正则表达式使用的特殊符号和字符

PS:这些特殊符号看起来简单,用起来会让人崩溃的。。。。

15.2.1 用管道符号(|)匹配多个正则表达式模式

选择被管道符号分隔的多个不同的正则表达式中的一个。比如:

正则表达式模式                    匹配的字符串

at|home                            at, home

r2d2|c3po                            r2d2,c3po

bat|bet|bit                        bat,bet,bit

有了这个符号,正则表达式的灵活性增强了,使得它可以匹配不止一个字符串。

15.2.2 匹配任意一个单个的字符(.)

“.”这个字符可以匹配除了换行符外地所有字符,当然如果要匹配符号"."的话,请用“\”对它进行转义。

15.2.4 从字符串的开头或结尾或单词边界开始匹配(^/$/\b/\B)

如果想从字
符串的开头开始匹配一个模式,你必须用脱字符号( ^ , 即,Caret)或特殊字符 \A (大写字母A 前面加上一个反斜线). 后者主要是为那些没有caret 符号的键盘使用的,比如说国际键盘。类似,美元符号 ( $ ) 或 \Z 是用来(零宽度)匹配字符串的结尾的。

正则表达式模式 匹配的字符串
^From                 匹配任何以From 开始的字符串
/bin/tcsh$             匹配任何以 /bin/tcsh 结束的字符串
^Subject: hi$         匹配仅由 Subject: hi 组成的字符串

特别说明,如果你想匹配这两个字符中的任何一个(或全部),就必须用反斜线进行转义。例如,如果你想匹配任何以 美元符号($) 结尾的字符串,一个可行的解决办法是用正则表达式模式“.*\$$”.
特殊字符 \b and \B 用来匹配单词边界。两者之间的区别是,\b 匹配的模式是一个单词边界,就是说,与之对应的模式一定在一个单词的开头,不论这个单词的前面是有字符(该词在一个字符串的中间),还是没有字符(该单词在一行的起始处)。同样地,\B 只匹配出现在一个单词中间的模式(即,不在单词边界上的字符)。看下面几个例子:

RE Pattern         Strings Matched
the                     任何包含有"the"的字符串
\bthe                 任何以"the"开始的字符串
\bthe\b             仅匹配单词 “the”
\Bthe                 任意包含“the”但不以“the”开头的单词

15.2.5 创建字符类([])

尽管点号可用来匹配任意字符,但有时候你需要匹配某些个特殊的字符。正因为如此,方括号( [ ] )被发明出来。使用方括号的正则表达式会匹配方括号里的任何一个字符。

正则表达式模式             匹配的字符串
b[aeiu]t                     bat, bet, bit, but
[cr][23][dp][o2]         一个包含4 个字符的字符串: 第一个字符是 “r” 或 “c”,后面是 “2”
                                或 “3”,再接下来是 “d” 或 “p”,最后是 “o” 或 “2“ ,例
                                如:c2do, r3p2, r2d2, c3po, 等等。

关于正则表达式 “[cr][23][dp][o2]” 的一点要说明: 如果只让 “r2d2” 或 “c3po” 成为有效的字符串,就需要限定更为严格的正则表达式。但因为方括号只有"逻辑或"(“logical OR”)的功能,所以用方括号不能实现这一限定要求。唯一的解决办法是用管道符号(pipe), 例如:“r2d2|c3po”.

对仅有单个字符的正则表达式 ,使用管道符号和方括号的效果是等价的。举例来说,正则表达式“ab” , 只匹配以"a"开头后面再跟一个"b"的字符串。如果我们只想要一个字母的字符串,即,“a” 或者 “b” 中的一个,就可以使用正则表达式 “[ab]” 。因为 “a” 和 “b” 是单个的字符串,我们也可以用正则表达式 “a|b”。但是,如果我们想用模式匹配"ab",后面接着是"cd"的字符串,就不能用方括号了,因为方括号只适用于单个字符的情况。这样,唯一的办法是用“ab|cd”,这和我们刚才提到的 “r2d2|c3po”的道理是相同的。

15.2.6 指定范围(-)和否定(^)

方括号里一对符号中间的连字符(-)用来表示一个字符的范围,例如A-Z,0-9,这是一个按字母顺序排序的范围,所以它不限于只用在字母和十进制数字上。另外,如果在左方括号后第一个字符是上箭头符号(^),就表示不匹配指定字符集里的任意字符。

正则表达式模式 匹配的字符
z.[0-9]                         字符"z",后面跟任意一个字符,然后是一个十进制数字
[r-u][env-y][us]             “r” “s,” “t” 或 “u” 中的任意一个字符,后面跟的是 “e,”
                                    “n,” “v,” “w,” “x,” 或 “y”中的任意一个字符,再后面
                                    是字符“u” 或 “s”.
[^aeiou]                         一个非元音字符
[^\t\n]                         除TAB 制表符和换行符以外的任意一个字符
["-a]                             在使用ASCII 字符集的系统中,顺序值在‘"‘ 和 “a”之间 的
                                    任意一个字符,即,顺序号在34 和97 之间的某一个字符。

15.2.7 使用闭包操作符(*,+,?,{})实现多次出现/重复匹配

星号或称星号操作符匹配它左边那个正则表达式出现零次或零次以上的情况。加号(+)操作符匹配它左边那个正则表达式模式至少出现一次的情况(它也被称为正闭包操作符),而问号操作符( ? )匹配它左边那个正则表达式模式出现零次或一次的情况。
还有花括号操作符({ }), 花括号里可以是单个的值,也可以是由逗号分开的一对值。如果是一个值,如,{N},则表示匹配N 次出现;如果是一对值,即,{M, N},就表示匹配M 次到N 次出现。可以在这些符号前用反斜线进行转义,使它们失去特殊作用,即, “\*” 将匹配星号本身等。
在上表中,我们注意到问号出现了不只一次(被重载),问号有两种含义:1.单独使用时表示匹配出现零次或一次的情况,2.紧跟在表示重复的元字符后面时,表示要求搜索引擎匹配的字符串越短越好。例如:(+?)

[dn]ot?         字符"d"或"n", 后面是一个"o", 最后是最多一个字符"t",即,do, no, dot,not
0?[1-9]         从1 到9 中的任意一位数字,前面可能还有一个"0". 例如:可以把它看
                    成一月到九月的数字表示形式,不管是一位数字还是两位数字的表示形式。
[0-9]{15,16}     15 或16 位数字表示,例如:信用卡号码
</?[^>]+>        匹配所有合法(和无效的)HTML 标签的字符串
[KQRBNP][a-h][1-8]-[a-h][1-8]     在“长代数”记谱法中,表示的国际象棋合法的棋盘。
                                                即, “K,” “Q,” “R,” “B,” “N,” 或 “P” 等
                                                字母后面加上两个用连字符连在一起的"a1"到"h8"之间
                                                的棋盘坐标。前面的编号表示从哪里开始走棋,后面的
                                                编号代表走到哪个位置(棋格)去。

15.2.8 特殊字符表示字符集

比如\d表示十进制,\w表示整个字符数字的字符集,\s代表空白字符。而大写形式则表示不匹配,比如\D表示非十进制数字的字符。

正则表达式模式 匹配的字符串
\w+-\d+             一个由字母或数字组成的字符串,和至少一个数字,两部分中间由连字符连接
[A-Za-z]\w*         第一个字符是字母,其余字符(如果存在的话),是字母或数字(它几乎
                            等价于Python 语言中合法的标识符[见参考练习])
\d{3}-\d{3}-\d{4}     (美国)电话号码,前面带区号前缀,例如 800-555-1212
\w+@\w+\.com         简单的XXX@YYY.com 格式的电子邮件地址

15.2.9 用圆括号(())组建组

现在,或许我们可以匹配一个字符串和丢弃那些不匹配的字符串了,但有时候,我们也许对匹配的数据本身更有兴趣。我们不仅想知道是否整个字符串匹配我们的条件(正则表达式),还想在匹配成功时取出某个特定的字符串或子字符串。要达到这个目的,只需要给正则表达式的两边加上一对圆括号。
一对圆括号(()) 和正则表达式一起使用时可以实现以下任意一个(或两个)功能:

1. 对正则表达式进行分组

2. 匹配子组

使用圆括号的一个额外好处就是匹配的子串会被保存到一个子组,便于今后使用。

为什么需要使用子组匹配呢? 主要是有时除了进行匹配操作外,你还想要提取匹配模式的内容。如果想知道在成功的匹配中,是哪些字符串匹配了我们的正则表达式模式。例如,我们想用正则表达式“\w+-\d+”匹配一些内容,但又想把第一部分的字符和第二部分的数字分别保存,该怎么做呢?

如果我们给两个子模式都加上圆括号,即,将它写成 “(\w+)-(\d+)” , 那我们就可以对这两个匹配的子组分别进行访问了。
正则表达式模式 匹配的字符串
\d+(\.\d*)?             表示简单的浮点数,即, 任意个十进制数字,后面跟一个可选的小
                            数点,然后再接零或多个十进制数字。例如:“0.004,” “2,” “75.”,等等。
(Mr?s?\. )?[A-Z][a-z]* [ A-Za-z-]+     名字和姓氏,对名字的限制(首字母大写,其它字
                                                    母(如果存在)小写), 全名前有可选的称谓(“Mr.,”
                                                    “Mrs.,” “Ms.,” 或 “M.,”),姓氏没有什么
                                                    限制,允许有多个单词、横线、大写字母。

15.3 正则表达式和python语言

15.3.1 re模块:核心函数和方法

compile(pattern,flags=0)  对正则表达式模式pattern 进行编译,flags 是可选标志符,并返回一个regex 对象
match(pattern,string, flags=0)        尝试用正则表达式模式pattern 匹配字符串string,

                                            flags 是可选标志符,如果匹配成功,则返回一个匹配对象;否则返回None


search(pattern,string, flags=0)         在字符串string 中查找正则表达式模式pattern 的第
                                                    一次出现,flags 是可选标志符,如果匹配成功,则返回
                                                    一个匹配对象;否则返回None
findall(pattern,string[,flags])               在字符串string 中查找正则表达式模式pattern 的所有
                                                    (非重复)出现;返回一个匹配对象的列表


finditer(pattern,string[, flags])         和findall()相同,但返回的不是列表而是迭代器;对
                                                   于每个匹配,该迭代器返回一个匹配对象


split(pattern,string, max=0)             根据正则表达式pattern 中的分隔符把字符string 分割
                                                    为一个列表,返回成功匹配的列表,最多分割max 次(默
                                                    认是分割所有匹配的地方)。


sub(pattern, repl, string, max=0)         把字符串string 中所有匹配正则表达式pattern 的地
                                                    方替换成字符串repl,如果max 的值没有给出,则对所有
                                                    匹配的地方进行替换(另外,请参考subn(),它还会返回
                                                    一个表示替换次数的数值)。


group(num=0)                                 返回全部匹配对象(或指定编号是num 的子组)
groups()                                         返回一个包含全部匹配的子组的元组

15.3.2 使用compile()编译正则表达式

如果大量使用到re,则请编译。

15.3.3 匹配对象和group(),groups()方法

在处理正则表达式时,除regex 对象外,还有另一种对象类型 - 匹配对象。这些对象是在match()或search()被成功调用之后所返回的结果。匹配对象有两个主要方法:group() 和 groups().
group()方法或者返回所有匹配对象或是根据要求返回某个特定子组。groups()则很简单,它返回一个包含唯一或所有子组的元组。如果正则表达式中没有子组的话, groups() 将返回一个空元组,而group()仍会返回全部匹配对象。

15.3.4 用match()匹配字符串

match()函数尝试从字符串的开头开始对模式进行匹配。如果匹配成功,就返回一个匹配对象,而如果匹配失
败了,就返回None。匹配对象的group() 方法可以用来显示那个成功的匹配。

>>> import re
>>> m = re.match("foo","foobar")
>>> if m is not None:
	m.group()

	
'foo'
>>> m = re.match("foo","barfoo, this is a test")
>>> m.group()

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    m.group()
AttributeError: 'NoneType' object has no attribute 'group'
>>> m = re.match("foo","food on the table")
>>> m.group()
'foo'
这里推荐使用if判断语句,毕竟没match到的时候,编译表达式返回的是None,而None并没有group方法。

我们可以写的更加简单:

>>> re.match("foo","food on the table").group()
'foo'

15.3.5 search()在一个字符串中查找一个模式(搜索与匹配的比较)

我们会发现,我们要搜索的模式出现在一个字符串中间的几率要比出现在字符串开头的几率更大一些。那么,serarch和match有什么区别吗?

search查找字符串中模式首次出现的位置,而不是尝试在起始处匹配。严格的说,search是从左到右进行搜索的。

>>> m = re.search("foo","seafood")
>>> if m is not None:
	m.group()

	
'foo'

15.3.6 匹配多个字符串(|)

>>> bt = "bat|bet|bit"
>>> m = re.match(bt, "bat")
>>> if m is not None:
	m.group()

	
'bat'
>>> m = re.match(bt, "blt")
>>> if m is not None:
	m.group()

	
>>> m = re.match(bt, "he bit me!")
>>> if m is not None:
	m.group()

	
>>> m = re.search(bt, "he bit me!")
>>> if m is not None:
	m.group()

	
'bit'

15.3.7 匹配任意单个字符(.)

明点号是不能匹配换行符或非字符(即,空字符串)的:

>>> anyend = ".end"
>>> m = re.match(anyend, "bend")
>>> if m is not None:
	m.group()

	
'bend'
>>> m = re.match(anyend, "end")
>>> if m is not None:
	m.group()

	
>>> m = re.match(anyend, "\nend")
>>> if m is not None:
	m.group()

	
>>> m = re.search(anyend, "the end.")
>>> if m is not None:
	m.group()

	
' end'
>>> m = re.match(anyend, " end")
>>> if m is not None:
	m.group()

	
' end'
可以匹配空格符。。。。

下面的例子是来搜索一个真正点号(小数点)的正则表达式,在正则表达式中,用反斜线对它进行转义,使点号失去它的特殊意义:

>>> patt314 = "3.14"
>>> pi_patt = "3\.14"
>>> m = re.match(pi_patt, "3.14")
>>> if m is not None:
	m.group()

	
'3.14'
>>> m = re.match(patt314, "3014")
>>> if m is not None:
	m.group()

	
'3014'
>>> m = re.match(patt314,"3.14")
>>> if m is not None:
	m.group()

	
'3.14'

15.3.8 创建字符集和([])

>>> m = re.match("[cr][23][dp][o2]","c3po")
>>> if m is not None:
	m.group()

	
'c3po'
>>> m = re.match("[cr][23][dp][o2]", "c2do")
>>> if m is not None:
	m.group()

	
'c2do'
>>> m = re.match("r2d2|c3po", "c2do")
>>> if m is not None:
	m.group()

	
>>> m = re.match("r2d2|c3po", "r2d2")
>>> if m is not None:
	m.group()

	
'r2d2'

15.3.9 重复,特殊字符和子组

>>> patt = "\w+@(\w+\.)?\w+\.com"
>>> re.match(patt, "nobody@xxx.com").group()
'nobody@xxx.com'
>>> re.match(patt, "nobody@www.xxx.com").group()
'nobody@www.xxx.com'
如果要求拥有任意数量的子域名存在呢?
>>> patt = "\w+@(\w+\.)*\w+\.com"
>>> re.match(patt, "nobody@www.xxx.yyy.zzz.com").group()
'nobody@www.xxx.yyy.zzz.com'
>>> m = re.match("(\w\w\w)-(\d\d\d)","abc-123")
>>> m.group()
'abc-123'
>>> m.group(1)
'abc'
>>> m.group(2)
'123'
>>> m.groups()
('abc', '123')
下面一些简单的例子,可以更深入理解group。
>>> m = re.match("ab","ab")
>>> m.group()
'ab'
>>> m.groups()
()
>>> m = re.match("(ab)","ab")
>>> m.group()
'ab'
>>> m.group(1)
'ab'
>>> m.groups()
('ab',)
>>> m = re.match("(a)(b)","ab")
>>> m.group()
'ab'
>>> m.group(1)
'a'
>>> m.group(2)
'b'
>>> m.groups()
('a', 'b')
>>> m = re.match("(a(b))","ab")
>>> m.group()
'ab'
>>> m.group(1)
'ab'
>>> m.group(2)
'b'
>>> m.groups()
('ab', 'b')

15.3.10 从字符串的开头或结尾匹配及在单词边界上的匹配

>>> m = re.search('^The', 'The end.') # match #匹配
>>> if m is not None: m.group()
...
'The'
>>> m = re.search('^The', 'end. The') # not at beginning #不在开头
>>> if m is not None: m.group()
...
>>> m = re.search(r'\bthe', 'bite the dog') # at a boundary #在词边界
>>> if m is not None: m.group()
...
'the'
>>> m = re.search(r'\bthe', 'bitethe dog') # no boundary #无边界
>>> if m is not None: m.group()
...
>>> m = re.search(r'\Bthe', 'bitethe dog') # no boundary #无边界
>>> if m is not None: m.group()
...
'the'

15.3.11 用findall()找到每个出现的匹配部分

findall()返回所有匹配部分的列表。

>>> re.findall("car","car")
['car']
>>> re.findall("car","scary")
['car']
>>> re.findall("car","carry the barcardi to the car")
['car', 'car', 'car']

15.3.12 用sub()[和subn()]进行搜索和替换

有两种函数/方法用于完成搜索和代替的功能: sub()和subn(). 二者几乎是一样的,都是将某字符串中所有匹配正则表达式模式的部分进行替换。用来替换的部分通常是一个字符串,但也可能是一个函数,该函数返回一个用来替换的字符串。subn()和sub()一样,但它还返回一个表示替换次数的数字,替换后的字符串和表示替换次数的数字作为一个元组的元素返回。

>>> re.sub("x","mr.smith","attn:x\n\ndear x,\n")
'attn:mr.smith\n\ndear mr.smith,\n'
>>> re.subn("x","mr.smith","attn:x\n\ndear x,\n")
('attn:mr.smith\n\ndear mr.smith,\n', 2)
>>> print re.sub("x","mr.smith","attn:x\n\ndear x,\n")
attn:mr.smith

dear mr.smith,

>>> re.sub("[ae]","x", "abcdef")
'xbcdxf'
>>> re.subn("[ae]","x", "abcdef")
('xbcdxf', 2)

15.4 正则表达式示例

from random import randint, choice
from string import lowercase
from sys import maxint
from time import ctime

doms = ("com","edu", "net","org","gov")
for i in range(randint(5,10)):
    dtint = randint(0, maxint - 1)
    dtstr = ctime(dtint)

shorter = randint(4, 7)
em = ""
for j in range(shorter):
    em += choice(lowercase)
longer = randint(shorter, 12)
dn = ""
for j in range(longer):
    dn += choice(lowercase)
print "%s::%s@%s.%s::%d-%d-%d" % (dtstr, em, dn, choice(doms), dtint, shorter, longer)
程序输出:
>>> 
Sat Oct 29 11:09:21 1988::zotxm@rxlldccck.com::594097761-5-9


15.5 练习

15-1. 识别下列字符串:“bat,” “bit,” “but,” “hat,” “hit,” 或 “hut”

我将所有匹配的用findall全部列出来:

import re
pattern = "(bat|bit|but|hat|hit|hut)"
string = "bat is not bit, but hit like hut"
print re.findall(pattern, string)
程序输出:
>>> 
['bat', 'bit', 'but', 'hit', 'hut']

15–2. 匹配用一个空格分隔的任意一对单词,比如,名和姓。

import re
pattern = "(\w+ \w+)"
string = "fzyz abc"
print re.match(pattern, string).group()
程序输出:
>>> 
fzyz abc
15–3. 匹配用一个逗号和一个空格分开的一个单词和一个字母。例如,英文人名中的姓和名的首字母。
import re
pattern = "(\w+), (\w+)"
string = "fzyz, abc"
result = re.match(pattern, string)
print result.group()
print result.group(1)
print result.group(2)
程序输出:
>>> 
fzyz, abc
fzyz
abc

15-4 匹配所有合法的Python 标识符。

import re
pattern = "([a-zA-Z_]\w*)"
string = "fzyz _abc 1aa"
result = re.findall(pattern, string)
print result
程序输出:
>>> 
['fzyz', '_abc', 'aa']
这里,我无法将1aa给过滤掉,那应该怎么弄呢?

进行修改后,发现可以用\b,注意用r""

import re
pattern = r"\b[a-zA-Z_]\w*"
string = "fzyz _abc 1aa"
result = re.findall(pattern, string)
print result
程序输出:
>>> 
['fzyz', '_abc']

15–5. 请根据您(读者)本地关于地址的格式写法匹配一个街道地址(你写出的正则表达式要尽可能通用以匹配任意数目的表示街道名字的单词,包括类型指示)。比如,美国的街道地址使用这样的格式:1180 Bordeaux Drive. 使你写的正则表达式尽可能通用,要求能够匹配多个单词的街道名字,如:3120 De la Cruz Boulevard.

import re
pattern = r"\d+( [a-zA-Z]+)+"
string = "3120 De la Cruz Boulevard"
result = re.match(pattern, string)
print result.group()
程序输出:
>>> 
3120 De la Cruz Boulevard
15–6. 匹配简单的以“www.”开头,以“.com”作结尾的Web 域名,例如:www.yahoo.com.  加题:使你写的正则表达式还支持其他顶级域名:.edu, .net 等,比如:www.ucsc.edu.
import re
pattern = r"\bwww\.\w+\.[com|edu]*"
string = "www.yahoo.com www.ucsc.edu"
result = re.findall(pattern, string)
print result
程序输出:
>>> 
['www.yahoo.com', 'www.ucsc.edu']
15-7. 匹配全体Python 整数的字符串表示形式的集合。

我表示这道题没看懂。。。。,直接用\d????

15–13. type(). type()内建函数返回一个对象类型,此对象显示为Python 的字符串形式,
如下所示:
>>> type(0)
<type 'int'>
>>> type(.34)
<type 'float'>
>>> type(dir)
<type 'builtin_function_or_method'>
请写一个正则表达式,能从这个字符串中提取出类型的名字。

import re
pattern = "'\w+'"
string = "<type 'int'> <type 'float'> <type 'builtin_function_or_method'>"
result = re.findall(pattern, string)
print result
程序输出:
>>> 
["'int'", "'float'", "'builtin_function_or_method'"]

15–15. 正则表达式。在15.2 小节里,我们给出一个匹配信用卡卡号的模式:(“[0-9]{15,16}”).但这个模式不允许用连字符号分割信用卡卡号中的数字。请写出一个允许使用连字符的正则表达式,但要求连字符必须出现在正确的位置。例如,15 位的信用卡卡号的格式是4-6-5,表示四个数字,一个连字符,后面接六个数字、一个连字符,最后是五个数字。16 位的信用卡卡号的格式是4-4-4-4,数位不足时,添0 补位。附加题:有一个用于确定某个信用卡卡号是否合法的算法。请写一段代码,它不但能识别格式正确的信用卡卡号,还能验证它的有效性。

import re
pattern = "(\d{4}-\d{6}-\d{5}|\d{4}-\d{4}-\d{4}-\d{4})+"
string = "1234-567890-12345 1234-1234-1234-1234 11-22-33-44-55"
result = re.findall(pattern, string)
print result
程序输出:
>>> 
['1234-567890-12345', '1234-1234-1234-1234']

15–16. 修改脚本gendata.py 的代码,使数据直接写入文件redata.txt 中,而不是输出到屏幕上。

from random import randint, choice
from string import lowercase
from sys import maxint
from time import ctime

doms = ("com","edu", "net","org","gov")
fobj = open("redata.txt","a+")
for i in range(randint(5,10)):
    dtint = randint(0, maxint - 1)
    dtstr = ctime(dtint)

    shorter = randint(4, 7)
    em = ""
    for j in range(shorter):
        em += choice(lowercase)
    longer = randint(shorter, 12)
    dn = ""
    for j in range(longer):
        dn += choice(lowercase)
    fobj.write("%s::%s@%s.%s::%d-%d-%d\n" % (dtstr, em, dn, choice(doms), dtint, shorter, longer))
fobj.close()
15–17. 统计生成的redata.txt 文件中,星期中的每一天出现的次数(或统计各月份出现的次数)。
import re
pattern = "(Mon|Tue|Wed|Thu|Fri|Sat|Sun)+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num += re.findall(pattern, item)
print "Mon:%d" % num.count("Mon")
print "Tue:%d" % num.count("Tue")
print "Wed:%d" % num.count("Wed")
print "Thu:%d" % num.count("Thu")
print "Fri:%d" % num.count("Fri")
print "Sat:%d" % num.count("Sat")
print "Sun:%d" % num.count("Sun")
程序输出:
>>> 
Mon:0
Tue:1
Wed:2
Thu:0
Fri:2
Sat:1
Sun:0
15–18.通过检查每个输出行中整数字段部分的第一个整数是否和该行开头的时间戳相匹配来
验证redata.txt 中的数据是否完好。
根据各练习的要求写出相应的正则表达式:
15–19. 提取出每行中完整的时间戳字段。
import re
pattern = "[a-zA-Z]+ [a-zA-Z]+ \d+ \d+:\d+:\d+ \d+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num += re.findall(pattern, item)
print num

程序输出:

>>> 
['Wed Mar 20 01:26:54 1991', 'Wed Jul 07 11:32:31 2004', 'Sat May 14 00:52:06 2033', 'Tue Jun 03 13:36:27 2031', 'Fri Mar 16 01:06:12 2035', 'Fri Apr 01 13:51:31 1983']

15–20. 提取出每行中完整的电子邮件地址。

import re
pattern = "[a-zA-Z]+@[a-zA-Z]+\.[a-zA-Z]+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num += re.findall(pattern, item)
print num
程序输出:
>>> 
['czyt@liua.net', 'iqbgad@erbacdfsliv.gov', 'uevp@ipbumrmz.net', 'phavy@fhbwbj.net', 'wlop@uxvmc.gov', 'pywen@bkyfrsrius.edu']

15–21. 只提取出时间戳字段中的月份。
import re
pattern = "[a-zA-Z]+ [a-zA-Z]+ \d+ \d+:\d+:\d+ \d+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num += re.findall(pattern, item)
for item in num:
    print item.split(" ")[1],
程序输出:
>>> 
Mar Jul May Jun Mar Apr
我原本直接想对文件进行月份的读取,不过发现很麻烦,最好的方法确实是把时间戳读取出来然后进行筛选。


15-22. 只提取出时间戳字段中的年份。

import re
pattern = "[a-zA-Z]+ [a-zA-Z]+ \d+ \d+:\d+:\d+ \d+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num += re.findall(pattern, item)
for item in num:
    print item.split(" ")[4],
程序输出:
>>> 
1991 2004 2033 2031 2035 1983

15–23. 只提取出时间戳字段中的值(格式:HH:MM:SS)。
import re
pattern = "[a-zA-Z]+ [a-zA-Z]+ \d+ \d+:\d+:\d+ \d+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num += re.findall(pattern, item)
for item in num:
    print item.split(" ")[3],
程序输出:
>>> 
01:26:54 11:32:31 00:52:06 13:36:27 01:06:12 13:51:31

15-24. 只从电子邮件地址中提取出登录名和域名(包括主域名和顶级域名,二者连在一起)。

15-25. 只从电子邮件地址中提取出登录名和域名(包括主域名和顶级域名,二者分别提取)。

import re
pattern = "[a-zA-Z]+@[a-zA-Z]+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num += re.findall(pattern, item)
print num
for item in num:
    print item.split("@")[0],"---",item.split("@")[1]
程序输出:
>>> 
['czyt@liua', 'iqbgad@erbacdfsliv', 'uevp@ipbumrmz', 'phavy@fhbwbj', 'wlop@uxvmc', 'pywen@bkyfrsrius']
czyt --- liua
iqbgad --- erbacdfsliv
uevp --- ipbumrmz
phavy --- fhbwbj
wlop --- uxvmc
pywen --- bkyfrsrius

15–26. 将每行中的电子邮件地址替换为你自己的电子邮件地址。
import re
pattern = "[a-zA-Z]+@[a-zA-Z]+\.\w+"
num = []
with open("redata.txt") as fobj:
    for item in fobj:
        num.append(re.sub(re.findall(pattern, item)[0], "fzyz.abc@gmail.com", item))
with open("redata.txt","w") as fobj:
    for item in num:
        fobj.write(item)
with open("redata.txt") as fobj:
    for line in fobj:
        print line
程序输出:
>>> 
Wed Mar 20 01:26:54 1991::fzyz.abc@gmail.com::669403614-4-4

Wed Jul 07 11:32:31 2004::fzyz.abc@gmail.com::1089171151-6-11

Sat May 14 00:52:06 2033::fzyz.abc@gmail.com::1999615926-4-8

Tue Jun 03 13:36:27 2031::fzyz.abc@gmail.com::1938231387-5-6

Fri Mar 16 01:06:12 2035::fzyz.abc@gmail.com::2057591172-4-5

Fri Apr 01 13:51:31 1983::fzyz.abc@gmail.com::418024291-5-10

15–27. 提取出时间戳中的月、日、年,并按照格式“月 日,年”显示出来,且每行仅遍
历一次。
如果可以把额外的信息提取出来,比如时间,然后再从月-日-时间-年从进行提取的话,很简单。但是如果第一次re操作,就要求提取出月-日-年的话,我不知道怎么操作。。。。。。。。

15–28.区号(第一组的三个数字和它后面的连字符)是可选的,即,你写的正则表达式对800-555-1212 和555-1212 都可以匹配。
貌似有点难写出来,因为不知道为什么,总有个800-的答案出现:

import re
pattern = "((\d+-)?\d+-\d+)"
string = "800-555-1212 555-1212"
result = re.findall(pattern, string)
print result
程序输出:
>>> 
[('800-555-1212', '800-'), ('555-1212', '')]

到底正则表达式哪里写错了呢???

最后发现是:即使次数为0(及?代表0次或者1次),也依旧被compile一下,导致800-的产生。

import re
pattern = r"((?:\d+-)*\d+)"
string = "888-555-1212 555-1212"
result = re.findall(pattern, string)
print result
程序输出:

>>> 
['888-555-1212', '555-1212']


转载于:https://my.oschina.net/voler/blog/138873

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值