正則表達式
正則表達式縮寫為regex、regexp、RE等。他是文本處理極為重要的技術,其應用非常廣泛,shell中處理文本的命令、各種高級編程語言都支持正則表達式,用它可以對字符串按照某種規則進行檢索和替換,
分類
- BRE:基本正則,grep、sed、vi等軟件支持。vim支持擴展正則
- ERE:擴展正則,egrep(grep -E)、sed -r等。
- PCRE:幾乎所有高級語言都是PCRE的變種。Python的RE可以認為是PCRE的子集
基本語法
- 元字符(matchcharacter)
代碼 | 說明 | 舉例 |
---|---|---|
. | 匹配除換行符外任意一個字符 | |
[abc] | 字符集合,只能表示一個字符位置。匹配所包含的任意一個字符 | [abc]匹配plain中的’a’ |
[^abc] | 字符集合,只能表示一個字符位置。匹配出去集合內字符的任意一個字符 | [^abc]可以匹配plain中p、l、i、n |
[a-z] | 字符范圍,也是集合,只能表示一個字符位置。匹配集合所包含的任意一個字符 | |
[^a-z] | 字符范圍,也是集合,只能表示一個字符位置。匹配除集合內字符的任意一個字符 | |
\b | 匹配單詞邊界 | \bb在文本中找到單詞中b開頭的b字符 |
\B | 不匹配單詞的邊界 | t\b包含t的單詞但是不以t結尾的t字符,例如write;\Bb不以b開頭的含有B的單詞,例如table |
\d | [0-9]匹配1位數字 | |
\D | [^0-9]匹配一位非數字 | |
\s | 匹配1位空白字符,包含換行符、制表符、空格 | |
\S | 匹配一位非空白字符 | |
\w | 匹配[a-zA-Z0-9_],包括中文的字 | |
\W | 匹配\w之外的字符 |
- 單行模式:
.可以匹配所有字符,包括換行符 ^表示整個字符串開頭,$表示整個字符串的結尾
- 多行模式:
.可匹配除換行符之外的字符 ^表示行收,$表示行尾 ^表示整個字符串的開始,$表示整個字符串的結尾。開始指的是\n后緊接著的下一個字符,結束指的是\n前的字符
注意:字符串中看不見的換行符,windows中的回車換行符\r\n會影響$的測試。
- 轉義:凡是在正則表達式中有特殊意義的符號,如果想使用它的本意,請使用\轉義。反斜杠自身,需使用\\;\r、\n是轉義后代表回車、換行
- 重復
符號 | 說明 | 舉例 |
---|---|---|
* | 表示前面的正則表達式會重復0次或多次 | e\w*單詞e后面有0到任意個非空字符 |
+ | 表示前面的正則表達式重復至少一次 | e\w+單詞e后至少有一個非空白字符 |
? | 表示前面的正則表達式會重復0或1次 | e\w?單詞e后最多有一個非空白字符 |
{n} | 重復固定的n次 | e\w{1}單詞e后面只能有一個非空白字符 |
{n,} | 重復至少n次 | e\w{1,}等價e\w+;\w{0,}等價e\w*;e\w{0,1}等價e\w? |
{n,m} | 重復至少n次,最多m次 | e\w{1,10}單詞e后面至少1個,最多是個非空白字符 |
- 練習
- 匹配手機號碼:\d{11}
- 匹配固定電話號碼\d{3-4}-\d{7-8}
- 分組
代碼 | 說明 |
---|---|
x|y | 匹配x或y |
(pattern) | 使用小括號制定一個子表達式,也叫分組。捕獲后會自動分配組號從1開始,可以改變優先級 |
\數字 | 匹配對應分組 |
(?:pattern) | 如果僅僅為了改變優先級,就不需要捕獲分組 |
(?<name>exp)(?’name’exp) | 分組捕獲,但是可以通過name訪問分組 |
零寬斷言 | |
(?=exp) | 零寬度正預測先行斷言:斷言exp一定在匹配的右邊出現,也就是說斷言后面一定跟個exp |
(?<=exp) | 零寬度正回顧:后發斷言:斷言exp一定出現在匹配的左邊,業績是前面一定有個exp前綴 |
負向零寬斷言 | |
(?!exp) | 零寬度付預測先行斷言:斷言一定不會在右側 |
(?<!exp) | 零寬度負回顧后發斷言:斷言一定不能出現在左側 |
注釋 | |
(?#comment) | 注釋 |
總結:分組和捕獲是同一個意思;同時在正則中能用簡單表達式就不要用復雜的
貪婪與非貪婪
正則表達殺死默認是貪婪模式,盡量多匹配更長的字符串。非貪婪模式很簡單就是再重復的符號后加一個”?”,匹配最少的字符串。
代碼 | 說明 |
---|---|
*? | 匹配人一次,盡量少重復 |
+? | 匹配至少一次,少重復 |
?? | 匹配0或依次,少重復 |
{n,}? | 匹配至少n次,少重復 |
{n,m}? | 匹配n到m次,,少重復 |
- 引擎選項
代碼 | 說明 | Python |
---|---|---|
IgnoreCase | 匹配時忽略大小寫 | re.I,re.IGNORECASE |
Singleline | 單行模式:.可以匹配所有字符,包括\n | re.S,re.DOTALL |
Multiline | 多行模式:^行首,$行尾 | re.M,re.MULTILINE |
IgnorePatternWhitespace | 忽略表達式中的空白字符,如果要使用空白字符用轉義,#可以用來做注釋 | re.X,re.VERBOSE |
練習
- 匹配0-999之間的數字 #匹配兩位數 [1]?\d #匹配三位數 ^([1-9]\d\d?|\d)(?!\w+) ^([1-9])\d\d?|\d)\r?$
- 匹配點分四段式IP
關于IP地址驗證問題: - 可以把數據提取出來后交給IP地址解析庫(socket模塊)處理,解析異常就說明IP有問題,如果正常則表示Ip合法 import socket nw = socket.inet_aton('1.2.4.8') # 輸入IP字符串,返回bytes,如果拋出異常則表示IP不合法 print(nw,socket.inet_ntoa(nw)) # inet_ntoa是反向解析 - 可以使用復雜正則表達式驗證地址正確性 - 前導0是可以的
(?:(25[0-5]|2[0-4]\d|[01]?\d\d?).){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)
- 選出有Ftp的連接,且文件類型是gz或者xz的文件名
ftp://ftp.astron.com/pub/file/file-5.14.tar.gzftp://ftp.gmplib.org/pub/gmp-5.1.2/gmp-5.1.2.tar.xzftp://ftp.vim.org/pub/vim/unix/vim-7.3.tar.bz2http://anduin.linuxfromscratch.org/sources/LFS/lfs-packages/conglomeration//iana-etc/iana-etc-2.30.tar.bz2http://anduin.linuxfromscratch.org/sources/other/udev-lfs-205-1.tar.bz2http://download.savannah.gnu.org/releases/libpipeline/libpipeline-1.2.4.tar.gzhttp://download.savannah.gnu.org/releases/man-db/man-db-2.6.5.tar.xzhttp://download.savannah.gnu.org/releases/sysvinit/sysvinit-2.88dsf.tar.bz2http://ftp.altlinux.org/pub/people/legion/kbd/kbd-1.15.5.tar.gzhttp://mirror.hust.edu.cn/gnu/autoconf/autoconf-2.69.tar.xzhttp://mirror.hust.edu.cn/gnu/automake/automake-1.14.tar.xz
<?<=.*ftp.*/>[^/]\.(?:gz|xz)
Python的re
Python使用re模塊提供了正則的處理能力
常量
- re.M = re.MULTILINE -> 多行模式
- re.S = re.DOTALL -> 單行模式
- re.I = re.IGNORECASE -> 忽略大小寫
- re.X = re.VERBOSE -> 忽略表達式中的空白字符
使用‘|’位或運算符開啟多種選項
方法
- 編譯
- re.compile(pattern,flags=0)
- 設定flags,編譯模式,返回正則表達式對象
- pattern是正則表達式字符串,flags是選項。正則表達式需要被編譯,為了提高效率,這些便宜的結果被保存,下次使用就不需要再次編譯。re的其他方法為提高效率都調用了編譯方法。
- re.compile(pattern,flags=0)
- 單次匹配
- re.match(pattern,string,flags=0)/regex.match(string[,pos[,endpos]])match匹配從字符串的開頭匹配,regex對象match方法可以設定開始位置和結束位置。返回match對象
- re.search(pattern,string,flags=0)/regex.search(string[,pos[,edpos]])從頭搜索知道第一個匹配,regex對象search方法可以設定開始位置和結束位置,返回match對象
- re.fullmatch(pattern,string,flags=0)/regex.match(string[,pos[,endpos]])這個字符串和正則表達式匹配,要完全匹配,多了或少了都不行
- 以上三種方法返回的都是match對象
- 全部匹配
- re.findall(pattern,string,flags=0)等價于regex.findall(string[,pos[,endpos]])對整個字符串從左到右匹配,返回所有匹配項的列表
- re.finditer(pattern,string,flags=0)等價于regex.finditer(string[,pos[,endpos]])對整個字符串從左向右匹配,返回所有匹配項的迭代器。每次迭代返回的是match對象
- 匹配替換
- re.sub(pattern,replacement,string,count=0,flags=0)使用pattern對字符串string進行匹配,對匹配項使用repl替換。repl可以是string、bytes、function
- subn(pattern,replacement,string,count=0,flags=0)返回一個元組(new_string,number_of__subs_made)
- 分割字符串
- 字符串分割函數不能指定多個字符進行分割,不好用。
- re.split(pattern,string,maxsplit=0,flags=0)maxsplit指最大分割數,默認是全部分割
import re s = '''01 bottle 02 bag 03 big1 100 able''' print(s.split()) #做不到 ret = re.split('[\s\d]+',s) print(1,ret) regex = re.compile('^[\s\d]+') ret = regex.split(s) print(2,ret) regex = re.compile('^[\s\d]+',re.M) ret = regex.split(s) print(3,ret) regex = re.compile('\s+\d+\s+') ret = regex.split(' ' + s) print(4,ret)
- 分組使用小括號的pattern捕獲的數據被放到了組group中。
- match、search函數可以返回match對象;findall返回字符串列表;finditer返回一個個match對象
- 如果pattern中使用了分組,如果有匹配的結果,會在match對象中:使用group(N)方式返回對應分組,1-n是對應的分組,0返回整個匹配的字符串;如果使用了命名分組,可以使用group(‘name’)的方式取分組;也可以使用groups()返回所有命名的分組;使用groupdict()返回所有命名的分組組成的字典
練習
- 匹配郵箱地址
test@hot-mail.com v-ip@magedu.com web.manager@magedu.com super.user@gmail.com a@w-a-com
- 匹配html標記內的內容
<a href='http://www.baidu.com/index.html' target='_blank'>馬哥教育</a>
- 匹配url
http://www.baidu.com/index.html https://login.magedu.com file:///etx/syconf/newksa
- 匹配二代身份證ID
- 匹配密碼強弱
- 單詞統計
1.
[\w-]+@[^\s-]+.com
\w+[-.\w]*@[\w-]+(\.[\w-]+)+
2.
<a href='(?<url>[^']+)' [^<>]+>\w+</a>
html提取
<[^<>]+>(.*)<[^<>]+>
匹配標記a
<(\w+)\s+{^<>}+>(.*)(</\1>)
3.
url提取
(\w+)://([^\s]+)
4.
\d{17}[0-9xX]|\d{15}
5.
^[a-zA-Z0-9_]{10,15}$
6. 單詞統計
import re
from collections import defaultdict
regex=re.compile(r'[^\w-]')
d = defaultdict(lambda : 0)
with open('d:/sample.txt',encoding='utf-8') as f:
for line in f:
for x in regex.split(line):
if len(x) >= 1:
d[x.lower()] += 1
# print(d)
print(sorted(d.items(),key= lambda x:x[1],reverse=True))
print(d['path1'],d['sub-path'])
def word_cnt(path:str):
d = defaultdict(lambda :0)
with open('d:/sample.txt',encoding='utf-8') as f:
for line in f:
for x in regex.split(line):
if len(x) >= 1:
d[x.lower()] += 1
return d
#print(word_cnt('d:/sample.txt'))
i = 0
for x in sorted(word_cnt('d:/sample.txt').items(),key = lambda x:x[1],reverse=True):
if i < 10:
print(x)
i += 1
print(word_cnt('d:/sample.txt')['path1'],word_cnt('d:/sample.txt')['sub-path'])
- 1-9 ?
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88237