精通正则表达式

时间:2022-10-06 16:39:07

举例

-----------------\<cat\> 匹配cat单词,\<表示但是开始 不好使
\<([a-zA-Z]+) +\1\> 在一个表达式中可以使用多个括号,再用\1 \2 \3来表示第一第二第三组括号匹配的文本,在python中以group(1)这种方式使用
[a-zA-Z_][a-zA-Z0-9_]* 匹配变量名
"[^"]*" 匹配引号内的字符串
\$[0-9]+(\.[0-9][0-9])? 匹配美元
Jan ([12][0-9]|3[01]|0?[1-9]) 匹配日期,注意多选分支的书写顺序

第三章 技术

re.sub(r'(\d*?\.\d\d[1-9]?)\d*',r'\1','1.25300046')    保留三位小数
re.sub(r'(\.\d\d(?>[1-9]?))\d+',r'\1','1.25300046') 保留三位小数 , (?>...)固化分组,固化分组中的备选状态会被抛弃,python不支持固化分组
re.sub(r'(\.\d\d[1-9]?+)\d+',r'\1','1.25300046') 保留三位小数,?+,*+,++,{m,n}+表示占有优先量词,和固化分组类似,占有优先量词从不交还已经匹配的字符,python不支持占有优先量词
re.sub(r'(\.\d\d(?=([1-9]?))\2)\d+',r'\1','1.25300046') 保留三位小数,使用肯定环视模拟固化分组
re.sub(r'(\.\d\d[1-9]|\.\d\d)\d+',r'\1',a) 保留三位小数,如果把多选分支中的顺序调换的话,表达式没有意义

re.match(r'^([0-9]+)(a)(?:[\t\n])?([0-9])','987a\t654') ?:表示非捕获型括号
b = re.match(r'(?P<num1>This) (?P<num2>is)','This is a test') b.group('num1') 命名捕获
re.match(r'^([0-9]+)(a)(?:[\s])?([0-9])','987A\t654',re.I) re.I 不区分大小写
re.sub(r'\bJeff','aaa','IJeff Jeffggg') \b单词分界符
re.sub(r'(?<=\bJeff)(?=s\b)',"'",'my name is Jeffs') (?=)肯定顺序环视,(?<=)肯定逆序环视
re.sub(r'(?<=\d)(?=(?:\d\d\d)+$)',",",'1234567890') (?=)肯定顺序环视,(?<=)肯定逆序环视
re.sub(r'(?<=\d)(?=(\d\d\d)+(?!\d))',',','tone of 12345Hz') (?!)否定顺序环视, (?<!)否定逆序环视
re.findall(r'((?i)a)','I AM a i am') ((?i)......) 括号内不区分大小写
re.sub(r'(is).*(a)',r'\1','This is a test') 反向引用
re.sub(r'(T.*?is)',r'zzzz','This is a test This is a test') 非贪婪模式
re.search(r'(?i)a(?i)*','aA').group() (?i).....(?i)某一部分不区分大小写

第四章 表达式的匹配原理

1.优先选择最左端(最靠开头)的匹配结果
匹配先从需要查找的字符串的起始位置尝试匹配。在这里,“尝试匹配”的意思是,在当前位置测试整个正则表达式能匹配的每样文本。如果在当前位置测试了所有的可能之后不能找到匹配结果,就需要从字符串的第二个字符之前的位置开始重新尝试。在找到匹配结果以前必须在所有的位置重复此过程。只有在尝试过所有的起始位置(直到字符串的最后一个字符)都不能找到匹配结果的情况下,才会报告“匹配失败”。
2.标准的匹配量词是匹配优先的(* ? + {m,n})
即贪婪模式,匹配尽可能多的

NFA:表达式驱动模式
不同的表达式写法会对匹配顺序和效率产生影响
匹配失败是报告失败
多选结构的匹配顺序是按照书写顺序的
回溯机制:NFA引擎最重要的性质是,他会依次处理各个子表达式或组成元素,遇到需要再两个可能成功的可能中 进行选择的时候,他会选择其一,同时记住另一个,以备稍后可能的需要。需要作出选择的情形包括量词和多选结构。不论选择哪一种途径,如果他能匹配成功,而且正则表达式的余下部分也成功了,匹配即告完成,如果正则表达式的余下部分最终失败,引擎会知道回溯到之前做选择的地方,选择其他的备用分支继续尝试。
回溯的两个要点:
1:面对众多选择时,哪个分支应当首先选择
如果要在”进行尝试“和”跳过尝试“之间进行选择,对于匹配优先的量词,引擎会优先选择”进行尝试“,而对于忽略优先量词,会选择”跳过尝试“
re.search(r'^.*([0-9]+)','abc123').group(1) 结果是3
re.search(r'^.*?([0-9]+)','abc123').group(1) 结果是123
2:回溯进行时,应该选择哪个保存的状态
距离当前最近存储的选项就是当本地失败强制回溯时返回的,使用的原则是LIFO(last in first out 后进先出)
固化分组:
re.sub(r'(\.\d\d(?>[1-9]?))\d+',r'\1','1.25300046') 保留三位小数 , (?>...)固化分组,固化分组中的备选状态会被抛弃,python不支持固化分组
re.sub(r'(\.\d\d[1-9]?+)\d+',r'\1','1.25300046') 保留三位小数,?+,*+,++,{m,n}+表示占有优先量词,和固化分组类似,占有优先量词从不交还已经匹配的字符,python不支持占有优先量词
re.sub(r'(\.\d\d(?=([1-9]?))\2)\d+',r'\1','1.25300046') 保留三位小数,使用肯定环视模拟固化分组

DFA:文本驱动模式
不同的表达式写法基本不会对匹配效率产生影响
匹配失败时返回失败中匹配成功的那部分,报告失败
多选结构的匹配顺序是按照最左最长原则的

第五章 技巧

正则表达式平衡法则
1.只匹配期望的文本,排除不期望的文本
2.易于控制和理解
3.如果使用NFA引擎,必须保证效率(如果能匹配,尽快返回结果,如果不能匹配,尽快报告失败)


匹配多行文本 re.search(r'^\w+ =(.*)',a\\\nb\nc,re.S).group() re.S 点任意匹配模式,点能匹配换行符
匹配ip re.search(r'(?<![\w.])\b(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\.(2[0-4]\d|25[0-5]|[01]?\d\d?)\b(?![\w.])','ip=192.168.1.1 and nice').group() 多选分支的顺序是将匹配三位数字的分支放在前面,否定环视保证只匹配到四个数字

匹配文件路径 re.search(r'^(.*)/([^/]*)$','/bin/config/all.txt').group(1)
匹配文件名 re.search(r'^(.*)/([^/]*)$','/bin/config/all.txt').group(2)
匹配嵌套括号 re.search(r'\([^()]*(\([^()]*\)[^()]*)*\)','val = foo(bar(this), 3.7) + 2 * (that - 1);').group() 这里是匹配单层嵌套的括号,正则表达式不支持匹配任意深度的嵌套结构,任意深度嵌套需要语言支持,perl,php支持
匹配浮点数 re.search(r'-?((?<=[^.\d])[0-9]+(\.[0-9]+)?((?=[^.\d])|(?=$))|(?<=[^.\d])\.[0-9]+((?=[^.\d])|(?=$)))',a).group()
匹配引号中内容 re.search(r'"(\\.|[^\\"])*"','"you need a 2\\"x3\\" photo"').group() 允许引号中嵌套转义引号
去掉首尾空白字符 re.sub(r'^\s*((?:.*\S)?)\s*$',r'\1',' I am a boy ')
匹配HTML tag re.search(r'<("[^"]*"|\'[^\']*\'|[^\'">])*>','<input name=dir value=">">').group() 对单引号转义是因为和r''这个原生字符串的单引号冲突了
匹配HTML link re.search(r'a\b\s*href\s*=\s*("[^"]*"|\'[^\']*\'|[^\'">])*>(.*?)</a>','<a href="http://www.oreilly.com">O\'Reilly Media</a>',re.I|re.S).group(1) group(2)是匹配链接文本
匹配URl中的host,port,path re.search(r'^http://([^:/]+)(:(\d+))?(/.*)?$',"http://www.oreilly.com:8080/job/view").group(1)
匹配URl re.search(r'(\w(?:[-\w]*\w)?\.)+(com|edu|biz|org|gov|in(?:t|fo)|mil|net|name|museum|coop|aero|[a-z][a-z])\b','www.OReilly.com').group()
匹配44开头的5位数字 re.findall(r'(?:(?!44)\d\d\d\d\d)*(44\d\d\d)','3454587654445459876744564')