Regular Expressions
- 当我们想从下面这段字符串中获取年份这个信息的时候,我们并不知道怎么分离这个字符串,什么样的表达形式代表了年份,这个时候就需要用到正则表达式。
'''
- `"Jan 17, 2012"`
- `"9/22/2005"`
- `"Spring 2007"`
- `"New Year's Eve 1999"`
'''
一个正则表达式就是一个字符序列,描述了一个搜索模式。通常情况下我们说一个字符串中存在这个模式,就认为这个字符串匹配了这个正则表达式。最简单的正则表达式就是一个普通的字符序列。
“.”——占位符
- python中有一个模块re可以使用正则表达式,其中有一些特殊字符,比如”.”表示这个位置可以填充任何字符:
“^”——开始符 “$”——终止符
- 特殊字符”^”表示这个是起始位置,”^”后面的就是初始字符,同样的”$”前面的是终止字符:
“^a” will match all strings that start with “a”.
“a$” will match all strings that end with “a”.
Reading And Printing The Dataset
import csv
f = open("askreddit_2015.csv", 'r')
csvreader = csv.reader(f)
posts_with_header = list(csvreader)
'''
posts_with_header
list (<class 'list'>)
[['Title', 'Score', 'Time', 'Gold', 'NumComs'],
['What\'s your internet "white whale", something you\'ve been searching for years to find with no luck?',
'11510',
'1433213314.0',
'1',
'26195'],
["What's your favorite video that is 10 seconds or less?",
...
'''
posts = posts_with_header[1:] #去掉第一行标签
for post in posts[:10]: # 打印前10行
print(post)
search——返回匹配str
前面提到re模块可以使用正则表达式,其中一个很有用的函数是search。re.search(regex, string)表达的是string是否匹配regex,如果匹配返回匹配的对象,否则返回None。
- 查看前面的字符串列表中是否有与”of Reddit”匹配的字符串:
import re
of_reddit_count = 0
for row in posts:
if re.search("of Reddit", row[0]) != None:
of_reddit_count += 1
'''
of_reddit_count : 76 # 有76个Title中有"of Reddit"字符串
'''
[abcde]——方括号任选其一
- 由于”of Reddit”与”of reddit”是不一样的正则表达式,但实际意义是一样的,为了避免产生这样的问题,我们需要将其都挑选出来,以为着这个位置可以是R也可以是r,用或表达式,在re中用方括号将多个字符括起来表示任选一个字符:
import re
of_reddit_count = 0
for row in posts:
if re.search("of [Rr]eddit", row[0]) != None:
of_reddit_count += 1
'''
of_reddit_count : 102 # 有102个Title中有"of Reddit"或者"of reddit"字符串
'''
‘\’——转义字符
- 转义字符’\’,比如我们想要获取有多少条Title中含有[Serious]字符串,如果直接写[Serious]则会认为是或表达式,因为[]方括号是特殊字符,因此需要在每一个[前面添加一个\转义字符:
import re
serious_count = 0
for row in posts:
if re.search("\[Serious\]", row[0]) != None:
serious_count += 1
Refining The Search
- 有人会用[Serious]有人会用[serious],我们要将这两种都挑选出来:
import re
serious_count = 0
for row in posts:
if re.search("\[[Ss]erious\]", row[0]) != None:
serious_count += 1
More Inconsistency
- 有的人可能会用”(Serious)” 或者 “(serious)”:
import re
serious_count = 0
for row in posts:
if re.search("[\[\(][Ss]erious[\]\)]", row[0]) != None:
serious_count += 1
“|”——或表达式,匹配前面或者后面
- “|”表示或,前面和后面任选一个:
import re
serious_start_count = 0
serious_end_count = 0
serious_count_final = 0
for row in posts:
if re.search("^[\[\(][Ss]erious[\]\)]", row[0]) != None:
serious_start_count += 1 #以serious开始 69
if re.search("[\[\(][Ss]erious[\]\)]$", row[0]) != None:
serious_end_count += 1 #以serious结束 11
if re.search("^[\[\(][Ss]erious[\]\)]|[\[\(][Ss]erious[\]\)]$", row[0]) != None:
serious_count_final += 1 #以serious开始或者以serious结束 80
sub——替换函数,找到匹配项进行替换
re模块中有一个替换函数sub(pattern,repl,string),表示将string中与pattern匹配的元素替换为repl.
- 为了统一表达,将”[serious]”, “(Serious)”, 和”(serious)” 都替换为 “[Serious]”
import re
posts_new = []
for row in posts:
row[0] = re.sub("[\[\(][Ss]erious[\]\)]", "[Serious]", row[0])
posts_new.append(row)
“-“——范围
- “[0-9]”匹配的是0到9之间的任意数字,”[a-z]”匹配a到z之间的任意字符。”[0-9][0-9][0-9][0-9]”匹配四位数。
# 提取出strings中年份在999到3000之间的元素
'''
strings
list (<class 'list'>)
['War of 1812', 'There are 5280 feet to a mile', 'Happy New Year 2016!']
'''
import re
year_strings = []
for string in strings:
if re.search("[1-2][0-9][0-9][0-9]", string) != None:
year_strings.append(string)
print(year_strings)
'''
['War of 1812', 'Happy New Year 2016!']
'''
“{}”——花括号值为重复的次数
- 前面匹配的时候有很多重复的项[0-9][0-9][0-9],而在re中有个特殊符号”{}”,花括号里的数字表示紧挨着的前面那个表达式重复多少次。匹配年份在1000到2999之间的字符串:
import re
year_strings = []
for string in strings:
if re.search("[1-2][0-9]{3}", string) != None:
year_strings.append(string)
Extracting Years
- re模块中有个含税findall(regex, string),它返回一个列表,包含了所有string中匹配regex的元素。比如re.findall(“[a-z]”, “abc123”) 将返回 [“a”, “b”, “c”]
import re
years = re.findall("[1-2][0-9]{3}", years_string)
'''
'2015 was a good year, but 2016 will be better!'
'''
print(years)
'''
['2015', '2016']
'''