分割字符串

根据某个分割符分割

  1. >>> a = '1,2,3,4'
  2. >>> a.split(',')
  3. ['', '', '', '']

根据多个分隔符分割

  1. >>> line = 'asdf fjdk; afed, fjek,asdf, foo'
  2. >>> import re
    >>> re.split(r'[;,\s]\s*', line)# re 匹配分隔符,
    ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']

如果你在结果列表中保留这些分隔符,可以捕获分组:

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

如果不保留这些分隔符,但想用分组正则表达式,可以使用非捕获分组:

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

匹配字符串开始或结束

检查字符串是否以 某字符开始或结束 可用 startswith() 和 endswith():

  1. >>> filename = 'spam.txt'
  2. >>> filename.endswith('.txt')
  3. True
  4. >>> filename.startswith('file:')
  5. False
  6. >>> url = 'http://www.python.org'
  7. >>> url.startswith('http:')
  8. True

如果你的检查有多种匹配的可能,可以传入一个包含匹配项的元组:

  1. >>> import os
  2. >>> filenames = os.listdir('.')
  3. >>> filenames
  4. [ 'Makefile', 'foo.c', 'bar.py', 'spam.c', 'spam.h' ]
  5.  
  6. >>> [name for name in filenames if name.endswith(('.c', '.h')) ]
  7. ['foo.c', 'spam.c', 'spam.h'
  8. >>> any(name.endswith('.py') for name in filenames)
  9. True

其他方式可以用切片 或 re 匹配:

  1. >>> url = 'http://www.python.org'
  2. >>> url[:5] == 'http:' or url[:6] == 'https:' or url[:4] == 'ftp:'
  3. True
  1. >>> import re
  2. >>> url = 'http://www.python.org'
  3. >>> re.match('http:|https:|ftp:', url)
  4. <_sre.SRE_Match object at 0x101253098>

使用shell通配符匹配字符串:

 *   匹配任意多个字符,包括 0 个
 ?  匹配任意一个字符,必须有一个字符
 [char]   匹配括号中的任意一个字符
 [!char]  匹配任意一个不属于括号中的字符的字符
 [:alnum:]  匹配任意一个字母或者数字
 [:alpha:]  匹配任意一个字母
 [:digit:]  匹配任意一个数字
 [:lower:]  匹配任意一个小写字母
 [:upper:]  匹配任意一个大写字母
  1. >>> from fnmatch import fnmatch, fnmatchcase
  2. >>> fnmatch('foo.txt', '*.txt')
  3. True
  4. >>> fnmatch('foo.txt', '?oo.txt')
  5. True
  6. >>> fnmatch('Dat45.csv', 'Dat[0-9]*')
  7. True
  8. >>> names = ['Dat1.csv', 'Dat2.csv', 'config.ini', 'foo.py']
  9. >>> [name for name in names if fnmatch(name, 'Dat*.csv')]
  10. ['Dat1.csv', 'Dat2.csv']

fnmatch() 函数使用底层操作系统的大小写敏感规则(不同操作系统不一样)进行匹配:

  1. >>> # On OS X (Mac)
  2. >>> fnmatch('foo.txt', '*.TXT')
  3. False
  4. >>> # On Windows
  5. >>> fnmatch('foo.txt', '*.TXT')
  6. True

如果你对这个区别很在意,可以使用 fnmatchcase() 来替代。它完全使用你的模式进行匹配。比如:

  1. >>> fnmatchcase('foo.txt', '*.TXT')
  2. False

>>> fnmatchcase('foo.txt', '*.txt')
  True

  1.  

这个函数在处理非文件名字符串中也非常有用:

  1. addresses = [
  2. '5412 N CLARK ST',
  3. '1060 W ADDISON ST',
  4. '1039 W GRANVILLE AVE',
  5. '2122 N CLARK ST',
  6. '4802 N BROADWAY',
  7. ]
  1. >>> from fnmatch import fnmatchcase
  2. >>> [addr for addr in addresses if fnmatchcase(addr, '* ST')]
  3. ['5412 N CLARK ST', '1060 W ADDISON ST', '2122 N CLARK ST']
  4. >>> [addr for addr in addresses if fnmatchcase(addr, '54[0-9][0-9] *CLARK*')]
  5. ['5412 N CLARK ST']

总结:fnmatch 的能力介于字符串方法和正则表达式之间,如果数据处理中只需要简单的通配符就能完成,fnmatch 或 fnmatchcase 会是个不错的选择。如果需要做文件名的匹配,最好使用 glob 模块。

字符串匹配和搜索

如果只是简单的字符串匹配,字符串方法足够使用了,例如:str.find() , str.startswith() , str.endswith() 。

对于复杂的匹配需要使用正则表达式和re模块:

  1. >>> text1 = '11/27/2012'
  2. >>> text2 = 'Nov 27, 2012'
  3. >>>
  4. >>> import re
  5. >>> # Simple matching: \d+ means match one or more digits
  6. >>> if re.match(r'\d+/\d+/\d+', text1):
  7. ... print('yes')
  8. ... else:
  9. ... print('no')
  10. ...
  11. yes
  12. >>> if re.match(r'\d+/\d+/\d+', text2):
  13. ... print('yes')
  14. ... else:
  15. ... print('no')
  16. ...
  17. no
  18. >>>

re.match() 总是从字符串开始去匹配,如果匹配到,返回 Match 对象。如果没有匹配到,返回 None。

如果想重复使用同一个正则,可以将模式字符串编译为模式对象:

  1. >>> datepat = re.compile(r'\d+/\d+/\d+')
  2. >>> if datepat.match(text1):
  3. ... print('yes')
  4. ... else:
  5. ... print('no')
  6. ...
  7. yes
  8. >>> if datepat.match(text2):
  9. ... print('yes')
  10. ... else:
  11. ... print('no')
  12. ...
  13. no

如果不想从字符串开始位置匹配,可以使用 re.search() 或者 re.findall(),re.search() 在第一个匹配到的位置返回一个 Match 对象,如果没有匹配到,则返回 None 。

re.findall() 将匹配到的所有字符串装进列表中返回。

在使用正则时,若表达式中包含分组,re.findall() 返回一个包含 groups 的列表,groups 是一个包含匹配到的所有分组的元组。

  1. >>> m = datepat.match('11/27/2012')
  2. >>> m
  3. <_sre.SRE_Match object at 0x1005d2750>
  4. >>> # Extract the contents of each group
  5. >>> m.group(0)
  6. '11/27/2012'
  7. >>> m.group(1)
  8. ''
  9. >>> m.group(2)
  10. ''
  11. >>> m.group(3)
  12. ''
  13. >>> m.groups()
  14. ('', '', '')
  15. >>> month, day, year = m.groups()
  16. >>>
  17. >>> # Find all matches (notice splitting into tuples)
  18. >>> text
  19. 'Today is 11/27/2012. PyCon starts 3/13/2013.'
  20. >>> datepat.findall(text)
  21. [('', '', ''), ('', '', '')]
  22. >>> for month, day, year in datepat.findall(text):
  23. ... print('{}-{}-{}'.format(year, month, day))
  24. ...
  25. 2012-11-27
  26. 2013-3-13

findall() 会以列表的形式返回结果,如果你想用迭代的形式返回,可以使用 finditer() :

  1. >>> for m in datepat.finditer(text):
  2. ... print(m.groups())
  3. ...
  4. ('', '', '')
  5. ('', '', '')

字符串的搜索和替换

对于简单的查找替换,可以使用 str.replace():

  1. >>> text = 'yeah, but no, but yeah, but no, but yeah'
  2. >>> text.replace('yeah', 'yep')
  3. 'yep, but no, but yep, but no, but yep'

对于复杂的查找替换,可以使用 re.sub():

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

其中 \3 等指向匹配模式中的分组

对于更加复杂的替换,可以传递一个回调函数:

  1. >>> from calendar import month_abbr
  2. >>> def change_date(m):
  3. ... mon_name = month_abbr[int(m.group(1))]
  4. ... return '{} {} {}'.format(m.group(2), mon_name, m.group(3))
  5. ...
  6. >>> datepat.sub(change_date, text)
  7. 'Today is 27 Nov 2012. PyCon starts 13 Mar 2013.'

出了替换后的结果以外,如果你还想知道替换了多少个,可以使用 re.subn() 来代替:

  1. >>> newtext, n = datepat.subn(r'\3-\1-\2', text)
  2. >>> newtext
  3. 'Today is 2012-11-27. PyCon starts 2013-3-13.'
  4. >>> n
  5. 2

如果想在匹配的时候,忽略大小写,可以给 re 提供一个标志参数,re.IGNORECASE:

  1. >>> text = 'UPPER PYTHON, lower python, Mixed Python'
  2. >>> re.findall('python', text, flags=re.IGNORECASE)
  3. ['PYTHON', 'python', 'Python']
  4. >>> re.sub('python', 'snake', text, flags=re.IGNORECASE)
  5. 'UPPER snake, lower snake, Mixed snake'

这个例子有一个小缺陷,替换字符串不会和匹配字符串的大小写保持一致,可以做如下修改:

  1. def matchcase(word):
  2. def replace(m):
  3. text = m.group()
  4. if text.isupper():
  5. return word.upper()
  6. elif text.islower():
  7. return word.lower()
  8. elif text[0].isupper():
  9. return word.capitalize()
  10. else:
  11. return word
  12. return replace
  1. >>> re.sub('python', matchcase('snake'), text, flags=re.IGNORECASE)
  2. 'UPPER SNAKE, lower snake, Mixed Snake'

re 匹配的结果可能并不准确,例:

  1. >>> str_pat = re.compile(r'\"(.*)\"')
  2. >>> text1 = 'Computer says "no."'
  3. >>> str_pat.findall(text1)
  4. ['no.']
  5. >>> text2 = 'Computer says "no." Phone says "yes."'
  6. >>> str_pat.findall(text2)
  7. ['no." Phone says "yes.']

我们想要的结果是 [ 'no.', 'yes.' ],但很显然,结果不正确。这是因为 * 操作符是贪婪的,会尽可能多的匹配内容。如果想要精准的匹配到 "" 中的内容,可以这样:

  1. >>> str_pat = re.compile(r'\"(.*?)\"')
  2. >>> str_pat.findall(text2)
  3. ['no.', 'yes.']

在 .* 后面加上 ? 的作用是改变 re 的匹配模式为 非贪婪模式,就是尽可能少的匹配内容。

re 实现多行匹配模式

. 去匹配任意字符的时候,无法匹配 换行符(\n),例:

  1. >>> comment = re.compile(r'/\*(.*?)\*/')
  2. >>> text1 = '/* this is a comment */'
  3. >>> text2 = '''/* this is a
  4. ... multiline comment */
  5. ... '''
  6. >>>
  7. >>> comment.findall(text1)
  8. [' this is a comment ']
  9. >>> comment.findall(text2)
  10. []

对此,你可以修改模式字符串,以增加对换行符的匹配支持:

  1. >>> comment = re.compile(r'/\*((?:.|\n)*?)\*/')
  2. >>> comment.findall(text2)
  3. [' this is a\n multiline comment ']

‘ ?: ’ 的作用是指定这个分组是非捕获分组(也就是它定义了一个仅仅用来做匹配,而不能通过单独捕获或者编号的组)

除此之外,也可以使用标记参数,使 . 能匹配到换行符:

  1. >>> comment = re.compile(r'/\*(.*?)\*/', re.DOTALL)
  2. >>> comment.findall(text2)
  3. [' this is a\n multiline comment ']

简单情况下,re.DATALL 能很好的工作,但如果匹配模式很复杂,它很可能出现问题。所以,最好还是定义自己的正则表达式模式。这里只是提供一种额外的选择。

删除字符串中不需要的字符

可以使用 str.strip()、str.lstrip()、str.rstrip():

  1. >>> # Whitespace stripping
  2. >>> s = ' hello world \n'
  3. >>> s.strip()
  4. 'hello world'
  5. >>> s.lstrip()
  6. 'hello world \n'
  7. >>> s.rstrip()
  8. ' hello world'
  9. >>>
  10. >>> # Character stripping
  11. >>> t = '-----hello====='
  12. >>> t.lstrip('-')
  13. 'hello====='
  14. >>> t.strip('-=')
  15. 'hello'

这些操作不会去除字符串中间的字符,如果想这么做,可以使用 str.replace() 代替。

将 Unicode 文本标准化

在 unicode 中,某些字符可以有多种合法表示:

  1. >>> s1 = 'Spicy Jalape\u00f1o'
  2. >>> s2 = 'Spicy Jalapen\u0303o'
  3. >>> s1
  4. 'Spicy Jalapeño'
  5. >>> s2
  6. 'Spicy Jalapeño'
  7. >>> s1 == s2
  8. False
  9. >>> len(s1)
  10. 14
  11. >>> len(s2)
  12. 15

文本 ‘Spicy Jalapeño’ 使用了两种形式表示,一种是整体字符 ‘ñ’(U+00F1)。一种是组合字符, n 后面跟一个 ‘~’ (U+3030)。

在比较字符串时,如果出现这种情况就麻烦了。解决办法是使用 unicodedata 模块将文本标准化:

  1. >>> import unicodedata
  2. >>> t1 = unicodedata.normalize('NFC', s1)
  3. >>> t2 = unicodedata.normalize('NFC', s2)
  4. >>> t1 == t2
  5. True
  6. >>> print(ascii(t1))
  7. 'Spicy Jalape\xf1o'
  8. >>> t3 = unicodedata.normalize('NFD', s1)
  9. >>> t4 = unicodedata.normalize('NFD', s2)
  10. >>> t3 == t4
  11. True
  12. >>> print(ascii(t3))
  13. 'Spicy Jalapen\u0303o'

其中 ‘NFC’ 和 ‘NFD’ 是字符串标准化的方式。‘NFC’表示字符应该是整体组成,‘NFD’表示字符应该被分解为多个组合字符。

python 同样支持扩展的标准化形式 NFKC 和 NFKD ,它们在处理某些字符串时增加了一些额外的特性:

  1. >>> s = '\ufb01' # A single character
  2. >>> s
  3. ' '
  4. >>> unicodedata.normalize('NFD', s)
  5. ' '
  6. # Notice how the combined letters are broken apart here
  7. >>> unicodedata.normalize('NFKD', s)
  8. 'fi'
  9. >>> unicodedata.normalize('NFKC', s)
  10. 'fi'

python 之 字符串处理的更多相关文章

  1. Python格式化字符串~转

    Python格式化字符串 在编写程序的过程中,经常需要进行格式化输出,每次用每次查.干脆就在这里整理一下,以便索引. 格式化操作符(%) "%"是Python风格的字符串格式化操作 ...

  2. python学习--字符串

    python的字符串类型为str 定义字符串可以用 ‘abc' , "abc", '''abc''' 查看str的帮助 在python提示符里 help(str) python基于 ...

  3. Python格式化字符串和转义字符

    地址:http://blog.chinaunix.net/uid-20794157-id-3038417.html Python格式化字符串的替代符以及含义     符   号     说     明 ...

  4. [转载] python 计算字符串长度

    本文转载自: http://www.sharejs.com/codes/python/4843 python 计算字符串长度,一个中文算两个字符,先转换成utf8,然后通过计算utf8的长度和len函 ...

  5. Python基础-字符串格式化_百分号方式_format方式

    Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This ...

  6. python判断字符串

    python判断字符串 s为字符串s.isalnum() 所有字符都是数字或者字母s.isalpha() 所有字符都是字母s.isdigit() 所有字符都是数字s.islower() 所有字符都是小 ...

  7. Python格式化字符串

    在编写程序的过程中,经常需要进行格式化输出,每次用每次查.干脆就在这里整理一下,以便索引. 格式化操作符(%) "%"是Python风格的字符串格式化操作符,非常类似C语言里的pr ...

  8. python(七)字符串格式化、生成器与迭代器

    字符串格式化 Python的字符串格式化有两种方式:百分号方式.format方式 1.百分号的方式 %[(name)][flags][width].[precision]typecode (name) ...

  9. Python 的字符串格式化和颜色控制

    (部分内容源自武神博客和网络收集.) Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两 ...

  10. python反转字符串(简单方法)及简单的文件操作示例

    Python反转字符串的最简单方法是用切片: >>> a=' >>> print a[::-1] 654321 切片介绍:切片操作符中的第一个数(冒号之前)表示切片 ...

随机推荐

  1. 【转】ArcObject与ArcEngine的联系与区别

    在ArcGIS系列产品中,ArcGIS Desktop.ArcGIS Engine和ArcGIS Server都是基于核心组件库ArcObjects搭建的. 所谓ArcObjects,现在一般都是指A ...

  2. 网络请求NSLog结果不全

    碰到了两次 NSLog请求结果只有一部分 如果NSLog语句中没有汉语就可以全部输出了,不清楚原因. NSLog(@"%@",object);//这样结果是全的 NSLog(@&q ...

  3. iOS开发系列-打印内存地址

    打印内存地址 基本数据类型 定义一个基本数据类型,会根据变量类型分配对应的内存空间.比如定义一个int类型的变量a. int a = 10; 内存如下 输入变量a在内存中内存地址 NSLog(@&qu ...

  4. ctx.beginPath()开始新路径

    beginPath() 方法开始一条路径,或重置当前的路径. 提示:请使用这些方法来创建路径 moveTo().lineTo().quadricCurveTo().bezierCurveTo().ar ...

  5. iloc,loc,ix,df[]

    总结一. iloc可以把i当做第几个,所以是按行序号;其他的就清楚了. import pandas df = pandas.DataFrame({'a': [1, 2, 3, 4],'b': [5, ...

  6. a^a^a^a^a^a^a^a^a

    a^a^a^a是从前向后算,也就是a^(a^3)

  7. 组合数学——cf1065E

    从两端到中间分段,然后累乘即可 #include<bits/stdc++.h> using namespace std; #define mod 998244353 #define max ...

  8. LUOGU P1039 侦探推理 (字符串+模拟)

    传送门 解题思路 一道%你神题,\(string\)好强大啊..首先枚举一个周几,再枚举一个罪犯是谁,然后判断的时候就是枚举所有人说的话.定义\(fAKe[i]\)表示第\(i\)个人说的是真话还是假 ...

  9. [CTSC 2012]熟悉的文章

    二分+单调队列优化dp+后缀自动机 //CTSC2012 熟悉的文章 #include <bits/stdc++.h> using namespace std; const int max ...

  10. Linux课程---15、域名相关

    Linux课程---15.域名相关 一.总结 一句话总结: 先购买域名,再备案,再解析,域名即可使用 1.域名备案是怎么回事(比如二级域名,三级域名)? 每个二级域名需要备案一次,三级域名不需要备案, ...