python3: 字串和文字

阿里瓜瓜發表於2019-01-29

1. 分割字串-使用多個界定符【re.split()】

>>> line = 'asdf fjdk; afed, fjek,asdf, foo'
>>> import re
>>> re.split(r'[;,\s]\s*', line)
['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']

string 物件的 split() 方法只適應於非常簡單的字串分割情形, 它並不允許有多個分隔符或者是分隔符周圍不確定的空格。 當你需要更加靈活的切割字串的時候,最好使用 re.split() 方法

>>> fields = re.split(r'(;|,|\s)\s*', line)
>>> fields
['asdf', ' ', 'fjdk', ';', 'afed', ',', 'fjek', ',', 'asdf', ',', 'foo']
>>>

如果re.split()中使用了括號捕捉分組。如果使用了括號,那麼被匹配的文字也將出現在結果列表中。

如果不想保留分割字串到結果列表中去,但仍然需要使用到括號來分組正規表示式的話, 確保你的分組是非捕獲分組,形如 (?:...) 。比如

>>> re.split(r'(?:,|;|\s)\s*', line)
['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
>>>

2. 字串的開頭匹配或結尾匹配【str.startwith()或 str.endwith()】

如果想匹配多種可能時.只需將所有的匹配項放入到一個元組【tuple】中然後傳給str.startwith()或 str.endwith()中

>>> filenames
[ 'Makefile', 'foo.c', 'bar.py', 'spam.c', 'spam.h' ]
>>> [name for name in filenames if name.endswith(('.c', '.h')) ]
['foo.c', 'spam.c', 'spam.h']

3. 用Shell萬用字元匹配字串--【fnmath模組提供的兩個函式--fnmatch()和fnmatchcase()】

 Unix Shell 中常用的萬用字元(比如 *.py , Dat[0-9]*.csv 等)去匹配文字字串

#  Shell萬用字元
#
#  ? 任意單個字元
#  * 任意0個或多個字元
#  [ ] 匹配方括號中的任意一個字元,如[abc] 則匹配abc中的一個
#  [ - ] "-"代表範圍,例如:[a-z] 則匹配任意一個小寫字母; [0-9] 則匹配任意一個0-9之間的資料,但是注意[0-10]不可以,不能超過9
#  [^] 邏輯非. 例如: [^0-9]匹配任意一個非數字字元
>>> from fnmatch import fnmatch, fnmatchcase
>>> fnmatch('foo.txt', '*.txt')
True
>>> fnmatch('foo.txt', '?oo.txt')
True
>>> fnmatch('Dat45.csv', 'Dat[0-9]*')
True
>>> names = ['Dat1.csv', 'Dat2.csv', 'config.ini', 'foo.py']
>>> [name for name in names if fnmatch(name, 'Dat*.csv')]
['Dat1.csv', 'Dat2.csv']

fnmatch() 函式使用底層作業系統的大小寫敏感規則(不同的系統是不一樣的)來匹配模式。比如:

>>> # On OS X (Mac)
>>> fnmatch('foo.txt', '*.TXT')
False
>>> # On Windows
>>> fnmatch('foo.txt', '*.TXT')
True
>>>

如果你對這個區別很在意,可以使用 fnmatchcase() 來代替。它完全使用你的模式大小寫匹配。

常用場景:在處理非檔名的字串時候它們也是很有用的。檔名的匹配,最好使用 glob 模組

addresses = [
    '5412 N CLARK ST',
    '1060 W ADDISON ST',
    '1039 W GRANVILLE AVE',
    '2122 N CLARK ST',
    '4802 N BROADWAY',
]
你可以像這樣寫列表推導:

>>> from fnmatch import fnmatchcase
>>> [addr for addr in addresses if fnmatchcase(addr, '* ST')]
['5412 N CLARK ST', '1060 W ADDISON ST', '2122 N CLARK ST']
>>> [addr for addr in addresses if fnmatchcase(addr, '54[0-9][0-9] *CLARK*')]
['5412 N CLARK ST']
>>>

4. 字串匹配和搜尋

如果你想匹配的是字面字串,那麼你通常只需要呼叫基本字串方法就行, 比如 str.find() , str.endswith() , str.startswith() 或者類似的方法:

 對於複雜的匹配需要使用正規表示式和 re 模組。 為了解釋正規表示式的基本原理,假設你想匹配數字格式的日期字串比如 11/27/2012 ,你可以這樣做
 
>>> text1 = '11/27/2012'
>>> text2 = 'Nov 27, 2012'
>>>
>>> import re
>>> # Simple matching: \d+ means match one or more digits
>>> if re.match(r'\d+/\d+/\d+', text1):
... print('yes')
... else:
... print('no')
...
yes
>>> if re.match(r'\d+/\d+/\d+', text2):
... print('yes')
... else:
... print('no')
...
no
>>>

如果你想使用同一個模式去做多次匹配,你應該先將模式字串預編譯為模式物件。比如:

>>> datepat = re.compile(r'\d+/\d+/\d+')
>>> if datepat.match(text1):
... print('yes')
... else:
... print('no')
...
yes
>>> if datepat.match(text2):
... print('yes')
... else:
... print('no')
...
no
>>>

match() 總是從字串開始去匹配,如果你想查詢字串任意部分的模式出現位置, 使用 findall() 方法去代替。比如:

>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
>>> datepat.findall(text)
['11/27/2012', '3/13/2013']
>>>

在定義正則式的時候,通常會利用括號去捕獲分組。

#在定義正則式的時候,通常會利用括號去捕獲分組
#捕獲分組可以使得後面的處理更加簡單,因為可以分別將每個組的內容提取出來
>>> datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
>>> m = datepat.match('11/27/2012')
>>> m
<_sre.SRE_Match object at 0x1005d2750>
>>> # Extract the contents of each group
>>> m.group(0)
'11/27/2012'
>>> m.group(1)
'11'
>>> m.group(2)
'27'
>>> m.group(3)
'2012'
>>> m.groups()
('11', '27', '2012')
>>> month, day, year = m.groups()
>>>
>>> # Find all matches (notice splitting into tuples)
>>> text
'Today is 11/27/2012. PyCon starts 3/13/2013.'
>>> datepat.findall(text)
[('11', '27', '2012'), ('3', '13', '2013')]
>>> for month, day, year in datepat.findall(text):
... print('{}-{}-{}'.format(year, month, day))
...
2012-11-27
2013-3-13
>>>

正規表示式步驟: re.compile()編譯正規表示式字串, 然後使用match(), findall(), finditer()等方法

 

如果你想精確匹配,確保你的正規表示式以$結尾,就像這麼這樣:

>>> datepat = re.compile(r'(\d+)/(\d+)/(\d+)$')
>>> datepat.match('11/27/2012abcdef')
>>> datepat.match('11/27/2012')
<_sre.SRE_Match object at 0x1005d2750>
>>>

最後,如果你僅僅是做一次簡單的文字匹配/搜尋操作的話,可以略過編譯部分,直接使用 re 模組級別的函式。比如:

>>> re.findall(r'(\d+)/(\d+)/(\d+)', text)
[('11', '27', '2012'), ('3', '13', '2013')]
>>>

2.5 字串搜尋和替換

對於簡單的字面模式,直接使用 str.replace() 方法即可,比如:str.replace('tihuan_qian', 'tihuan_hou')

複雜的模式,使用re模組的sub()函式。

例: 11/27/2012 的日期字串改成 2012-11-27

>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
>>> import re
>>> re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)
'Today is 2012-11-27. PyCon starts 2013-3-13.'
>>>

sub() 函式中的第一個引數是被匹配的模式,第二個引數是替換模式。反斜槓數字比如 \3 指向前面模式的捕獲組號。

 如果你打算用相同的模式做多次替換,考慮先編譯它來提升效能。比如:
>>> import re
>>> datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
>>> datepat.sub(r'\3-\1-\2', text)
'Today is 2012-11-27. PyCon starts 2013-3-13.'
result, n  = re.subn(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)

re.subn()可以知道替換次數

 

相關文章