网络工程师——正则表达式(模糊匹配)
網絡工程師——正則表達式(模糊匹配)
(本博客借鑒《網絡工程師的python之路這本書》
1.什么是正則表達式
??正則表達式,又稱規則表達式,計算機科學的一個概念。正則表達式通常被用來檢索、替換那些符合某個模式(規則)的文本。
許多程序設計語言都支持利用正則表達式進行字符串操作。例如,在Perl中就內建了一個功能強大的正則表達式引擎。正則表達式這個概念最初是由Unix中的工具軟件(例如sed和grep)普及開的。正則表達式通常縮寫成“regex”,單數有regexp、regex,復數有regexps、regexes、regexen。
(來源百度百科)
2.正則表達式的規則(模糊匹配)
| . | 匹配除換行符之外的所有字符(一次) |
| * | 用來匹配緊靠該符號左邊的符號,匹配次數0次或多次。 |
| + | 用來匹配緊靠該符號左邊的符號,匹配次數1次或多次。 |
| ? | 用來匹配緊靠該符號左邊的符號,匹配次數0次或1次。 |
| {m} | 用來匹配緊靠該符號左邊的符號,指定匹配次數為m次,例如字符串’abbbbcccd’,使用ab{2}將匹配到abb,使用bc{3}d將匹配到bcccd。 |
| {m,n} | 用來匹配緊靠該符號左邊的符號,指定匹配次數為最少m次,最多n次。 例如字符串’abbcccd’,使用ab{2,3}將只能匹配到abb,如果字符串為’abbbbcccdabbccd’,使用ab{2,3}將能同時匹配到abbb和abb。如果字符串內容為’abcd’使用ab{2,3}將匹配不到任何東西。 |
| {m,} | 用來匹配緊靠該符號左邊的符號,指定匹配次數為最少m次,最多無限次。 |
| {,n} | 用來匹配緊靠該符號左邊的符號,指定匹配次數為最少0次,最多n次。 |
| \ | 例如字符串內容中出現了問號"?”,而你又想精確匹配這個問號,那就要使用?來進行匹配。除此之外,\也用來表示一個特殊序列。 |
| [] | 表示字符集合,用來精確匹配。比如想要精確匹配一個數字,可以使用[0-9]。如果要精確匹配一個小寫字母,可以用[a-z],如果要精確匹配一個大寫字母,可以用[A-Z],如果要匹配一個數字、字母或者下劃線,可以用[0-9a-zA-Z_]。另外在[]中加^表示取非,比如[^O-9]表示匹配一個非數字的字符,[^a-z]表示匹配一個非字母的字符,以此類推。 |
| | | 表示或匹配(兩項中匹配其中任意一項),比如要匹配FastEthernet和GigabitEthernet這兩種端口名,可以寫作Fa|Gi。 |
| (…) | 組合,匹配括號內的任意正則表達式,并標識出組合的開始和結尾,例如(blcd)ef表示bef或cdef。 |
| \d | 匹配任意一個十進制數字,等價于[0-9] |
| \D | ld取非,匹配任意一個非十進制數字,等價于[^0-9] |
| \w | 匹配任意一個字母,十進制數字以及下劃線,等價于[a-zA-Z0-9_] |
| \W | 匹配任意個一個空白字符,包括空格,換行符\n等等。 |
| \s | 匹配任意個一個空白字符,包括空格,換行符\n等等。 |
在使用表達式之前一定要先import re
2.1[]的應用
>>> re.match('a[a-z]c',`在這里插入代碼片`'abc') <re.Match object; span=(0, 3), match='abc'>匹配a-z之間任意字符 >>> re.match('a[a-z]c','a1c') >>> re.match('a[0-9]c','a1c') <re.Match object; span=(0, 3), match='a1c'>匹配0-9之間任意字符 >>> re.match('a[^a-z]c','a1c') <re.Match object; span=(0, 3), match='a1c'>匹配除了a-z之間任意2.2* ?()的應用
>>> re.match('ba(na)?','ba') <re.Match object; span=(0, 2), match='ba'> >>> re.match('ba(na)?','bana') <re.Match object; span=(0, 4), match='bana'> >>> re.match('ba(na)?','banana') <re.Match object; span=(0, 4), match='bana'> >>> re.match('ba(na)*','ba') <re.Match object; span=(0, 2), match='ba'> >>> re.match('ba(na)*','bana') <re.Match object; span=(0, 4), match='bana'> >>> re.match('ba(na)*','banana') <re.Match object; span=(0, 6), match='banana'>總結:? 0或1 * 0或無窮2.3 |的應用
>>> re.match('root|Root','root') <re.Match object; span=(0, 4), match='root'> >>> re.match('root|Root','Root') <re.Match object; span=(0, 4), match='Root'>2.4貪婪匹配
??*, +,?,{m}, {m,}, {m,n}這六種匹配符號默認都是貪婪匹配的,即會盡可能多的去匹配符合條件的內容。舉例如下:
舉例如下:
??假設給定的字符串為’xxzyzyz’,我們使用正則表達式x.*y來做匹配(注:精確匹配和模糊匹配可以混用)。在匹配到第一個x后,開始匹配.*,因為.和*默認是貪婪匹配,這里它會一直往后匹配,直到匹配到最后一個y,因此這里的匹配結果為xxzyzy。
(測試網站為https://regex101.com/)
2.5非貪婪匹配
??要實現非貪婪匹配很簡單,就是在上述六種貪婪匹配符號后面加上問號?即可,也即是*?, +?, ??, {m}?, {m,}?, {m,n}?。
因為.*?是非貪婪匹配,這里它在匹配到第一個y后便隨即停止,因此這里的匹配結果為xxzy。
?可以認為是對于?后面的字符生效,只要匹配到一次?后面的字符就結束匹配
2.6實例
匹配思科交換機中日志類型\w{1,9}-\d-\w{6,13}
000459: Feb 17 17:10:35.202: %LINK-3-UPDOWN: Interface FastEthernet0/2, changed state to up 000460: Feb 17 17:10:36.209: %LINEPROTO-5-UPDOWN: Line protocol on Interface FastEthernet0/2, changed state to up 000461: Feb 17 22:39:26.464: %SSH-5-SSH2_SESSION: SSH2 Session request from 10.1.1.1 (tty = 0) using crypto cipher 'aes128-cbc', hmac 'hmac-sha1' Succeeded 000462: Feb 17 22:39:27.748: %SSH-5-SSH2_USERAUTH: User 'test' authentication for SSH2 Session from 10.1.1.1 (tty = 0) using crypto cipher 'aes128-cbc', hmac 'hmac-sha1' Succeeded用LINK-3-UPDOWN舉例
%:匹配% w{1,9}:匹配 link -:匹配- \d:匹配3 w{6,13}:匹配 UPDOWN3 各種方法
3.1 re.mach()
(前文已經用過了)
??這里我們使用re.match()函數,從字符" Test match() function of regular expression. "里去精確匹配模式’Test’,因為’Test’位于該段字符串的起始位置,所以匹配成功,并且返回了一個匹配到的對象<re.Match object; span=(0, 4), match=‘Test’>,為了查看該對象的具體的值,我們可以對該對象調用group()方法,得到具體的值’Test’,該值的數據類型為字符串。
輸出:
<re.Match object; span=(0, 4), match='Test'> Test??如果這里我們不從字符串的起始位置去匹配,而是去匹配中間或末尾的字符串內容的話,那么re.match()將匹配不到任何東西,從而返回None,比如這里我們去匹配’function’這個詞,因為function不在’Test match() function of regular expression.'這句字符串的開頭,所以這里re.match()返回的值為None。
import re test='Test match() function or regular expression.' a=re.match(r"function",test) print(a) print(a.group())輸出
Nonere.match 必須從開頭匹配
a=re.match(r"function",test)中的r什么意思?意思是“ ”中的是原始字符串,總之在正則表達式中,建議都使用原始字符串。
3.2 re.search()
import re test='Test match() function or regular expression.' a=re.search(r"function",test) print(a) print(a.group())輸出:
<re.Match object; span=(13, 21), match='function'> functionre.search方法可以匹配字符串中的任意位置的內容。
但是它和re.match()一樣一次只能匹配到一個字串內容,比如下面是某臺路由器上show ip int brief的輸出結果,我們希望用正則表達式來匹配到在該輸出內容中出現的所有IPv4地址:
R6#sh ip int br Interface IP-Address OK? Method Status Protocol FastEthernet0/0 192.168.1.1 YES manual up up FastEthernet1/0 192.168.2.1 YES manual up up FastEthernet2/0 192.168.3.1 YES manual up up import re ip='''R6#sh ip int br Interface IP-Address OK? Method Status Protocol FastEthernet0/0 192.168.1.1 YES manual up up FastEthernet1/0 192.168.2.1 YES manual up up FastEthernet2/0 192.168.3.1 YES manual up up '''#用'''可以匹配大段字符串 ip_address=re.search(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}",ip) print(ip_address.group())輸出:
192.168.1.1??這里我們用正則表達式\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}做為模式來匹配任意IPv4地址,注意我們在分割每段IP地址的‘.’前面加了轉義符號\,如果不加\,寫成\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}的話,那么將會匹配到’GigabitEthernet1/1’中的’1/1’.
??print (a.group())后可以看到這里我們只匹配到了192.168.1.1這一個IPv4地址,如果想匹配到其他所有的IPv4地址,必須用到grre.findall()。
3.3 re.findall()
??如果字符串中有多個關鍵詞都能被匹配出來,那么可以使用re.findall()。與re.match()和re.search()不一樣的是,re.findall()的返回值是列表,不需要.group()。
import re ip='''R6#sh ip int br Interface IP-Address OK? Method Status Protocol FastEthernet0/0 192.168.1.1 YES manual up up FastEthernet1/0 192.168.2.1 YES manual up up FastEthernet2/0 192.168.3.1 YES manual up up '''#用'''可以匹配大段字符串 ip_address=re.findall(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}",ip) print(type(ip_address)) print(ip_address)結果:
<class 'list'> ['192.168.1.1', '192.168.2.1', '192.168.3.1']??這樣就匹配到了4個人ip地址。
3.4 re.sub()
??re.sub()函數用來替換字符串里被匹配到的字符串的內容。
R1#sh ip arp Protocol Address Age (min) Hardware Addr Type Interface Internet 192.168.13.1 - cc01.3ed8.0000 ARPA FastEthernet0/0 Internet 192.168.13.3 13 cc03.1048.0000 ARPA FastEthernet0/0??以路由器的arp表為例,替換兩個mac地址。
import re mac='''R1#sh ip arp Protocol Address Age (min) Hardware Addr Type Interface Internet 192.168.13.1 - cc01.3ed8.0000 ARPA FastEthernet0/0 Internet 192.168.13.3 13 cc03.1048.0000 ARPA FastEthernet0/0 '''#用'''可以匹配大段字符串 mac_address=re.sub(r"\w{4}\.\w{4}\.\w{4}",'1234.abcd.12ab',mac) print(type(mac_address)) print(mac_address)結果:
<class 'str'> R1#sh ip arp Protocol Address Age (min) Hardware Addr Type Interface Internet 192.168.13.1 - 1234.abcd.12ab ARPA FastEthernet0/0 Internet 192.168.13.3 13 1234.abcd.12ab ARPA FastEthernet0/0用1234.abcd.12ab替換了兩個mac地址,默認是匹配的到的都替換。用re.sub()返回值是str,也就是字符串,也不用.goup()
如果只想改變某一個位置的mac地址可以加上參數
import re mac='''R1#sh ip arp Protocol Address Age (min) Hardware Addr Type Interface Internet 192.168.13.1 - cc01.3ed8.0000 ARPA FastEthernet0/0 Internet 192.168.13.3 13 cc03.1048.0000 ARPA FastEthernet0/0 '''#用'''可以匹配大段字符串 mac_address=re.sub(r"\w{4}\.\w{4}\.\w{4}",'1234.abcd.12ab',mac,1) print(type(mac_address)) print(mac_address)re.sub()里有一個最后一個位置為optional選項,如果是1則只有第一個被匹配到的會被替換,如果是2則是前兩個被匹配到的會被替換。
結果:
但是如果你想只替換第二個,那么可以用精確匹配。
4 re.match()拓展與練習
??re.match()也可以輸出多個結果,但是本質上也是只匹配了一個字符串內容,但是用.groups()使得用正則表達式匹配出來的內容變成元組,再用元組的索引就可以做到輸出多個結果了。
例1:
輸出:
<class 'tuple'> ('Port-channel1.189', '192.168.189.254', 'YES', 'up') -------------------------------------------------------------------------------- 接口 :Port-channel1.189 IP地址 :192.168.189.254 狀態 :YES ('166', '54a2.74f7.0326', 'DYNAMIC', 'Gi1/0/11') -------------------------------------------------------------------------------- vlan id :166 mac :54a2.74f7.0326 Type :DYNAMIC interface :Gi1/0/11Process finished with exit code 0例2
import re tr = """TCP Student 192.168.189.167:32806 Teacher 137.78.5.128:65247, idle 0:00:00, bytes 74, flags UIO TCP Student 192.168.189.167:80 Teacher 137.78.5.128:65233, idle 0:00:03, bytes 334516, flags UIO""" str_list = tr.split('\n') #print(str_list )zidian={} for x in str_list:#print('1',x)result1 = re.match('(\s*\w+\s+\w+)\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).(\d+)\s+(\w+)\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).(\d+).\s+(\w+\s+)(\d.\d+.\d+.).*\w+\s(\d+).*\w+\s+(\w+)\s*',x).groups()#print('s',result1)key=result1[1],result1[2],result1[4],result1[6]value=result1[-2],result1[-1]zidian[key]=valueprint("打印字典") print(zidian) print("格式化打印輸出")for key in zidian:print('%10s:%-20s|%10s:%-20s|%10s:%-10s|%10s:%-20s|'%('src',key[0],'src_p',key[1],'dst',key[2],'dit_p',key[3]))print('%10s:%-20s|%10s:%-20s'%('bytes',zidian[key][0],'flags', zidian[key][1])) print("=" * 150)結果:
打印字典 {('192.168.189.167', '32806', '137.78.5.128', 'idle '): ('74', 'UIO'), ('192.168.189.167', '80', '137.78.5.128', 'idle '): ('334516', 'UIO')} 格式化打印輸出src:192.168.189.167 | src_p:32806 | dst:137.78.5.128| dit_p:idle |bytes:74 | flags:UIO src:192.168.189.167 | src_p:80 | dst:137.78.5.128| dit_p:idle |bytes:334516 | flags:UIO ======================================================================================================================================================總結
以上是生活随笔為你收集整理的网络工程师——正则表达式(模糊匹配)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 8的一些新用法
- 下一篇: JointJs快速入门