1、字符串对象方法
对于大部分字符串处理应用而言,内置的字符串方法已经能够满足要求了。
(1)split 以逗号分隔的字符串可以用split拆分成数段
val = 'a,b,guido'
val.split(',')
Out[3]: ['a', 'b', 'guido']
(2)strip split结合strip(用于修剪空白符(包括换行符))一起使用:
pieces = [x.strip() for x in val.split(',')]
pieces
Out[5]: ['a', 'b', 'guido']
(3)加法 连接子字符串
first + '//'+second+'//'+third
Out[8]: 'a//b//guido'
向字符串“//”的join方法传入一个列表或元组:
'::'.join(pieces)
Out[9]: 'a::b::guido'
(4)子串定位 检测子串的最佳方式是利用Python的in关键字,也可以使用index和find
val.index(',')
Out[11]: 1
val.find(',')
Out[12]: 1
val.find(':')
Out[13]: -1
注意:find和index的区别:如果找不到字符串,index将会引发一个异常(而不是返回-1)
(5)count函数 返回指定子串的出现次数
val.count(',')
Out[15]: 2
(6)replace 用于将指定模式替换为另一个模式,也常常用于删除模式:传入空字符串
val.replace(',','::')
Out[16]: 'a::b::guido'
val.replace(',','')
Out[17]: 'abguido'
方法 | 说明 |
count | 返回子串在字符串中的出现次数(非重叠) |
endswith、startswith | 如果字符串以某个后缀结尾(以某个前缀开头),则返回True |
join | 将字符串用作连接其他字符串序列的分隔符 |
index | 如果在字符串中找到子串,则返回子串第一个字符所在的位置。如果没有找到,则引发ValueError |
find | 如果在字符串中找到子串,则返回第一个发现的子串的第一个字符所在的位置。如果没有找到,则返回-1 |
rfind | 如果在字符串中找到子串,则返回最后一个发现的子串的第一个字符所在的位置。如果没有找到,则返回-1 |
replace | 用另一个字符串替换指定子串 |
strip、rstrip、lstrip | 去除空白符(包括换行符),相当于对各个元素执行x.strip()(以及rstrip、lstrip) |
split | 通过指定的分隔符将字符串拆分为一组子串 |
lower、upper | 分别将字母字符转换为大写或小写 |
ljust、rjust | 用空格(或其他字符)填充字符串的空白侧以返回符合最低宽度的字符串 |
2、正则表达式
re模块的函数可以分为三个大类:模式匹配、替换以及拆分。
一个regex描述了需要在文本中定位的一个模式,假设我想要拆分一个字符串,分隔符为数量不定的一组空白符(制表符、空格、换行符),描述一个或多个空白符的regex是\s+:
调用re.split('\s+',text)时,正则表达式会先被编译,然后再在text上调用其split方法。可以用re.compile自己编译regex以得到一个可重用的regex对象:如果打算对许多字符串应用同一条正则表达式,强烈建议通过re.compile创建regex对象。
regex = re.compile('\s+')
regex.split(text)
①如果只希望得到匹配regex的所有模式,则可以使用findall方法:
regex.findall(text)
Out[22]: [' ', '\t ', ' \t']
注意:如果想避免正则表达式中不需要的转义(\),则可以使用原始字符串字面量如r'C:\x'(也可以编写其等价式'C:\\x')
②match和search跟findall功能类似
findall返回的是字符串中所有的匹配项,而search则只返回第一个匹配项。match更加严格,只匹配字符串的首部。
例子:假设有一段文本以及一条能够识别大部分电子邮件地址的正则表达式。
import re
text="""Dave dave@google.com
Steve steve@gmail.com
Rob rob@gmail.com
Ryan ryan@yahoo.com
"""
pattern = r'[A-Z0-9.-]+@[A-Z0=9.-]+\.[A-Z]{2,4}'
regex = re.compile(pattern,flags=re.IGNORECASE)#re.IGNORECASE的作用是使正则表达式对大小写不敏感
regex.findall(text)#对text使用findall将得到一组电子邮件地址
Out[32]: ['dave@google.com', 'steve@gmail.com', 'rob@gmail.com', 'ryan@yahoo.com']
search返回的是文本中第一个电子邮件地址(以特殊的匹配项对象形式返回),对于上面那个regex,匹配项对象只能告诉我们模式在原字符串中的起始和结束位置:
m = regex.search(text)
m
Out[34]: <_sre.SRE_Match object; span=(5, 20), match='dave@google.com'>
text[m.start():m.end()]
Out[35]: 'dave@google.com'
regex.match则将返回None,因为它只匹配出现在字符串开头的模式:
print(regex.match(text))
None
另外还有一个sub方法,它会将匹配到的模式替换为指定字符串,并返回所得到的新字符串:
print(regex.sub('REDACTED',text))
Dave REDACTED
Steve REDACTED
Rob REDACTED
Ryan REDACTED
假设不仅找出电子邮件地址,还想将各个地址分为3个部分:用户名、域名以及域后缀。要想实现此功能,只需将待分段的模式的各部分用括号包起来即可:
pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9._]+)\.([A-Z]{2,4})'
regex = re.compile(pattern,flags=re.IGNORECASE)
由这种正则表达式所产生的匹配项对象,可以通过其groups方法返回一个由模式各段组成的元组:
m = regex.match('wesm@bright.net')
m.groups()
Out[5]: ('wesm', 'bright', 'net')
对于带有分组功能的模式,findall会返回一个元组列表:
regex.findall(text)
Out[6]:
[('dave', 'google', 'com'),
('steve', 'gmail', 'com'),
('rob', 'gmail', 'com'),
('ryan', 'yahoo', 'com')]
sub还能通过诸如\1、\2之类的特殊符号访问各匹配项中的分组:
print(regex.sub(r'Username:\1,Domain:\2,Suffix:\3',text))
Dave Username:dave,Domain:google,Suffix:com
Steve Username:steve,Domain:gmail,Suffix:com
Rob Username:rob,Domain:gmail,Suffix:com
Ryan Username:ryan,Domain:yahoo,Suffix:com
由这种正则表达式所产生的匹配项对象可以得到一个简单易用的带有分组名称的字典:
m = regex.match('wesm@bright.net')
m.groupdict()
Out[10]: {'username': 'wesm', 'domain': 'bright', 'suffix': 'net'}
方法 | 说明 |
findall、finditer | 返回字符串中所有的非重叠匹配模式。findall返回的是由所有模式组成的列表,而finditer则通过一个迭代器逐个返回 |
match | 从字符串起始位置匹配模式,还可以对模式各部分进行分组。如果匹配到模式,则返回一个匹配项对象,否则返回None |
search | 扫描整个字符串以匹配模式。如果找到则返回一个匹配项对象。跟match不同,其匹配项可以位于字符串的任意位置,而不仅仅是起始处 |
split | 根据找到的模式将字符串拆分为数段 |
sub、subn | 将字符串中所有的(sub)或前n个(subn)模式替换为指定表达式。在替换字符串中可以通过\1、\2的符号表示各项分组项 |
3、pandas中矢量化的字符串函数
清理待分析的散乱数据时,常常需要做一些字符串规整化工作。更为复杂的情况是,含有字符串的列有时还含有缺失数据:
data = {'Dave':'dave@google.com','Steve':'steve@gmail.com','Rob':'rob@gmail.com','Wes':np.nan}
data = pd.Series(data)
data
Out[20]:
Dave dave@google.com
Steve steve@gmail.com
Rob rob@gmail.com
Wes NaN
dtype: object
data.isnull()
Out[21]:
Dave False
Steve False
Rob False
Wes True
dtype: bool
(1)Series中有一些能够跳过NA值得字符串操作方法
通过Series的str属性即可访问这些方法。例如通过str.contains检查各个电子邮件地址是否含有“gmail”:
data.str.contains('gmail')
Out[23]:
Dave False
Steve True
Rob True
Wes NaN
dtype: object
这里也可以使用正则表达式,还可以加上任意re选项(如IGNORECASE):
pattern
Out[24]: '([A-Z0-9._%+-]+)@([A-Z0-9._]+)\\.([A-Z]{2,4})'
data.str.findall(pattern,flags=re.IGNORECASE)
Out[25]:
Dave [(dave, google, com)]
Steve [(steve, gmail, com)]
Rob [(rob, gmail, com)]
Wes NaN
dtype: object
(2)实现矢量化的元素获取操作
①str.get ②在str属性上使用索引
方法 | 说明 |
cat | 实现元素级的字符串连接操作,可指定分隔符 |
contains | 返回表示各字符串是否含有指定模式的布尔型数组 |
count | 模式的出现次数 |
endswith、startswith | 相当于对各个元素执行w.endswith(pattern)或x.startswith(pattern) |
findall | 计算各字符串的模式列表 |
get | 获取各元素的第i个字符 |
join | 根据指定的分隔符将Series中各元素的字符串连接起来 |
len | 计算各字符串的长度 |
lower、upper | 转换大小写。相当于对各个元素执行x.lower()或x.upper() |
match | 根据指定的正则表达式对各个元素执行re.match |
pad | 在字符串的左边、右边或左右两边添加空白符 |
center | 相当于pad(side='both') |
repeat | 重复值。例如,s.str.repeat(3)相当于对各个字符串执行x*3 |
replace | 用指定字符串替换找到的模式 |
slice | 对Series中的各个字符串进行子串截取 |
split | 根据分隔符或正则表达式对字符串进行拆分 |
strip、rstrip、lstrip | 去除空白符,包括换行符。相当于对各个元素执行x.strip()、x.rstrip()、x.lstrip() |