2.1 使用多个界定符分割字符串

需要将一个字符串分割为多个字段,但是分割符( and 周围の空格)并不是固定

string 对象 的 split() 只是用与非常简单 の 字符串分割情况,起不允许有多个分割符 OR分隔符周围不确定 の 空格 When要更加灵活地切割字符串 の 时候 最好 使用 re.split()


In [5]:
line = 'asdf fjdk; afed, fjedk, asdef , foo'
import re
line_new = re.split(r'[;,\s]\s*',line)

In [4]:
line_new


Out[4]:
['asdf', 'fjdk', 'afed', 'fjedk', 'asdef', '', 'foo']

re.split() 允许为分隔符指定一个 Regular mode 在以上例子中:分隔符可以是 逗号 分号 OR 空格 同时允许后面紧跟任意个空格 只要这个模式被找到 则匹配 の 分隔符两边 の 实体都会被当成结果中 の element 返回结果为一个字段 list 与 str.split() 返回值类型一致
When 使用 re.split() 需要注意 Regular Expression 是否包含一个括号捕获分组 IF 使用捕获分组,则被匹配的文本亦会出现在结果 list 中


In [7]:
fields = re.split(r'(;|,|\s)\s*',line)
fields


Out[7]:
['asdf',
 ' ',
 'fjdk',
 ';',
 'afed',
 ',',
 'fjedk',
 ',',
 'asdef',
 ' ',
 '',
 ',',
 'foo']

捕获到 分割字符 亦可使用 想要保留之 用来在后面重新构造一个新的输出 string


In [9]:
values = fields[::2]
delimiters = fields[1::2] + ['']
values


Out[9]:
['asdf', 'fjdk', 'afed', 'fjedk', 'asdef', '', 'foo']

In [10]:
delimiters


Out[10]:
[' ', ';', ',', ',', ' ', ',', '']

In [11]:
# Reform the line using the same delimiters
''.join(v + d for v, d in zip(values, delimiters))


Out[11]:
'asdf fjdk;afed,fjedk,asdef ,foo'

IF 不想保留分隔符 字符串 到 list 中去,但仍然要是用括号来分组 Regular Expression 的话,确保你的 分组是非捕获分组,形如 (?:...)


In [12]:
re.split('(?:,|;|\s)\s*',line)


Out[12]:
['asdf', 'fjdk', 'afed', 'fjedk', 'asdef', '', 'foo']

2.2 字符串开头 OR 结尾匹配

需要制定的文本模式去检查字符串的开头 OR 结尾,比如文件名后缀 URL Scheme等

检查字符串开头 OR 结尾 使用 str.startswith() AND str.endswith() method


In [3]:
filename = 'spm.txt'
filename.endswith('.txt')


Out[3]:
True

In [4]:
filename.startswith('spa')


Out[4]:
False

In [5]:
url = 'http://www.python.prg'
url.startswith('http:')


Out[5]:
True

需要检查多种匹配可能 只需将所有匹配项放入到一个元组中去 然后传入 startswith OR endswith()


In [6]:
import os
filenames = os.listdir('.')
filenames


Out[6]:
['.git',
 '.gitignore',
 '.ipynb_checkpoints',
 'data_structure_and_algorithm_py2_1.ipynb',
 'data_structure_and_algorithm_py3_2.ipynb',
 'data_structure_and_algorithm_py3_3.ipynb',
 'data_structure_and_algorithm_py3_4.ipynb',
 'data_structure_and_algorithm_py3_5.ipynb',
 'data_structure_and_algorithm_py3_6.ipynb',
 'data_structure_and_algorithm_py3_7.ipynb',
 'data_structure_and_algorithm_py3_8.ipynb',
 'LICENSE',
 'README.md',
 'somefile.txt',
 'test.py',
 'write_somefile.py']

In [7]:
[name for name in filenames if name.endswith(('.ipynb','.py'))]


Out[7]:
['data_structure_and_algorithm_py2_1.ipynb',
 'data_structure_and_algorithm_py3_2.ipynb',
 'data_structure_and_algorithm_py3_3.ipynb',
 'data_structure_and_algorithm_py3_4.ipynb',
 'data_structure_and_algorithm_py3_5.ipynb',
 'data_structure_and_algorithm_py3_6.ipynb',
 'data_structure_and_algorithm_py3_7.ipynb',
 'data_structure_and_algorithm_py3_8.ipynb',
 'test.py',
 'write_somefile.py']

In [8]:
any(name.endswith('.py') for name in filenames)


Out[8]:
True

奇怪点:这个方法必须以一个元组作为参数 IF 恰好有一个 list OR set 类型的选择项 AND 确保传递参数前先调用 tuple 将其转换为 元组型


In [11]:
choices = ['htttp:','ftp:']
url.startswith(choices)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-a5abdf1592ba> in <module>()
      1 choices = ['htttp:','ftp:']
----> 2 url.startswith(choices)

TypeError: startswith first arg must be str or a tuple of str, not list

In [12]:
url.startswith(tuple(choices))


Out[12]:
False

startswith AND endswith method 提供了一个非常方便的方式去做字符串 开头 AND 结尾 的检查 类似的操作亦可使用 slice 来实现 BUT 不优雅


In [13]:
filename[-4:] == '.txt'


Out[13]:
True

In [14]:
url[:5] == 'http:' or url[:6] == 'https:' or url[:4] == 'ftp:'


Out[14]:
True

亦可使用正则表达式来实现


In [20]:
import re
re.match('http:|https:|ftp:',url)


Out[20]:
<_sre.SRE_Match object; span=(0, 5), match='http:'>

当和其它操作 如 检查某个文件夹中是否存在指定的文件类型


In [1]:
if any(name.endsw)


  File "<ipython-input-1-7c62ec3253ef>", line 1
    if any(name.endsw)
                      ^
SyntaxError: invalid syntax

2.3 用 Shell 通配符匹配字符串

需要使用 Unix Shell 中常用的通配符 (比如 *.py,Dat[0-9]*.csv 等)去匹配字符串

fnmatch module 提供 两个 Func fnmatch() OR fnmatchcase() 来实现这样匹配


In [2]:
from fnmatch import fnmatch,fnmatchcase
fnmatch('foo.txt','*.txt')


Out[2]:
True

In [3]:
fnmatch('foo.txt','?oo.txt')


Out[3]:
True

In [4]:
fnmatch('Data45.csv','Dat[0-9]*')


Out[4]:
False

In [5]:
fnmatch('Dat45.csv','Dat[0-9]*')


Out[5]:
True

In [6]:
names = ['Dat1.csv','Dat2.csv','Dat3.csv','config.ini','foo.py']
[name for name in names if fnmatch(name,'Dat*.csv')]


Out[6]:
['Dat1.csv', 'Dat2.csv', 'Dat3.csv']

fnmatch() Func 使用底层操作系统 の 大小写敏感规则 (不同 の 系统是不一样的)来匹配模式

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

IF 对这个区别很难受 可使用 fnmatchcase 来代替 其可完全按你的模式大小来写匹配


In [8]:
fnmatchcase('foo.txt','*.TXT')


Out[8]:
False

此两函数通常被忽略的一个特性即在除了非文件名的字符串其亦有用


In [9]:
address = [
    '5412 N CLARK ST',
    '1060 W ADDISON ST',
    '1039 W GRANVILLE AVE',
    '2122 N CLARK ST',
    '4802 N BROADWAY',
]
[addr for addr in address if fnmatchcase(addr,'* ST')]


Out[9]:
['5412 N CLARK ST', '1060 W ADDISON ST', '2122 N CLARK ST']

In [11]:
[addr for addr in address if fnmatchcase(addr,'54[0-9][0-9] *CLARK*')]


Out[11]:
['5412 N CLARK ST']

fnmatch Func can 介于简单 の string AND 强大 正则表达式 之间 IF 在数据处理操作中只需简单的通配符就可完成的时候
IF Code need 做 文件名 の 匹配 最好使用 glob module

2.4 字符串匹配 AND 搜索

匹配 OR 搜索 指定模式 の 文本

IF 匹配的是 字面字符串 则 需要调用基本字符串方法 str.find() AND str,endswith() str.startswith() OR 类似方法


In [1]:
text = 'yeah, but no,but yeah,but no,but yeah'
# Exact match
b0 = text == 'yeah'
# Match at start or end
b1 = text.startswith('yeah')
b2 = text.endswith('yeah')
# Search for the location of the first occurrence
b3 = text.find('no')
boolean = [b0,b1,b2,b3]
print(boolean)


[False, True, True, 10]

对于复杂 の 匹配需要使用 正则表达式 AND re module
为解释 Regular Expression の 基本原理


In [2]:
text1 = '11/27/2013'
text2 = 'Nov 27,2012'
import re
# Simple matching: \d+ means match one or more digits
for text in (text1,text2):
	if re.match(r'\d+/\d+/\d+',text):
		print('Match yes!')
	else:
		print('Match no!')


Match yes!
Match no!

IF 想要使用 同一模式去做多次匹配 YOU Should 将模式 String 预编译为一个模式对象


In [2]:
import re
text1 = '11/27/2013'
text2 = 'Nov 27,2012'
datepat = re.compile(r'\d+/\d+/\d+')
for text in [text1,text2]:
	if datepat.match(text):
		print('yes')
	else:
		print('no')


yes
no

match 总是以字符串开始匹配 IF想去查找字符串任意部分的模式出现位置 使用 findall() 取代


In [3]:
text = 'Today is 11/27/2012,pycon starts 3/12/2013'
datepat.findall(text)


Out[3]:
['11/27/2012', '3/12/2013']

在定义 Regular Expression 通常用括号来捕获分组


In [5]:
datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
m = datepat.match('11/27/1009')
m


Out[5]:
<_sre.SRE_Match object; span=(0, 10), match='11/27/1009'>

In [6]:
m.group(0)


Out[6]:
'11/27/1009'

In [7]:
m.group(1) + m.group(2) + m.group(3)


Out[7]:
'11271009'

In [11]:
from functools import reduce
def addd(x,y):
    return x + y
reduce(addd,m.groups())


Out[11]:
'11271009'

In [12]:
month,day,year = m.groups()

In [13]:
month + day + year


Out[13]:
'11271009'

In [14]:
datepat.findall(text)


Out[14]:
[('11', '27', '2012'), ('3', '12', '2013')]

In [15]:
for month,day,year in datepat.findall(text):
	print('{}-{}-{}'.format(year,month,year))


2012-11-2012
2013-3-2013

findall 会搜索文本同时以列表形式 返回所有匹配 IF以迭代形式 返回匹配 可使用 finditer() 来代替


In [16]:
for m in datepat.finditer(text):
	print(m.groups())


('11', '27', '2012')
('3', '12', '2013')

使用 re 模块 进行 匹配 AND 搜索 文本 最基本方法 核心:

  1. 先使用 re.compile() 进行编译 Regular Expression string
  2. 后使用 match() OR findall() OR finditer() 等方法

在使用 re 模块 时 一定要对 string 前 + r


In [17]:
m = datepat.match('11/27/2013abcd')
m.groups()


Out[17]:
('11', '27', '2013')

精确匹配 以 $ 结尾


In [18]:
datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
datepat.match('11/27/2013abc') == datepat.match('11/27/2013')


Out[18]:
False

IF 仅仅 做一次 简单 文本匹配 OR 搜索 可略过 编译 compile 过程 直接调用 re 模块级别 Func


In [19]:
re.findall(r'(\d+)/(\d+)/(\d+)',text)


Out[19]:
[('11', '27', '2012'), ('3', '12', '2013')]

但是需要注意的是,如果你打算做大量的匹配和搜索操作的话,最好先编译正则表达式,然后再重复使用它。 模块级别的函数会将最近编译过的模式缓存起来,因此并不会消耗太多的性能, 但是如果使用预编译模式的话,你将会减少查找和一些额外的处理损耗。

2.5 字符串搜索 AND 替换

在字符串中搜索和匹配指定的文本的模式

对于简单的字面模式 可用 str.relace()


In [2]:
text = 'yeah,but nol,but yeah,byt no,but yeah'
text.replace('yeah','yep')


Out[2]:
'yep,but nol,but yep,byt no,but yep'

对于复杂的模式,可使用 re 模块中 sub() 函数 说明将 形式
11/27/2012 --> 2012-11-27


In [3]:
text = 'Today is 11/27/2012,pycon starts 4/12/2021'
import re
re.sub(r'(\d+)/(\d+)/(\d+)',r'\3-\1-\2',text)


Out[3]:
'Today is 2012-11-27,pycon starts 2021-4-12'

sub Func first argv 被匹配的模式 second argv 替换模式
反斜杠数字比如 \3 指向前面模式 の 捕获组号
若打算以相同的模式做多次替换 考虑先编译其将提升性能


In [ ]:
datept = re.compile(r'(\d+)/(\d+)/(\d+)')
datept