201803-3-URL映射
问题
思路
文本处理题:使用re正则匹配
步骤:
- 将每条URL映射规则转换为,(正则表达式模式,名字,输出参数列表res),存入pts列表
- 通过
str(int(res[i]))
,实现去掉前导零- 将每条URL去与pts列表匹配
实现
# 导入正则表达式模块
import re
# n:URL映射规则条数,m:待处理URL地址个数
n, m = map(int, input().split())
# 正则表达式模式,用于替换URL映射规则中的<str>、<int>、<path>标记
pat = re.compile(r'<int>|<str>|<path>')
# 存储每个URL映射规则的正则表达式模式、名称以及<int>标记个数的列表
pts = [] # exp[(re.compile('^/articles/([0-9]+)/([0-9]+)/([^/]+)/$'), 'article_detail', [0, 1]), ]
# 将n条规则转换为 pts
for i in range(n):
# pt:URL匹配规则,name:URL匹配名字
pt, name = input().strip().split(' ')
# 获取<int>标记个数的列表
int_mark = []
for j, pa in enumerate(pat.findall(pt)):
if pa == '<int>':
int_mark.append(j)
# 替换规则中的<str>、<int>、<path>标记为正则表达式模式
pt = pt.replace('<int>', '([0-9]+)')
pt = pt.replace('<str>', '([^/]+)')
pt = pt.replace('<path>', '(.+)')
pt = '^' + pt + '$' # ^表示匹配字符串的开头,$表示匹配字符串的结尾,这样可以确保整个字符串都与这个正则表达式模式匹配
# 将规则的正则表达式的模式、名称和<int>标记索引添加到列表中
pts.append((re.compile(pt), name, int_mark))
# 处理每个待处理的URL地址
for i in range(m):
# 初始化匹配结果
matched = False
# 获取URL地址输入
url = input()
# 迭代每个URL映射规则,直到找到一个匹配的规则
for pt, name, int_mark in pts:
res = pt.match(url)
# 如果匹配成功,则将匹配的结果转换为列表形式,以便后面修改<int>标记的值
if res != None:
res = list(res.groups())
for j in int_mark:
res[j] = str(int(res[j])) # 除去数字前面的0
# 输出匹配的名称和匹配的值,并将匹配结果标记为True
print(*([name] + res))
matched = True
break
# 如果没有匹配的规则,则输出"404"
if matched == False:
print('404')
1、
pat.findall(pt)
pat:正则表达式模式(r’||'),pt:URL匹配规则 返回得到一条URL匹配规则匹配该模式的列表,例如
pt: /articles/<int>/<int>/<str>/ article_detail pat.findall(pt)得到:[<int>,<int>,<str>]
2、
res = pt.match(url)
、res.groups()
从开头匹配,匹配成功返回res对象,失败则返回None
res.groups()为匹配成功的子串元组
pt: ^/articles/([0-9]+)/([0-9]+)/([^/]+)/$ url:/articles/1985/09/aloha/ res.groups()得到:('1985', '09', 'aloha')
3、
print(' '.join([name] + res))
' '.join()
是Python中字符串的一个方法,用于将一个可迭代对象中的所有元素以指定的字符连接成一个字符串。在这个实现中,' '.join([name] + res)
用于将匹配的规则名称和匹配的值连接成一个字符串,其中每个值之间以空格分隔。等价与
print(*([name] + res))
Appendix
正则
原始字符r' '
与普通文字量' '
的区别
原始字符只有一点特殊,就是其中反斜线符号仍作为转义符。
re.compile(r"C:\courses\python\progs")
与re.complie("C:\\courses\\python\\progs")
等价
⚠️:有反斜线建议使用原始字符r' '
常用正则表达式构造
构造 | 含义 |
---|---|
字符组描述符[...] , | 与方括号中的任一字符匹配,求补是:[^...] |
原点字符(.) | 通配符 |
常用字符组
字符组 等价与 \d
,\D
[0-9]
,[^0-9]
\s
,\S
与所有空白字符匹配,与所有非空白字符匹配 [ \t\v\n\f\r]
,[^ \t\v\n\f\r]
\w
,\W
与所有字符数字字符下划线匹配,与所有非字符数字字符下划线匹配 [0-9a-zA-Z_]
,[^ 0-9a-zA-Z_]
exp:
p\w\w\w
与以p开头随后为任意三个字母数字的串匹配
- 重复描述符
重复描述符:* 、+ | 0次或任意多次、1次或任意多次 |
可选描述符:? | 比如’-?\d+‘ 表示整数(可能负) |
重复次数描述符:{m} | |
重复次数的范围描述符:{m,n} |
- 选择描述符
|
- 首位描述符
^
、$
例如上面的例子中
'^' + pt + '$'
用于将这个不完整的正则表达式模式转换成一个完整的正则表达式模式。^
表示匹配字符串的开头,$
表示匹配字符串的结尾,这样可以确保整个字符串都与这个正则表达式模式匹配。因此,使用^
和$
将不完整的正则表达式模式包起来,就可以得到一个完整的正则表达式模式,用于进行匹配。
常用方法
re.compile(pattern, flags=0) | 编译正则表达式模式,返回一个正则表达式对象 |
re.search(pattern, string, flags=0) | 在字符串中搜索匹配正则表达式模式的第一个位置,返回一个匹配对象 |
re.match(pattern, string, flags=0) | 从字符串的开头开始匹配正则表达式模式,返回一个匹配对象 |
re.findall(pattern, string, flags=0) | 搜索字符串中所有匹配正则表达式模式的字符串,返回一个列表 |
re.sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替换字符串中所有匹配正则表达式模式的子串,返回替换后的字符串 |
re.split(pattern, string, maxsplit=0, flags=0) | 根据正则表达式模式分割字符串,返回一个列表 |
re.finditer(pattern, string, flags=0) | 搜索字符串中所有匹配正则表达式模式的字符串,返回一个迭代器,迭代器中每个元素都是一个匹配对象 |
re.escape(string) | 对字符串中的特殊字符进行转义,返回转义后的字符串 |
re.match(pattern, string, flags=0)
等价与pattern.match(string)
search()、match()、findall()
对比
import re
pattern = r'\d+' # 匹配一个或多个数字
text = 'abc123def456ghi'
match_obj = re.search(pattern, text)
if match_obj:
print(match_obj.group()) # 123
regex = re.compile(pattern)
result = regex.findall(text)
print(result) # ['123', '456']
match_obj = re.match(pattern, text)
if match_obj:
print(match_obj.group()) # None,因为在字符串的开头没有匹配到数字