2018.04.15 已经用python一段时间了,最近遇到几个与字符串相关的问题,虽然最后都解决了,但很不pythonic,所以决定回补字符串相关基础知识。
参考资料:
python3中文版参考-第二章:字符串和文本
小甲鱼 字符串:格式化 – 零基础入门学习Python015
先看基础——正则表达
参考:python正则表达式系列(1)——正则元字符
why?因为字符串的处理除了用到python的str方法之外,很多时候还需要用到re正则模块中更加强大、灵活的方法进行处理,而后者的重要基础就是正则。因此,想要灵活处理字符串问题,就必须先掌握正则基础。
正则元字符:. ^ $ * + ? { } [ ] \ | ( )
A 元字符之[ ]——指定字符集
正则表达式[]里的五个特殊字符
通常的字符集匹配:
- [abc]匹配:a或b或c
- [^a]匹配:匹配非a的一个字符;
- [a-zA-Z0-9]匹配:大小写英文字母和数字
- [^0-9]匹配:不包含0123456789的其他任意字符
然而,
① [ ] - \ ^五个字符在[ ]中都有着特殊意义,而其余的元字符.、*、+、|在[ ]中均不再保留任何特殊意义;
②因此,若要在[]中匹配[ ]-\^五个元字符一定要添加反义字符 \
- [\^a\-bc]匹配:^和a和-和b和c共五个字符组成的字符集
- [a+]匹配:a或+
- [*\-+]匹配:* - + 推荐!
- [*-+]这个表达式其实表示的是*的ASCII值到+的ASCII值的范围。不推荐!
- [+-*]将会报错,就像你不能写成[9-0]一样,因为+、*的ASCII值大小顺序反了。错误!
B 元字符之()——匹配 pattern 并保留匹配符号
正则表达式 - 元字符
(pattern): 匹配 pattern 并获取这一匹配;
(?:pattern): 匹配 pattern 但不获取匹配结果;
(?=pattern):正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。
(?!pattern):正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。
(?<=pattern):反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。
(?<!pattern):反向否定预查,与正向否定预查类似,只是方向相反。
C 元字符之{ }——匹配前一个字符或子表达式出现指定次数
例如:
- {0,}:0次或多次,相当于*
- {1,}:1次或多次,相当于+
- {0,1}:0次或1次,相当于?
- {m,n}:m次到n次(m <= n)
1 字符串分割:str.split()和re.split()
参考:
Python字符串分割方法总结
中文说明 2.1 使用多个界定符分割字符串
1.1 str.split()方法
S.split(sep=None, maxsplit=-1)
返回的是一个list
- sep为分隔符,默认以空白字符whitespace (空格,TAB和回车)为分隔符;
- maxsplit为最大分割次数,当指定最大分割次数maxsplit时,结果列表长度为maxsplit+1。;
- print(str.split._ doc _)查看相关说明
- 缺点:sep只能指定一个分隔符!!
1.2 re.split()方法
re.split(pattern, string, maxsplit=0, flags=0)
返回的是一个list
- pattern相当于sep的功能,但它是更加灵活的正则表达式;
- string为目标字符串;
- maxsplit为最大分割次数,当指定最大分割次数maxsplit时,结果列表长度为maxsplit+1;
- flags为标志,表示正则表达式用到的标志。
核心:可以用灵活的正则表达式作为分隔符
①用[ ]符号:表示字符集,无需再用或,也不加逗号。
import re
line = 'asdf fjdk; afed, fjek,asdf, foo'
re.split(r'[,;\s]\s*', line)
Out[5]: ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
②用()符号:保留分割符号
line = 'asdf fjdk; afed, fjek,asdf, foo'
fields = re.split(r'(;|,|\s)\s*', line)
Out[6]: ['asdf', ' ', 'fjdk', ';', 'afed', ',', 'fjek', ',', 'asdf', ',', 'foo']
2. 字符串开头、结尾匹配:str.startwith()、str.endwith()
经常用于文件名、扩展名的快速优雅匹配
>>> filename = 'spam.txt'
>>> filename.endswith('.txt')
True
>>> filename.startswith('file:')
False
返回的是True、False,经常用作if 的判断参数,将if嵌入for循环中进行列表快速筛选!
经典用法1:快速筛选有效数据生成list
>>> 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'
经典用法2:检查某个文件夹中是否存在指定的文件类型
if any(name.endswith(('.c', '.h')) for name in listdir(dirname)):
str.startwith()、str.endwith()的功能用切片比对的方法也能实现,但那样很不优雅!
3.用通配符匹配字符串:fnmatch() 和 fnmatchcase()
与前述的str.startwith()、str.endwith()是字符串精准匹配,而fnmatch() 和 fnmatchcase()可以用通配符进行更加灵活的匹配
from fnmatch import fnmatch, fnmatchcase
#导入fnmatch模块的相关方法
fnmatch('foo.txt', '*.txt')
经典用法:
import fnmatch
import os
for file in os.listdir('.'):
if fnmatch.fnmatch(file, '505996-*-2017.txt'):
print(file)
4.字符串匹配和搜索:str.find() , str.endswith() , str.startswith()和re.match()
str.find():返回的是字符串在str中第一次出现的位置(int)
text = 'yeah, but no, but yeah, but no, but yeah'
text.find('no') #返回的是字符串在str中第一次出现的位置(int)
10
re.match():
text1 = '11/27/2012'
if re.match(r'\d+/\d+/\d+', text1):
print('yes')
5.字符串中插入变量:{}和format()方法
>>> s = '{name} has {n} messages.'
#用{ }来定义变量
>>> s.format(name='Guido', n=37)
#用str.format(变量=vlaue)来给字符串中的变量赋值
out: 'Guido has 37 messages.'
应用场景:爬虫中多页的url地址,切换{num}url中的页码变量