分割字符串python
根据某个分割符分割git
>>> a = '1,2,3,4' >>> a.split(',') ['1', '2', '3', '4']
根据多个分隔符分割正则表达式
>>> line = 'asdf fjdk; afed, fjek,asdf, foo' >>> import re
>>> re.split(r'[;,\s]\s*', line)# 用 re 匹配分隔符,
['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
若是你在结果列表中保留这些分隔符,能够捕获分组:shell
>>> fields = re.split(r'(;|,|\s)\s*', line) >>> fields ['asdf', ' ', 'fjdk', ';', 'afed', ',', 'fjek', ',', 'asdf', ',', 'foo']
若是不保留这些分隔符,但想用分组正则表达式,可使用非捕获分组:api
>>> re.split(r'(?:,|;|\s)\s*', line) ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
匹配字符串开始或结束函数
检查字符串是否以 某字符开始或结束 可用 startswith() 和 endswith():this
>>> filename = 'spam.txt' >>> filename.endswith('.txt') True >>> filename.startswith('file:') False >>> url = 'http://www.python.org' >>> url.startswith('http:') True
若是你的检查有多种匹配的可能,能够传入一个包含匹配项的元组:url
>>> import os >>> filenames = os.listdir('.') >>> 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' >>> any(name.endswith('.py') for name in filenames) True
其余方式能够用切片 或 re 匹配:spa
>>> url = 'http://www.python.org' >>> url[:5] == 'http:' or url[:6] == 'https:' or url[:4] == 'ftp:' True
>>> import re >>> url = 'http://www.python.org' >>> re.match('http:|https:|ftp:', url) <_sre.SRE_Match object at 0x101253098>
使用shell通配符匹配字符串:操作系统
* | 匹配任意多个字符,包括 0 个 |
? | 匹配任意一个字符,必须有一个字符 |
[char] | 匹配括号中的任意一个字符 |
[!char] | 匹配任意一个不属于括号中的字符的字符 |
[:alnum:] | 匹配任意一个字母或者数字 |
[:alpha:] | 匹配任意一个字母 |
[:digit:] | 匹配任意一个数字 |
[:lower:] | 匹配任意一个小写字母 |
[:upper:] | 匹配任意一个大写字母 |
>>> 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() 来替代。它彻底使用你的模式进行匹配。好比:
>>> fnmatchcase('foo.txt', '*.TXT') False
>>> fnmatchcase('foo.txt', '*.txt')
True
这个函数在处理非文件名字符串中也很是有用:
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']
总结:fnmatch 的能力介于字符串方法和正则表达式之间,若是数据处理中只须要简单的通配符就能完成,fnmatch 或 fnmatchcase 会是个不错的选择。若是须要作文件名的匹配,最好使用 glob 模块。
字符串匹配和搜索
若是只是简单的字符串匹配,字符串方法足够使用了,例如:str.find() , str.startswith() , str.endswith() 。
对于复杂的匹配须要使用正则表达式和re模块:
>>> 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 >>>
re.match() 老是从字符串开始去匹配,若是匹配到,返回 Match 对象。若是没有匹配到,返回 None。
若是想重复使用同一个正则,能够将模式字符串编译为模式对象:
>>> 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
若是不想从字符串开始位置匹配,可使用 re.search() 或者 re.findall(),re.search() 在第一个匹配到的位置返回一个 Match 对象,若是没有匹配到,则返回 None 。
re.findall() 将匹配到的全部字符串装进列表中返回。
在使用正则时,若表达式中包含分组,re.findall() 返回一个包含 groups 的列表,groups 是一个包含匹配到的全部分组的元组。
>>> 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
findall() 会以列表的形式返回结果,若是你想用迭代的形式返回,可使用 finditer() :
>>> for m in datepat.finditer(text): ... print(m.groups()) ... ('11', '27', '2012') ('3', '13', '2013')
字符串的搜索和替换
对于简单的查找替换,可使用 str.replace():
>>> text = 'yeah, but no, but yeah, but no, but yeah' >>> text.replace('yeah', 'yep') 'yep, but no, but yep, but no, but yep'
对于复杂的查找替换,可使用 re.sub():
>>> 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.'
其中 \3 等指向匹配模式中的分组
对于更加复杂的替换,能够传递一个回调函数:
>>> from calendar import month_abbr >>> def change_date(m): ... mon_name = month_abbr[int(m.group(1))] ... return '{} {} {}'.format(m.group(2), mon_name, m.group(3)) ... >>> datepat.sub(change_date, text) 'Today is 27 Nov 2012. PyCon starts 13 Mar 2013.'
出了替换后的结果之外,若是你还想知道替换了多少个,可使用 re.subn() 来代替:
>>> newtext, n = datepat.subn(r'\3-\1-\2', text) >>> newtext 'Today is 2012-11-27. PyCon starts 2013-3-13.' >>> n 2
若是想在匹配的时候,忽略大小写,能够给 re 提供一个标志参数,re.IGNORECASE:
>>> text = 'UPPER PYTHON, lower python, Mixed Python' >>> re.findall('python', text, flags=re.IGNORECASE) ['PYTHON', 'python', 'Python'] >>> re.sub('python', 'snake', text, flags=re.IGNORECASE) 'UPPER snake, lower snake, Mixed snake'
这个例子有一个小缺陷,替换字符串不会和匹配字符串的大小写保持一致,能够作以下修改:
def matchcase(word): def replace(m): text = m.group() if text.isupper(): return word.upper() elif text.islower(): return word.lower() elif text[0].isupper(): return word.capitalize() else: return word return replace
>>> re.sub('python', matchcase('snake'), text, flags=re.IGNORECASE) 'UPPER SNAKE, lower snake, Mixed Snake'
re 匹配的结果可能并不许确,例:
>>> str_pat = re.compile(r'\"(.*)\"') >>> text1 = 'Computer says "no."' >>> str_pat.findall(text1) ['no.'] >>> text2 = 'Computer says "no." Phone says "yes."' >>> str_pat.findall(text2) ['no." Phone says "yes.']
咱们想要的结果是 [ 'no.', 'yes.' ],但很显然,结果不正确。这是由于 * 操做符是贪婪的,会尽量多的匹配内容。若是想要精准的匹配到 "" 中的内容,能够这样:
>>> str_pat = re.compile(r'\"(.*?)\"') >>> str_pat.findall(text2) ['no.', 'yes.']
在 .* 后面加上 ? 的做用是改变 re 的匹配模式为 非贪婪模式,就是尽量少的匹配内容。
re 实现多行匹配模式
. 去匹配任意字符的时候,没法匹配 换行符(\n),例:
>>> comment = re.compile(r'/\*(.*?)\*/') >>> text1 = '/* this is a comment */' >>> text2 = '''/* this is a ... multiline comment */ ... ''' >>> >>> comment.findall(text1) [' this is a comment '] >>> comment.findall(text2) []
对此,你能够修改模式字符串,以增长对换行符的匹配支持:
>>> comment = re.compile(r'/\*((?:.|\n)*?)\*/') >>> comment.findall(text2) [' this is a\n multiline comment ']
‘ ?: ’ 的做用是指定这个分组是非捕获分组(也就是它定义了一个仅仅用来作匹配,而不能经过单独捕获或者编号的组)
除此以外,也可使用标记参数,使 . 能匹配到换行符:
>>> comment = re.compile(r'/\*(.*?)\*/', re.DOTALL) >>> comment.findall(text2) [' this is a\n multiline comment ']
简单状况下,re.DATALL 能很好的工做,但若是匹配模式很复杂,它极可能出现问题。因此,最好仍是定义本身的正则表达式模式。这里只是提供一种额外的选择。
删除字符串中不须要的字符
可使用 str.strip()、str.lstrip()、str.rstrip():
>>> # Whitespace stripping >>> s = ' hello world \n' >>> s.strip() 'hello world' >>> s.lstrip() 'hello world \n' >>> s.rstrip() ' hello world' >>> >>> # Character stripping >>> t = '-----hello=====' >>> t.lstrip('-') 'hello=====' >>> t.strip('-=') 'hello'
这些操做不会去除字符串中间的字符,若是想这么作,可使用 str.replace() 代替。
将 Unicode 文本标准化
在 unicode 中,某些字符能够有多种合法表示:
>>> s1 = 'Spicy Jalape\u00f1o' >>> s2 = 'Spicy Jalapen\u0303o' >>> s1 'Spicy Jalapeño' >>> s2 'Spicy Jalapeño' >>> s1 == s2 False >>> len(s1) 14 >>> len(s2) 15
文本 ‘Spicy Jalapeño’ 使用了两种形式表示,一种是总体字符 ‘ñ’(U+00F1)。一种是组合字符, n 后面跟一个 ‘~’ (U+3030)。
在比较字符串时,若是出现这种状况就麻烦了。解决办法是使用 unicodedata 模块将文本标准化:
>>> import unicodedata >>> t1 = unicodedata.normalize('NFC', s1) >>> t2 = unicodedata.normalize('NFC', s2) >>> t1 == t2 True >>> print(ascii(t1)) 'Spicy Jalape\xf1o' >>> t3 = unicodedata.normalize('NFD', s1) >>> t4 = unicodedata.normalize('NFD', s2) >>> t3 == t4 True >>> print(ascii(t3)) 'Spicy Jalapen\u0303o'
其中 ‘NFC’ 和 ‘NFD’ 是字符串标准化的方式。‘NFC’表示字符应该是总体组成,‘NFD’表示字符应该被分解为多个组合字符。
python 一样支持扩展的标准化形式 NFKC 和 NFKD ,它们在处理某些字符串时增长了一些额外的特性:
>>> s = '\ufb01' # A single character >>> s ' ' >>> unicodedata.normalize('NFD', s) ' ' # Notice how the combined letters are broken apart here >>> unicodedata.normalize('NFKD', s) 'fi' >>> unicodedata.normalize('NFKC', s) 'fi'