[python]《Python編程快速上手:讓繁瑣工作自動化》學習筆記1

1. 模式匹配與正則表達式筆記(第七章)

正則表達式,又稱規則表達式。(英語:Regular Expression,在代碼中常簡寫爲regex、regexp或RE),計算機科學的一個概念。正則表達式通常被用來檢索、替換那些符合某個模式(規則)的文本。

1.1 正則表達式匹配步驟
雖然在Python 中使用正則表達式有幾個步驟,但每一步都相當簡單。

  1. 用import re 導入正則表達式模塊。
  2. 用re.compile()函數創建一個Regex 對象(記得使用原始字符串)。
  3. 向Regex 對象的search()方法傳入想查找的字符串。它返回一個Match 對象。
  4. 調用Match 對象的group()方法,返回實際匹配文本的字符。
# 用import re 導入正則表達式模塊
import re
# 創建了一個Regex對象
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
# 匹配字符串
mo = phoneNumRegex.search('My number is 415-555-4242.')
# group()方法返回實際匹配的字符串
print('phone num found is: ' + mo.group())
phone num found is: 415-555-4242

1.2 正則表達式匹配更多模式

  1. 利用括號分組
    添加括號將在正則表達式中創建“分組”(\d\d\d)-(\d\d\d-\d\d\d\d)。然後可以使用group()匹配對象方法,從一個分組中獲取匹配的文本。
  2. 用管道匹配多個分組
    字符|稱爲“管道”。希望匹配許多表達式中的一個時,就可以使用它。例如,正則表達式r’Batman|Tina Fey’將匹配’Batman’或’Tina Fey’。
  3. 用問號實現可選匹配
    有時候,想匹配的模式是可選的。就是說,不論這段文本在不在,正則表達式都會認爲匹配。字符?表明它前面的分組在這個模式中是可選的。也可以理解爲匹配這個問號之前的分組零次或一次。
  4. 用星號匹配零次或多次
    *(稱爲星號)意味着“匹配零次或多次”,即星號之前的分組,可以在文本中出現任意次。它可以完全不存在,或一次又一次地重複。
  5. 用加號匹配一次或多次
    加號+前面的分組必須“至少出現一次
  6. 用花括號匹配特定次數
    • 如果想要一個分組重複特定次數,就在正則表達式中該分組的後面,跟上花括號包圍的數字。例如,正則表達式(Ha){3}將匹配字符串’HaHaHa’。
    • 除了一個數字,還可以指定一個範圍,即在花括號中寫下一個最小值、一個逗號和一個最大值。例如,正則表達式(Ha){1,2}將匹配 ‘Ha’ 和 ‘HaHa’。
    • 也可以不寫花括號中的第一個或第二個數字,不限定最小值或最大值。例如,(Ha){3,}將匹配3 次或更多次實例。
  7. 用花括號和問號匹配非貪心模式
    Python 的正則表達式默認是“貪心”的,這表示在有二義的情況下,它們會儘可能匹配最長的字符串。花括號的“非貪心”版本匹配儘可能最短的字符串,即在
    結束的花括號後跟着一個問號。

1.3 findall()方法
search()將返回一個Match對象,包含被查找字符串中的“第一次”匹配的文本,而findall()方法將返回一組字符串,包含被查找字符串中的所有匹配。

import re
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
# search返回第一次匹配
mo = phoneNumRegex.search('Cell: 415-555-9999 Work: 212-555-0000')
mo.group()
'415-555-9999'
# findall返回所有結果
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']

1.4 字符分類

縮寫字符分類 表示
\d 0 到9 的任何數字
\D 除0 到9 的數字以外的任何字符
\w 任何字母、數字或下劃線字符(可以認爲是匹配“單詞”字符)
\W 除字母、數字和下劃線以外的任何字符
\s 空格、製表符或換行符(可以認爲是匹配“空白”字符)
\S 除空格、製表符和換行符以外的任何字符

1.5 建立自己的字符分類

  • 有時候你想匹配一組字符,但縮寫的字符分類(\d、\w、\s 等)太寬泛。你可
    以用方括號定義自己的字符分類。例如,字符分類[aeiouAEIOU]將匹配所有元音字
    符,不論大小寫。
import re
vowelRegex = re.compile(r'[aeiouAEIOU]')
vowelRegex.findall('RoboCop eats baby food. BABY FOOD.')
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']
  • 可以使用短橫表示字母或數字的範圍。例如,字符分類[a-zA-Z0-9]將匹配所有小寫字母、大寫字母和數字。
import re
vowelRegex = re.compile(r'[a-zA-Z0-9]')
vowelRegex.findall('RoboCop eats baby food. BABY FOOD.')
['R',
 'o',
 'b',
 'o',
 'C',
 'o',
 'p',
 'e',
 'a',
 't',
 's',
 'b',
 'a',
 'b',
 'y',
 'f',
 'o',
 'o',
 'd',
 'B',
 'A',
 'B',
 'Y',
 'F',
 'O',
 'O',
 'D']
  • 通過在字符分類的左方括號後加上一個插入字符(^),就可以得到“非字符類”。非字符類將匹配不在這個字符類中的所有字符。
import re
consonantRegex = re.compile(r'[^aeiouAEIOU]')
consonantRegex.findall('RoboCop eats baby food. BABY FOOD.')
['R',
 'b',
 'C',
 'p',
 ' ',
 't',
 's',
 ' ',
 'b',
 'b',
 'y',
 ' ',
 'f',
 'd',
 '.',
 ' ',
 'B',
 'B',
 'Y',
 ' ',
 'F',
 'D',
 '.']
  • 插入字符和美元字符
    正則表達式的開始處使用插入符號(^),表明匹配必須發生在被查找文本開始處。
    正則表達式的末尾加上美元符號,表示該字符串必須以這個正則表達式的模式結束。可以同時使用^和$,表明整個字符串必須匹配該模式。

1.6 通配字符

  1. .(句點)字符稱爲“通配符”。它匹配除了換行之外的所有字符。
  2. 用點-星(.*)表示“任意文本”。
  3. 通過傳入re.DOTALL 作爲re.compile()的第二個參數,可以讓句點字符匹配所有字符,包括換行字符。

1.7 正則表達式符號總結

img

1.8 sub() 替換字符串

Regex對象的sub()方法需要傳入兩個參數。第一個參數是一個字符串,用於取代發現的匹配。第二個參數是一個字符串,即正則表達式。sub()方法返回替換完成後的字符串。

import re
namesRegex = re.compile(r'Agent \w+')
namesRegex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
'CENSORED gave the secret documents to CENSORED.'

1.9 compile的第二個參數

  1. re.IGNORECASE 忽視大小寫
  2. re.VERBOSE 編寫註釋
  3. re.DOTALL 句點表示所有字符(沒有這個則表示除換行符外的所有字符)

2. 項目

2.1 電話號碼和E-mail 地址提取程序

功能:在剪貼板的文本中查找電話號碼和E-mail 地址,按一下Ctrl-A 選擇所有文本,按下Ctrl-C 將它複製到剪貼板,然後運行程序。找到電話號碼和E-mail地址,替換掉剪貼板中的文本。
文本例子地址:http://www.nostarch.com/contactus

主要步驟:
第1步:爲電話號碼創建一個正則表達式
第2步:爲E-mail 地址創建一個正則表達式
第3步:在剪貼板文本中找到所有匹配
第4步:所有匹配連接成一個字符串,複製到剪貼板

#! python3
# Finds phone numbers and email addresses on the clipboard.
# text:
'''
Contact Us

No Starch Press, Inc.
245 8th Street
San Francisco, CA 94103 USA
Phone: 800.420.7240 or +1 415.863.9900 (9 a.m. to 5 p.m., M-F, PST)
Fax: +1 415.863.9950

Reach Us by Email

General inquiries: [email protected]
Media requests: [email protected]
Academic requests: [email protected] (Please see this page for academic review requests)
Help with your order: [email protected]
Reach Us on Social Media
Twitter
Facebook
Instagram
Pinterest
100.420.7240 x 123
'''

import pyperclip
import re

# 爲電話號碼創建一個正則表達式
# 電話號碼 phone
phoneRegex = re.compile(r'''(
        (\d{3}|\(\d{3}\))? # area code
        (\s|-|\.)? # separator
        (\d{3}) # first 3 digits
        (\s|-|\.) # separator
        (\d{4}) # last 4 digits
        (\s*(ext|x|ext.)\s*(\d{2,5}))? # extension
        )''', re.VERBOSE)

# 爲E-mail 地址創建一個正則表達式
# Create email regex. 郵件
emailRegex = re.compile(r'''(
    [a-zA-Z0-9._%+-]+ # username
    @ # @ symbol
    [a-zA-Z0-9.-]+ # domain name
    (\.[a-zA-Z]{2,4}) # dot-something
    )''', re.VERBOSE)
# TODO: Create email regex.
# TODO: Find matches in clipboard text.
# TODO: Copy results to the clipboard.


# Find matches in clipboard text.
# 獲得剪切板文件
text = str(pyperclip.paste())
matches = []
a = phoneRegex.findall(text)
for groups in phoneRegex.findall(text):
    # 讀取數字
    phoneNum = '-'.join([groups[1], groups[3], groups[5]])
    # 如果沒有分號
    if groups[8] != '':
        phoneNum += ' x' + groups[8]
    matches.append(phoneNum)
for groups in emailRegex.findall(text):
    matches.append(groups[0])

# Copy results to the clipboard.
if len(matches) > 0:
    pyperclip.copy('\n'.join(matches))
    print('Copied to clipboard:')
    print('\n'.join(matches))
else:
    print('No phone numbers or email addresses found.')
Copied to clipboard:
800-420-7240
415-863-9900
415-863-9950
100-420-7240 x123
[email protected]
[email protected]
[email protected]
[email protected]

2.2 強口令檢測

寫一個函數,它使用正則表達式,確保傳入的口令字符串是強口令。強口令的定義是:長度不少於8 個字符,同時包含大寫和小寫字符,至少有一位數字。

import re
import pyperclip

def detect(text):
    if len(text)<8:
        return False
    test1=re.compile(r'([0-9])')
    result1=test1.search(text)
    if result1==None:
        return False
            
    test2=re.compile(r'([A-Z])')
    result2=test2.search(text)
    if result2==None:
        return False       
    
    test3=re.compile(r'([a-z])')
    result3=test3.search(text)
    if result3==None:
        return False
    
    return True        
            
text='uusssZmi0546'
status=detect(text)
print(status)
True

2.3 strip()的正則表達式版本

寫一個函數,它接受一個字符串,做的事情和strip()字符串方法一樣。如果只傳入了要去除的字符串,沒有其他參數,那麼就從該字符串首尾去除空白字符。否
則,函數第二個參數指定的字符將從該字符串中去除。

import re

def my_strip(text,param=''):
    if len(param)<1:
        param='\s'
    params_begin= r'^[{}]*'.format(param)
    params_end= r'[{}]*$'.format(param)
    my_begin=re.compile(params_begin,re.I)
    my_end=re.compile(params_end,re.I)
    my=my_begin.sub('', text)
    text=my_end.sub('', my)
    return text
text='SpamSpamBaconSpamEggsSpamSpam'
param='SPAM'    
text=my_strip(text,'SPAM')
print(text)
BaconSpamEgg
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章