机器学习之正则表达式

机器学习之正则表达式

  • 机器学习之正则表达式
    • 1、原始字符串(r)
    • 2、特殊字符( [] * + ? {} | () . ^ $ \)
      • 2.1、集合字符(中括号 [])
      • 2.2 字符次数(* + ? {})
      • 2.3 并列字符(竖线|)
      • 2.4 提取字符(小括号())
      • 2.5 转义字符((点.),(托字符\^),(美元符$),(反斜杠\\))
      • 2.6 反斜杠\,特殊符号转义为本身
      • 2.7 反斜杠\,本身转义为特殊符号
    • 3、常用函数
      • 3.1 match(pat, str)
      • 3.2 search(pat, str)
      • 3.3 findall(pat, str)
      • 3.3 finditer(pat, str)
      • 3.4 split(pat, str)
      • 3.5 sub(pat, repl, str)
      • 3.6 compile(pat)
    • 4、 综合实例
      • 4.1 密码例子
      • 4.2 邮箱例子
      • 4.3 摘要例子

机器学习之正则表达式

正则表达式(Regular Expression, RE)就是一组定义某种搜索模式(pattern)的字符。

1、原始字符串(r)

  • 原始字符串(raw string)是所有的字符串都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符。

1)对于没有转义字符(反斜杠\)时,原始字符串和普通字符串是一样的

print('hello') #输出hello
print(r'hello') #输出hello

2)对于有转义字符(反斜杠\)时,原始字符串和普通字符串是不一样的

print('\bhello') #输出hello,\b匹配字符串首尾的空字符
print(r'\bhello') #输出\bhello

如果想保持字符串的原始模样,可以使用原始字符串r。

2、特殊字符( [] * + ? {} | () . ^ $ \)

  • python中包含一些自带的特殊字符,分类如下:
  • 表示集合:[]
  • 表示次数:* + ? {}
  • 表示并列:|
  • 用于提取:()
  • 用于转义:. ^ $ \

定义一个函数looking,当在string中没有找到pat时,返回“空”,反之打印所有符合模式的子字符串。

import re
def looking(pat, strs):
	return '空' if re.search(pat, strs) is None else re.findall(pat, strs)

2.1、集合字符(中括号 [])

中括号中包含了一些列字符的集合:

  • 明确字符:[abc] 匹配字符a,b,c
  • 范围字符:[a-z] 匹配字符a-z
  • 补集字符:[^6] 匹配除了6以外的字符

1)明确字符
匹配括号里任意一个字符

pat=r'[abc]'
print(looking(pat, 'a'))
print(looking(pat, 'ac'))
print(looking(pat, 'cba'))
print(looking(pat, 'seven'))
# 输出
['a']
['a', 'c']
['c', 'b', 'a']

2)范围字符
在[]中加入-即可设定范围,如:

  • [a-e]=[abcde]
  • [1-4]=[1234]
  • [a-ep]=[abcdep]
  • [0-38]=[01238]
print(looking(r'[a-ep]', 'person'))
print(looking(r'[0-38]', '666'))
#输出
['p', 'e']

3)补集字符
在[]中加入^即可表示出去后面的字符集,如:

  • [^abc] 除去a,b,c的字符
  • [^123] 除去1,2,3的字符
print( looking(r'[^abc]', 'baba') )
print( looking(r'[^abc]', 'steven') )
print( looking(r'[^123]', '456') )
print( looking(r'[^123]', '1+2=3') )
#输出['s', 't', 'e', 'v', 'e', 'n']
['4', '5', '6']
['+', '=']

2.2 字符次数(* + ? {})

上面的模式有缺陷,即只能匹配单个字符,在实际使用中需要带有次数的匹配字符。
1)贪婪模式

  • * 表示后面跟0个或多个字符
  • +表示后面跟1个或多个字符
  • ?表示后面跟0个或1个字符

2)非贪婪模式

  • *? 表示后面跟0个或多个字符,但只取第一个;
  • +? 表示后面跟1个或多个字符,但只取第一个;
  • ?? 表示后面跟0个或1个字符,但只取第一个;
'''星号*:匹配u字符0次或多次'''
pat=r'colou*r'
print(looking(pat, 'color'))
print(looking(pat, 'colour'))
print(looking(pat, 'colouuuur'))
# 输出
['color']
['colour']
['colouuuur']
'''加号+:匹配u字符1次或多次'''
pat=r'colou+r'
print(looking(pat, 'color'))
print(looking(pat, 'colour'))
print(looking(pat, 'colouuuur'))
# 输出['colour']
['colouuuur']
'''问号?:匹配u字符0次或1次'''
pat=r'colou?r'
print(looking(pat, 'color'))
print(looking(pat, 'colour'))
print(looking(pat, 'colouuuur'))
# 输出
['color']
['colour']

有的时候一个句子里会有重复的字符,假如是 > 字符,如果我们要匹配这个>,到底在哪一个 > 就停止了呢?

这个就是贪婪(greedy)模式非贪婪(non-greedy)模式的区别,让我们来看个例子。

heading=r'

TITLE

'

如果采用模式 <.+>,那么我们要获取的就是以 < 开头,以 > 结尾,中间有 1 个或多个字符的字符串。其中(.) 字符,它是一个通配符,可以代表任何除新行 (\n)之外的其他字符。

pat=r'<.+>'
print(looking(pat, heading))
#结果
['

TITLE

'
]

结果如上,获取的字符串确实以 < 开头,以 > 结尾,但是仔细看下,其实在 heading[3] 出也是 >,为什么没有匹配到它而是匹配到最后一个 > 呢?

原因就是上面用了贪婪模式,即在整个表达式匹配成功的前提下,尽可能多的匹配。那么其对立的非贪婪模式,就是在整个表达式匹配成功的前提下,尽可能少的匹配。
实现非贪婪模式只需在最后加一个 ? 字符,代码如下:

pat =  r'<.+?>'
print( looking(pat, heading) )
#输出
['

', '

'
]

3) 大括号{}:非常明确要匹配的字符出现几次,如

  • 中国的手机号位数是 13 位,n = 13;
  • 密码需要 8 位以上,n ≥ 8;
  • 公众号文章标题长度不能超过 64,n ≤ 64;
  • 用户名需要在 8 到 16 位之间,8 ≤ n ≤ 16;

我们可以设定具体的上界或(和)下界,使得代码更加有效也更好读懂,规则如下:

  • {n} 左边的字符串是否出现 n 次;
  • {n, } 左边的字符串是否出现大于等于 n 次;
  • {, n} 左边的字符串是否出现小于等于 n 次;
  • {n, m} 左边的字符串是否出现在 n 次和 m 次之间;

如下例子所示:

s = 'a11bbb2222ccccc'
print( looking(r'[a-z]{1}', s) )
print( looking(r'[0-9]{2,}', s) )
print( looking(r'[a-z]{,5}', s) ) #匹配五个以下的 a 到 z 小写字母,当然也包括零个,因此结果包含那些空字符。
print( looking(r'[0-9]{2,4}', s) )
#输出
['a', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'c']
['11', '2222']
['a', '', '', 'bbb', '', '', '', '', 'ccccc', '']
['11', '2222']

上面都是贪婪模式,当然也有其对应的非贪婪模式,但只有 {n, m}? 有意义。上面的模式对于前一个字符重复 m 到 n 次,并且取尽可能少的情况。比如在字符串’sssss’中,s{2,4} 会匹配 4 个 s,但 s{2,4}? 只匹配 2 个 s。

2.3 并列字符(竖线|)

字符集合问题解决了,字符次数问题解决了,如果现在面临的问题着是匹配 A 或 B 其中一个呢?用垂线 | 字符,A|B,如果 A 匹配了,则不再查找 B,反之亦然。

匹配句子中like或love一词的模式:

pat = r'like|love'
print( looking(pat, 'like you') )
print( looking(pat, 'love you') )
#输出
['like']
['love']

2.4 提取字符(小括号())

首先定义“beat 的第三人称,过去式,过去分词和现在进行式”的模式,获取 beat 加正确后缀的所有单词。

pat = r'beat(s|ed|en|ing)'

print( looking(pat, 'beats') )
print( looking(pat, 'beated') )
print( looking(pat, 'beaten') )
print( looking(pat, 'beating') )
#输出
['s']
['ed']
['en']
['ing']

我们将出现在 () 里面的后缀都获取出来了。

但其实这不是我们想要的,我们想把带着后缀的 beat 给获取出来。那么只有在最外面再加一层 (),模式如下。

pat = r'(beat(s|ed|en|ing))'

print( looking(pat, 'beats') )
print( looking(pat, 'beated') )
print( looking(pat, 'beaten') )
print( looking(pat, 'beating') )
#输出
[('beats', 's')]
[('beated', 'ed')]
[('beaten', 'en')]
[('beating', 'ing')]

其可视图如下,我们发现 Group 2 嵌套在 Group 1 里面。
机器学习之正则表达式_第1张图片
现在带着后缀的 beat 已经获取出来了,在列表中每个元组的第一个元素,但如果不想要后缀(即元组的第二个元素),可以用下面的模式。

在 () 中最前面加入 ?:。(?:) 代表只匹配不获取(non-capturing),结果看上去非常自然。


pat = r'(beat(?:s|ed|en|ing))'
print( looking(pat, 'beats') )
print( looking(pat, 'beated') )
print( looking(pat, 'beaten') )
print( looking(pat, 'beating') )
#输出
['beats']
['beated']
['beaten']
['beating']

2.5 转义字符((点.),(托字符^),(美元符$),(反斜杠\))

  • 转义字符:能够转换自身含义的字符,如(点.),(托字符^),(美元符$),(反斜杠\)
  • 点.:通配符,可以匹配除新行(newline)外的其他任意字符。
# 定义含有1个或多个非新行字符
pat=r'.+'
print(looking(pat, 'a'))
print(looking(pat, 'b1'))
print(looking(pat, 'a@9'))
print(looking(pat, '$ 9_fZ'))
print(looking(pat, '9z_\t\r\n'))
#输出
['a']
['b1']
['a@9']
['$ 9_fZ']
['9z_\t\r']
#除了换行符\n没匹配到,其他字符都匹配出来了
  • 托字符^:表示字符串开头
# 定义以s 开头字符串”的模
pat=r'^s[\w]*' #\w匹配任意数字、大小写字母和下划线,等价于[a-zA-Z0-9_]
print(looking(pat, 'son'))
print(looking(pat, 'shot'))
print(looking(pat, 'come'))
#结果
['son']
['shot']
  • 美元符$:表示字符串结尾
#定义以 s 结尾字符串”的模式
pat=r'[\w]*s$'
print( looking(pat, 'yes') )
print( looking(pat, 'mess') )
print( looking(pat, 'come') )
#结果
['yes']
['mess']
  • 反斜杠\:可对特殊字符进行转义,也可以对普通字符进行转义
    1)将特殊字符转成自身含义:用 \ 作用在 ^ . \ 等身上,代表乘方\ ^、小数点\ . 和除号\ \;
    2)将自身字符转成特殊含义:用 \ 作用在 w d n 等身上,代表数字字母下划线 \w、数字 \d 和新行 \n。

2.6 反斜杠\,特殊符号转义为本身

  • 反斜杠下$符号代表美元
#反斜杠下$符号代表美元
pat=r'\$[0-9.]+'
print(looking(pat, 'it costs $99.99'))
#结果
['$99.99']
  • 反斜杠的限制下, ^ . \ 终于代表乘方、小数点和除号
pat = r'(\\|\/|\^|\.)'
print( looking(pat, '(5/2)^2=6.25') )
#结果
['/', '^', '.']

# 没有了反斜杠的限制,一切乱了套
#点 . 就是通配符,可以匹配字符串里所有字符。
pat = r'(\|/|^|.)'
print( looking(pat, '(5/2)^2=6.25') )
# 结果
['', '(', '5', '/', '2', ')', '^', '2', '=', '6', '.', '2', '5']
  • 如果在中括号 [] 集合里,每个字符就是它本身的意义,点就是点,而不是通配符
pat=r'[/^\.]'
print(looking(pat, '(5/2)^2=6.25'))
#结果
['/', '^', '.']

2.7 反斜杠\,本身转义为特殊符号

符号 含义
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
\B 匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
\d 匹配任何“数字”字符,等价于 [0-9]
\D 匹配任何“非数字”字符,等价于 [^0-9]
\s 匹配任何“空白”字符,等价于 [\t\n\r]
\S 匹配任何“非空白”字符,等价于 [^\t\n\r]
\w 匹配任何“字母数字下划线”字符,等价于 [a-zA-Z0-9_]
\W 匹配任何“非字母数字下划线”字符,等价于 [^a-zA-Z0-9_]
\A 匹配句子的“开头”字符,等价于 ^
\Z 匹配句子的“结尾”字符,等价于 $
\t 匹配句子的“制表键 (tab)”字符
\r 匹配句子的“回车键 (return)”字符
\n 匹配句子的“换行键 (newline)”字符
  • \b \B:匹配一个单词边界,也就是指单词和空格间的位置、匹配非单词边界
pat = r'\blearn\b'
print( looking(pat, 'learn Python') )
print( looking(pat, 'relearn Python') )
print( looking(pat, 'learning Python') )
print( looking(pat, 'relearning Python') )
#输出
['learn']
空
空
空

\b 只能匹配 learn单词的边界,那么只能匹配不带前缀和后缀的 learn, 即 learn 本身。

pat = r'\Blearn\B'
print( looking(pat, 'learn Python') )
print( looking(pat, 'relearn Python') )
print( looking(pat, 'learning Python') )
print( looking(pat, 'relearning Python') )
#输出
空
空
空
['learn']

\B 匹配 learn单词的非边界,那么只能匹配带前缀和后缀的 learn,即 relearning。

pat = r'\blearn\B'
print( looking(pat, 'learn Python') )
print( looking(pat, 'relearn Python') )
print( looking(pat, 'learning Python') )
print( looking(pat, 'relearning Python') )
#输出
空
空
['learn']

learn 前 \b 后 \B,只能匹配带后缀的 learn,即 learning。

pat = r'\Blearn\b'
print( looking(pat, 'learn Python') )
print( looking(pat, 'relearn Python') )
print( looking(pat, 'learning Python') )
print( looking(pat, 'relearning Python') )
#输出['learn']
空
空

learn 前 \B 后 \b,只能匹配带前缀的 learn,即 relearn。

  • \d \D:匹配数字字符、匹配非数字字符
pat = r'\d+'
print( looking(pat, '12+ab34-cd56*ef78/gh90%ij'))
#输出
['12', '34', '56', '78', '90']
pat = r'\D+'
print( look_for(pat, '12+ab34-cd56*ef78/gh90%ij') )
#输出
['+ab', '-cd', '*ef', '/gh', '%ij']
  • \s \S:匹配空白字符、匹配非空白字符
pat = r'\s+'
s = '''please  don't
leave  me
    alone'''
print( looking(pat, s) )
#输出
[' ', '\n', ' ', '\n ']
#匹配各种空格比如制表、回车或新行。
pat = r'\S+'
print( looking(pat, s) )
#输出
['please', "don't", 'leave', 'me', 'alone']
#匹配各种非空格
  • \w \W:匹配任何“字母数字下划线”字符、匹配非数字字母下划线
pat=r'\w+'
print(looking(pat, '12+ab_34-cd56_ef78'))
#结果
['12', 'ab_34', 'cd56_ef78']
pat=r'\W+'
print(looking(pat, '12+ab_34-cd56_ef78'))
#结果
['+', '-']
  • \A \Z:匹配开头、匹配结尾
pat1 = r'^y[\w]*'
pat2 = r'\Ay[\w]*'
str1 = 'you rock'
str2 = 'rock you'

print( look_for(pat1, str1) )
print( look_for(pat2, str1) )

print( look_for(pat1, str2) )
print( look_for(pat2, str2) )
#结果
['you']
['you']
空
空

匹配开头字符,\A 和 ^ 等价。


pat1 = r'[\w]*k$'
pat2 = r'[\w]*k\Z'
str1 = 'you rock'
str2 = 'rock you'

print( look_for(pat1, str1) )
print( look_for(pat2, str1) )

print( look_for(pat1, str2) )
print( look_for(pat2, str2) )
#结果
['rock']
['rock']
空
空

匹配结尾字符,\Z 和 $ 等价。

3、常用函数

函数 含义
match(pat, str) 检查字符串str的开头是否符合pat模式
search(pat, str) 检查字符串中是否符合某个模式
findall(pat, str) 返回所有符合某个模式的字符串,以列表形式输出
finditer(pat, str) 返回所有符合某个模式的字符串,以迭代器形式输出
split(pat, str) 以某个模式为分割点,拆分整个句子为一系列字符串,以列表形式输出
sub(pat, repl, str) 句子 str 中找到匹配正则表达式模式的所有子字符串,用另一个字符串 repl 进行替换
compile(pat) 将某个模式编译成对象,供之后使用

3.1 match(pat, str)

判断模式是否在字符串开头位置匹配。如果匹配,返回对象,如果不匹配,返回 None。

s = 'Kobe Bryant'
print( re.match(r'Kobe', s) )
print( re.match(r'Kobe', s).group() )
print( re.match(r'Bryant', s) )
#结果
<re.Match object; span=(0, 4), match='Kobe'>
Kobe
None

该函数返回的是个对象(包括匹配的子字符串和在句中的位置索引),如果只需要子字符串,需要用 group() 函数。

由于值匹配句头,那么句中的 Bryant 无法被匹配到。

3.2 search(pat, str)

在字符串中查找匹配正则表达式模式的位置。如果匹配,返回对象,如果不匹配,返回 None。

s = 'Kobe Bryant'
print( re.search(r'Kobe', s) )
print( re.search(r'Kobe', s).group() )
print( re.search(r'Bryant', s) )
print( re.search(r'Bryant', s).group() )
#结果
<re.Match object; span=(0, 4), match='Kobe'>
Kobe
<re.Match object; span=(5, 11), match='Bryant'>
Bryant

该函数返回的是个对象(包括匹配的子字符串和在句中的位置索引),如果只需要子字符串,需要用 group() 函数。
如果句子出现两个 Bryant 呢?

s = 'Kobe Bryant loves Gianna Bryant'
print( re.search(r'Bryant', s) )
print( re.search(r'Bryant', s).group() )
print( re.search(r'Bryant', s) )
#结果
<re.Match object; span=(5, 11), match='Bryant'>
Bryant
<re.Match object; span=(5, 11), match='Bryant'>

根据结果只匹配出第一个,我们需要下面的函数来匹配全部。

3.3 findall(pat, str)

在字符串中找到正则表达式所匹配的所有子串,并组成一个列表返回。

s = 'Kobe Bryant loves Gianna Bryant'
print( re.findall(r'Kobe', s) )
print( re.findall(r'Bryant', s) )
print( re.findall(r'Gigi', s) )
#结果
['Kobe']
['Bryant', 'Bryant']
[]

3.3 finditer(pat, str)

和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并组成一个迭代器返回。

s = 'Kobe Bryant loves Gianna Bryant'
print( [i.group() for i in re.finditer(r'Kobe', s)] )
print( [i for i in re.finditer(r'Bryant', s)] )
print( [i for i in re.finditer(r'Gigi', s)] )
#结果
['Kobe']
[<re.Match object; span=(5, 11), match='Bryant'>,
 <re.Match object; span=(25, 31), match='Bryant'>]
[]

如果需要匹配子串在原句中的位置索引,用 finditer,此外用 findall。

3.4 split(pat, str)

将字符串匹配正则表达式的部拆分开并返回一个列表。

s = 'Kobe Bryant loves Gianna Bryant'
print( re.split(r'\s', s) ) #按空格拆分
#结果
['Kobe', 'Bryant', 'loves', 'Gianna', 'Bryant']

3.5 sub(pat, repl, str)

句子 str 中找到匹配正则表达式模式的所有子字符串,用另一个字符串 repl 进行替换。如果没有找到匹配模式的串,则返回未被修改的句子 str,其中 repl 既可以是字符串也可以是一个函数。

s = 'Kobe Bryant loves Gianna Bryant'
print( re.sub(r'\s', '-', s) ) #用-替代空格
#结果
Kobe-Bryant-loves-Gianna-Bryant
print( re.sub(r'Gianna', 'Gigi', s) )
#结果
Kobe Bryant loves Gigi Bryant
#用 Gigi 代替 Gianna。
print( re.sub(r'\d+', '_', s) )
#结果
Kobe Bryant loves Gianna Bryant
#用 _ 代替数字(一个或多个),但句中没有数字,因此没用替代动作。
print( re.sub(r'\d*', '_', s)
#结果
_K_o_b_e_ _B_r_y_a_n_t_ _l_o_v_e_s_ _G_i_a_n_n_a_ _B_r_y_a_n_t_
#用 _ 代替数字(零个或多个),虽然句中没有数字,但是零个数字就是空字符,因此 _ 替代所有空字符。

3.6 compile(pat)

把正则表达式的模式转化成正则表达式对象,供其他函数如match 和 search 使用。对象创建出来可以循环使用,如果某种模式要重复使用话,用“先 compile 再 findall”的方式更加高效。
用处理电邮地址来举例。

email = '''Shengyuan Personal: [email protected]
Shengyuan Work: [email protected]
Shengyuan School: [email protected]
Obama: [email protected]'''
print(email)

创建电邮的模式 r’[\w.-]+@[\w.-]+’,用 compile 先创建 RE 对象,供之后使用。

pat = r'[\w.-]+@[\w.-]+'
obj = re.compile(pat)
obj
#结果
re.compile(r'[\w.-]+@[\w.-]+', re.UNICODE)

在对象 obj 上分别使用 match, search, findall, findieter 等方法,结果如下:

print( obj.match(email), '\n') #匹配以obj对象开头
print( obj.search(email), '\n' ) #找到第一个obj
print( obj.findall(email), '\n' ) #找到所有的obj
print( [i for i in obj.finditer(email)]) #找到所有的obj迭代对象
#结果
None

<re.Match object; span=(20, 41), match='[email protected]'> 

['[email protected]',
 '[email protected]',
 '[email protected]',
 '[email protected]']

[<re.Match object; span=(20, 41), match='[email protected]'>,
<re.Match object; span=(58, 88), match='[email protected]'>,
<re.Match object; span=(107, 126), match='[email protected]'>,
<re.Match object; span=(134, 161), match='[email protected]'>]

在对象 obj 上还可使用 sub 方法,结果如下:

print( obj.sub('[email protected]', email), '\n' )
#结果
Shengyuan Personal: ---@---.---
Shengyuan Work: ---@---.---
Shengyuan School: ---@---.---
Obama: ---@---.---

在对象 obj 上还可使用 split 方法,即把 @ 前后的子串拆分出来,结果如下:

for addr in obj.findall(email):
    print( re.split(r'@', addr))
#结果
['quantsteven', 'gmail.com']
['shengyuan', 'octagon-advisors.com']
['g0700508', 'nus.edu.sg']
['barack.obama', 'whitehouse.gov']

我们还可以再创建个 RE 对象 obj1,专门用来做拆分。

obj1 = re.compile(r'@')
for addr in obj.findall(email):
    print( obj1.split(addr))
#结果
['quantsteven', 'gmail.com']
['shengyuan', 'octagon-advisors.com']
['g0700508', 'nus.edu.sg']
['barack.obama', 'whitehouse.gov']

4、 综合实例

4.1 密码例子

密码通常有如下要求:

  • 最少 8 个最多 16 个字符.
  • 至少含有一个大写字母,一个小写字母,一个数字
  • 至少含有一个特殊字符@ ! $ # % _ - ,但不包括空格
pat=r'^[a-z0-9A-Z@!$#%_-]{8,16}$' #必须加^和$
print( look_for(pat, 'stevenliu') )
print( look_for(pat, '19831031') )
print( look_for(pat, 'steven1031') )
print( look_for(pat, 'steven@1031') )
print( look_for(pat, 'Steven@1031') )
print( look_for(pat, 's1031') ) #长度小于8
print( look_for(pat, 's@1031') ) #长度小于8
print( look_for(pat, 'stevenliu19831031') )#长度大于16,如果不加^和$的话,会匹配出stevenliu19831031和stevenliu@19831031,但这2个明显长度大于16了。
print( look_for(pat, 'stevenliu@19831031') )#长度大于16
#结果
['stevenliu']
['19831031']
['steven1031']
['steven@1031']
['Steven@1031']
空
空
空
空

结果好像不太对,因为密码必须要含有数字,大小写和特殊字符。
这时候需要用 (?=…) 这个操作了,意思就是匹配 ’…’ 之前的字符串。在本例中 ‘…’ 包括小写 [a-z],大写 [A-Z],数字 \d,特殊字符 [@$!%*?&_],言下之义就是上面这些必须包含中密码中。
说明

  • 环视:只进行子表达式的匹配,不占有字符,匹配到的内容不保存到最终的匹配结果,是零宽度的。环视匹配的最终结果就是一个位置。
    环视的作用相当于对所在位置加了一个附加条件,只有满足这个条件,环视子表达式才能匹配成功。
    环视按照方向划分有顺序逆序两种,按照是否匹配有肯定否定两种,组合起来就有四种环视。顺序环视相当于在当前位置右侧附加一个条件,而逆序环视相当于在当前位置左侧附加一个条件。
  • (?<=Expression):逆序肯定环视,表示所在位置左侧能够匹配Expression
    (?:逆序否定环视,表示所在位置左侧不能匹配Expression
    (?=Expression):顺序肯定环视,表示所在位置右侧能够匹配Expression
    (?!Expression):顺序否定环视,表示所在位置右侧不能匹配Expression

参考:https://www.cnblogs.com/kernel0815/p/3375249.html

pat = r'^(?=.*[a-z])
         (?=.*[A-Z])
         (?=.*\d)
         (?=.*[$@$!%*?&_])
         [A-Za-z\d$@$!%*?&_]{
     8,16}$'

print( look_for(pat, 'stevenwang') )
print( look_for(pat, '19831031') )
print( look_for(pat, 'steven1031') )
print( look_for(pat, 'steven@1031') )
print( look_for(pat, 'Steven@1031') )
print( look_for(pat, 's1031') )
print( look_for(pat, 's@1031') )
print( look_for(pat, 'stevenwang19831031') )
print( look_for(pat, 'stevenwang@19831031') )
空
空
空
空
['Steven@1031']
空
空
空
空

结果完全正确。

4.2 邮箱例子

首先定义邮箱地址的模式 ‘\S+@\S+’,还记得 \S 是非空格字符,基本代表了所需的字符要求。我们想从从 email.txt 文本中筛选出所有邮箱信息。

pat = r'\S+@\S+'
obj = re.compile(pat)
email_list = []
hand = open('email.txt')
for line in hand:
    line = line.rstrip()
    email_addr = obj.findall(line)
    if len(email_addr) > 0:
        email_list.append(email_addr[0])
list(set(email_list))

机器学习之正则表达式_第2张图片
咋一看结果是对的,但细看(高亮处)有些邮箱地址包含了 <> 的符号,或者根本不是正常的邮箱地址,比如 apache@localhost。
这时候我们需要在模式中添加更多规则,如下:

  • '[a-zA-Z\d]\S+ 代表第一字符要是数字或字母
  • \w+\.[a-z]{2,3} 代表 A.B 这样的结构,其中 A 由若干字母数字下划线组成,而 B 由 2 或 3 个小写字母组成(因为通常邮箱最后就是 com, net, gov, edu 等等)。
pat = r'[a-zA-Z\d]\S+@\w+\.[a-z]{2,3}'

机器学习之正则表达式_第3张图片
结果完全正确。

4.3 摘要例子

在下面摘要中获取人物、买卖动作、股票数量、股票代号、日期和股价这些关键信息。

news = 
"""
Jack Black sold 15,000 shares in AMZN on 2019-03-06 at a price of $1044.00.
David V.Love bought 811 shares in TLSA on 2020-01-19 at a price of $868.75.
Steven exercised 262 shares in AAPL on 2020-02-04 at a price of $301.00.
"""
pat = r'([a-zA-Z]*)' \
        '\s(sold|bought|exercised)' \
        '\s*([\d,]+)' \
        '.*in\s([A-Z]{,5})' \
        '.*(\d{4}-\d{2}-\d{2})' \
        '.*price of\s(\$\d*.\d*)'

re.findall( pat, news )
#结果
[('Jack Black', 'sold', '15,000', 'AMZN', '2019-03-06', '$1044.00'),
 ('David V.Love', 'bought', '811', 'TLSA', '2020-01-19', '$868.75'),
 ('Steven', 'exercised', '262', 'AAPL', '2020-02-04', '$301.00')]

你可能感兴趣的:(机器学习之正则表达式,机器学习,nlp,正则表达式,python)