赞
踩
概念复习
结构简单的语言文本,即是某种(或某些)自然语言中基本的文字符号构成的序列。
基本文字符号
有穷的一组字符构成的集合
字符集里的字符定义的一种顺序
可看做特殊的线性表
ASCI, Unicode,
python字符串实现是一种不变数据类型(另一种实现是可变数据类型)
字符串里字符的长度
长度为0的串
从0开始算起
从左向右查看两个串中下标相同的各对字符,遇到的第一对不同字符的字
符序决定了这两个字符串的顺序;另外,如果两个串中相同下标的各对字符都相同,但
其中一个串较短,那么就认为它较小,排在前面。举例说,考虑英文字母的集合,字
符序采用字母表中的顺序abcd…。这样就有字符串abc小于add,也小于abcd,因为虽
然abc和abd的前3个字符相同,但后者较长。
两个串拼接得到另一个串,python是用+,s1+s2
s2包括了s1,s1就是子串
第一个字符的位置
如果存在字符串 s ′ s^{'} s′, s 2 = s 1 + s ′ s_2 = s_1 + s ^{'} s2=s1+s′,则 s 1 s_1 s1为 s 2 s_2 s2的前缀,如果 s 2 = s ′ + s 1 s_2 = s^{'}+s_1 s2=s′+s1,则 s 1 s_1 s1是 s 2 s_2 s2的后缀。空串即是前缀,也是后缀。
串s的n次幂是连续的n个s拼接而成的串
必须规定替换的顺序,从左到右还是从右到左。
str是一个不变类型,创建后内容和长度都不变化,不同str对象的长度不同。
str对象操作分为两类:
考虑字符串的表示时,要确定两个方面:
def naive_matching(t, p): ''' t:source p:model ''' m, n = len(p), len(t) i, j = 0, 0 while i < m and j < n: if p[i] == t[j]: i, j = i + 1, j + 1 else: i, j= 0, j - i + 1 if i == m: return j - i return -1
分析:
时间复杂度
O
(
m
×
n
)
O(m \times n)
O(m×n)
每次把字符比较看做完全独立的操作,没有利用字符串的特点。
图为朴素匹配与KMP匹配过程
对于每个匹配字符串,先计算每个字符对应的移动位移,可记为pnext表,就是对于每个匹配字符串里的每个字符,下次匹配的字符。
def matching_KMP(t, p, pnext):
'''
t: source str
p: model str
'''
j, i = 0, 0
n, m = len(t), len(p)
while j < n and i < m:
if i == -1 or t[j] == p[i]:
j, i = j+1, i + 1
else:
i = pnext[i]
if i == m:
return j - i
return -1
对模式串p中每个i,都有与之对应的下标
k
i
k_i
ki,与被匹配的目标串无关。
表元素pnext[i]记录与
i
i
i对应的
k
i
k_i
ki的值。
表pnext的分析构造如图:
找到一个位置k,下次匹配用
p
k
p_k
pk与前面匹配失败的
t
j
t_j
tj比较,也就是模式串一道
p
k
p_k
pk与
t
j
t_j
tj对准的位置。确定k的问题就变成了确定
p
0
,
.
.
.
p
i
−
1
p_0,...p_{i-1}
p0,...pi−1的相等前缀和后缀的长度。
k
k
k值越小表示移动得越远(也就是匹配得越少)。
如果
p
0
.
.
.
p
i
−
1
p_0...p_{i-1}
p0...pi−1的最长相等前后缀的长度为,
k
,
(
0
≤
k
<
i
−
1
)
k,(0 \leq k < i - 1)
k,(0≤k<i−1),
p
i
≠
t
j
p_i \neq t_j
pi=tj时,模式串就应该右移
i
−
k
i-k
i−k位。也就是pnext[i]设置为k。
因此对模式串的每个位置i,求出模式串的子串
p
0
.
.
.
p
i
−
1
p_0...p_{i-1}
p0...pi−1的最长相等前后缀的长度。
假设pnext[i-1]的计算结果为k-1,比较 p i p_i pi与 p k p_k pk,有两张情况:
一直pnext[0] = -1和直至pnext[i-1]的已有值求pnext[i]的算法:
def gen_pnext(p):
'''
generate check table for each char in p
use for KMP
'''
i, k, m = 0, -1, len(p)
pnext = [-1] * m
while i < m - 1:
if k == -1 or p[i] == p[k]:
i, k = i + 1, k + 1
pnext[i] = k
else:
k = pnext[k]
return pnext
这个算法的时间复杂性是 O ( m ) O(m) O(m),m是模式串的长度。
pnext的分析构造图可以看出,匹配失败时有 p i ≠ t j p_i \ne t_j pi=tj, 设 p n e x t [ i ] = k pnext[i] = k pnext[i]=k,如果发现 p i = p k p_i = p_k pi=pk, 那么一定也有 p k ≠ t j p_k \ne t_j pk=tj.这种情况下,模式串应该右移到pnext[k](而不是右移到pnext[i]),下一步应该用 p n e x t [ k ] p_{next[k]} pnext[k]与 t j t_j tj比较。这一修改可以把模式串右移更远。
def gen_pnext(p):
i, k, m = 0, -1, len(p)
pnext = [-1] * m
while i < m - 1:
if k == -1 or p[i] == p[k]:
i, k = i + 1, k + 1
if p[i] == p[k]:
pnext[i] = pnext[k]
else:
pnext[i] = k
else:
k = pnext[k]
return pnext
KMP算法包括:构造pnext表和匹配。如果模式串和目标串的长度分别为 m m m和 n n n,KMP算法时间的复杂性是 O ( m + n ) O(m+n) O(m+n),通常情况下 m < < n m << n m<<n,因此可认为算法的复杂性为 O ( n ) O(n) O(n),显然优于朴素匹配算法的 O ( m × n ) O(m \times n) O(m×n)
一个模式描述了(能匹配)一些字符串(一个字符串集合)
对于一般的模式,都需要注意三个问题:模式的形式是什么,描述的字符串集合如何确定,怎样做匹配
描述能力越强,需要的匹配算法负复杂性越高
*所有字符串
?一个字符串
理论的正则表达式如下定义,其中 α \alpha α和 β \beta β都是正则表达式:
举例:
abc,只匹配abc
a(b*)(c*)与所有保号一个a之后任意多个b,再之后任意多个c的串匹配
a((b|c)*)与所有在一个a后出现任意多个b和c的串匹配
模式语言,介绍一种简化的正则表达式:
其中“^“和”$"符号至多在模式的开始和结束出现一次
举例:
p1 = “ab.”
p2 = “^abc.$"
p3 = "aabc.bca”
匹配算法:
def match(re, text): def match_here(re, i, text, j): """ text begin from index j re begin from index i """ while True: if i == rlen: return True if re[i] == '$': return i + 1 == rlen and j == tlen if i + 1 < rlen and re[i + 1] == '*': return match_star(re[i], re, i+2, text, j) if j == tlen or (re[i] != '.' and re[i] != text[j]): return False i, j = i + 1, j + 1 def match_star(c, re, i, text, j): """ Skip 0 or more c in text """ for n in range(j, tlen): if match_here(re, i, text, n): return True if text[n] != c and c != '.': break return False rlen, tlen = len(re), len(text) if re[0] == '^': if match_here(re, 1, text, 0): return 1 for n in range(tlen): if match_here(re, 0, text, n): return n return -1
python正则表达式功能由标准包re提供。
一个具有正则表达式形式的字符串代表一个字符串模式,描述了一个特定的字符串几何。
re包的正则表达式实际上构成了一个特殊的小语言:
普通字符串魏子良:str类型的字符串对象
原始字符串的形式,加r或R:
R"abcdefg"
r"C:\courses\python\progs"
""不代表转义字符
re里面有14个:
. ^ $ * + ? \ | { } [ ] ( )
说明:pattern表示模式串(正则表达式的字符串),string表示被处理的字符串,repi表示替换串,即操作中使用的另一个字符串。
import re
# re.compile(pattern,flag = 0)
r1 = re.compile('abc')
生成与‘abc’对应的正则表达式对象,赋值给r1
# re.search(patern, string,flag=0)
re.search('abc','aeabced',1)
<_sre.SRE_Match object; span=(2, 5), match='abc'>
# re.match(pattern,string,flag=0)
>>> r1 = re.match('abc','aeabced',1)
>>> r1
>>> r1 = re.match('abc','abc',1)
>>> r1
<_sre.SRE_Match object; span=(0, 3), match='abc'>
# re.split(pattern,string,maxsplit=0,flags=0)
re.split(' ','abc abc are not the same')
['abc', 'abc', 'are', 'not', 'the', 'same']
re.findall(pattern,string,flags=0)
注意两点:
如果需要在字符组里包括字符KaTeX parse error: Expected group after '^' at position 1: ^̲,就不能把它放在第一个位置,也可以放在后面,或者写"^",如果字符组需要包括”-“,"]",必须写"-","]"
a…b匹配所有a开头b结尾的四字字符
re采用转义串的形式定义了一些常用字符组,包括:
- \d: 十进制数字匹配
- \D:非十进制数字的所有字符匹配 [ 0 − 9 ] [^ 0-9] [0−9]
- \s: 所有空白字符匹配,等价于KaTeX parse error: Undefined control sequence: \t at position 3: [ \̲t̲ ̲\v \n \f \r]
- \S:所有非空白字符匹配
- \w:所有字母数字字符匹配
- \W:所有非字母数字字符匹配
re.split('a*','abbaaabbdbbabbababbabb')
['', 'bb', 'bbdbb', 'bb', 'b', 'bb', 'bb']
re.match(‘ab*’,‘abbbbbbbc’)可以与字符串里的a匹配,也可以与ab匹配。一般正则表达式匹配都提供了两种可能:
- 贪婪匹配:与最长的子串匹配,abbbbbbb.re规定“*”做贪婪匹配
- 非贪婪匹配(吝啬匹配):匹配最短子串
与“*”略不同的是“+”,表示作用模式的1次货多次重复,至少要一次
描述正整数的一个简单模式是‘\d+’,等价于,“\d\d *”
‘?’
比如’a?‘可以与空串货与a匹配的字符串匹配。要求与a匹配的字符串的0或1次重复匹配
描述整数的一种简单模式‘-?\d+’
确定次数的重复用{n}描述,a{n}与a匹配的串的n次重复匹配
背景固话号码的模式串可以写’(010-)?[2-9][0-9]{7}‘,010-开始,随后是数字2-9,再后是7个十进制数字。(010-)?表示整个字段可选
a{m,n}与a匹配的串的m到n此重复匹配,其中包括m次和n次
a{3,7}与3到7个a构成的串匹配
go{2,5}gle与google,gooogle,goooogle,gooooogle匹配。
a{n}等价于a{n,n}
a?等价于a{1, infinity}
*,+,?,{m,n}都才去贪婪匹配规则
非贪婪匹配描述符,加一个?
*? ?? +? {m,n}?
描述两种或多种情况之一的匹配。
a|b|c可以匹配a或者b或者c,[abc]是其简写。
’0|[1-9]\d*'可以匹配python语言里的十进制整数。(python负号看做运算符,非0整数不能以0开头)。这个模式会与0123的0匹配,与abc123,a123b的123匹配。他们在程序里不看做整数,都需要排除。
匹配国内固定电话号码模式:‘0\d{2} -\d{8}|0\d{3}-d{7,8}
\b 在实际单词边界位置匹配空串(不匹配实际字符)。胆子是字母数字的任意连续序列,边界就是非字母数字的字符或者无字符(串的开头/结束)
python里面\b是退格符,re里面是单词边界,解决方法有两个
找出字符串里面所有整数,计算他们的和
def sumInt(s):
re_int = r'\b(0|[1-9]\d*)\b'
if s == None:
return 0
int_list = map(int, re.findall(re_int, s))
print(int_list)
s = 0
for n in int_list:
s+= n
return s
sumInt('adfas 1 2 dafsdf 4 dfa 56')
<map object at 0x000001A3DFA320B8>
转义元字符\B是\b的补,匹配空串,但是要求在相应位置是字母或者数字
re.findall('py.\B', 'python, py 2, py342py1py2py4')
Out[41]: ['pyt', 'py3', 'py1', 'py2']
匹配操作的结果可用于逻辑判断
取得被匹配的子串:mat.group()。通过匹配成功得到了mat,所用的正则表达式自然与目标串的一个子串成功匹配
mat = re.search('.((.)e)f','abcdef')
mat
<_sre.SRE_Match object; span=(2, 6), match='cdef'>
mat.group(0)
Out[46]: 'cdef'
mat.group(1)
Out[47]: 'de'
mat.group(2)
Out[48]: 'd'
#mat.group(3) no such group
r’(.{2}) \1’匹配前两个字符后,是空号,然后下两个要跟前面两个一样。普通字符串里\1表示二进制编码为1de zifu 0*01,故也可以写成’(.{2}) \1’
用regex代表一个正则表达式对象,方括号括起的部分表示可选
待续。。
Python原始字符串,元字符,常规字符,顺序组合(拼接),字符组,重复模式,选择模式,
组概念。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。