Skip to content

Latest commit

 

History

History
227 lines (170 loc) · 6.84 KB

basic5.md

File metadata and controls

227 lines (170 loc) · 6.84 KB

Python的基础故事(五)——操作字符串

本篇文章仅仅带来Python中字符串的一些基础性操作。后续会开启字符串系列专门分享Python中的字符串、字节、编码、正则表达式等更复杂的内容。

字符串基础

我们知道,在Python中,字符串是由单引号或双引号引起来的字符的集合。Python还支持三个单引号来引用长字符串:

a = 'hello'
b = "world"
c = '''
This
is 
a
long
string
'''

如果需要在字符串中使用单双引号,一种方式是包括的符号采用另一种防止冲突,另一种方式是采用转义字符:

a = 'he"ll"o'
b = "wor'ld"
c = 'hel\'lo'
d = "wor\"ld"
print(a, b, c, d)
# he"ll"o wor'ld hel'lo wor"ld

Python中没有字符的概念,即使只有一个字符,它也是一个字符串。

字符串可以像其他容器类型那样通过下标来索引,也支持切片lenin等操作,但需要注意的是,字符串是不可变对象

a = 'hello'
print(a[0])
# h
print(a[:3])
# hel
print(len(a))
# 5
print('he' in a)
# True
a[4] = 'l'
# TypeError: 'str' object does not support item assignment

转义字符:

和大多数语言一样,Python使用反斜线\来转义字符,例如\n代表换行,\t代表横向制表符,\\代表一个反斜线本身等等:

a = 'hell\no'
print(a)
# hell
# o
b = 'hel\\lo'
print(b)
# hel\lo

此外,\还可以作为续行符使用:

a = 'hel\
lo'
print(a)
# hello

字符串操作

字符串可以直接利用for...in...遍历:

a = 'hello'
for s in a:
    print(s)
    
# h
# e
# l
# l
# o

字符串拼接可以直接利用+号:

a = 'hello'
b = 'world'
print(a + b)
# helloworld

另一种连接的方式是利用join方法,它需要一个字符串列表作为参数,将每个元素由调用字符串连接起来:

a = ['a', 'b', 'c']
b = ':'.join(a)
print(b)
# a:b:c

当然,如果对空字符串调用join则效果同+号一致:

b = ''.join(a)
print(b)
# abc
print('a' + 'b' + 'c')
# abc

**注意:**我们在做字符串操作时,尽量采用join的形式。因为join在效率上要远高于++操作每一次都会分配一个新的空间来存储两个字符串,而join一次性计算出需要的空间,只会做一次内存分配,所以需要连接的字符串数量越多,join的性能优势越明显。

字符串拆分:

我们可以利用split方法来拆分一个字符串成为一个字符串列表。split方法是join的逆方法,唯一的不同点是split不接受一个空字符串来分割

b = 'a:b:c'
a = b.split(':')
print(a)
# ['a', 'b', 'c']

b = 'abc'
a = b.split('')
# ValueError: empty separator

如果想要通过空字符串来分割一个字符串,可以有如下几种方式:

# 利用list
b = 'abc'
a = list(b)
print(a)
# ['a', 'b', 'c']

# 利用推导式
a = [s for s in b]
print(a)
# ['a', 'b', 'c']

更多的字符串操作方法,请查阅官方文档,地址:

https://docs.python.org/3.7/library/stdtypes.html#string-methods

原始字符串

试想一下,当我们需要写一些字符串来说明一些转义字符的意思时,我们需要以原始模样来呈现一个转义字符。例如,我们写一个字符串说明\n的意义:

a = '\n是一个转义字符'

这里\n会被转义为换行符。所以我们需要在\n前面再加一个\来把\n的反斜线转义为普通反斜线:

print(a) #直接打印
#
# 是一个转义字符
a = '\\n是一个转义字符'
print(a)
# \n是一个转义字符

试想当一个字符串中包含大量的反斜线需要转义时,上述方式会增加多少工作量。另外一个巨大的不可避免的问题来自于正则表达式。接触过正则表达式的朋友一定记得正则表达式中包含大量的反斜线来表明一些形式的字符。这些反斜线的出现极大得加大了反斜线转义的复杂度。例如,当我们想在正则表达式中匹配到反斜线时,我们需要这么写匹配模式:'\\\\'。因为我们想要匹配一个反斜线,而一个反斜线在Python字符串里通常是\\的形式,匹配两个\\的字符串自然是'\\\\'

import re
target = '我想匹配\\'
p = '\\\\'
m = re.search(p, target)
print(m.group(0))
# \

为了避免这些事的发生,Python给出了原始字符串的解决方式。所谓原始字符串,顾名思义,这个字符串不论有多少转义字符都不进行转义,保留了其本来的面目。这些字符串以标志位r开始:

a = r'\t\n\n\\\\'
print(a)
# '\t\n\n\\\\'

原始字符串也是字符串,它的基本原理是产生一个新的转义字符串来表示原字符串本身:

print(type(a))
# <class 'str'>
print(a == '\\t\\n\\n\\\\\\\\')
# True

通过上面的例子我们发现,r'\t\n\n\\\\'实际上最后生成了所有反斜线都被转义的一个普通字符串。原始字符串让解释器帮你把转义这件事搞定了,所以从用户角度讲更加清晰了许多,所谓所见即所得。我们再来看看他们的长度:

print(len(r'\n'))
print(len('\n'))

如果你理解上面的内容,你会知道上面两个输出是21。因为r'\n'被作为两个独立的字符\n对待。

下面来看另一个问题:

a = r'\'

思考一下这个的结果是什么?

答案是报错。为什么会这样呢?

这个问题的解释来自于Python设计者对于Python的一个特殊设计,即Python的字符串不允许以奇数个反斜线为结尾。原理上说,原始字符串r'\'应当生成'\\'。这样一种机制的设计的目的在于:

  1. 简化Python解释器的词法分析功能。解释器如何区别一个字符串?按照定义,从一个单引号或双引号起,直至另一个单引号或双引号截止。依照这个方式,我们发现对于r'\'来说,解释器无法判断第二个单引号就是字符串结束的标志(因为它对于解释器来说被反斜线转义了)。所以为了简化词法分析,这种写法直接被禁止掉了,而不是专为此修改词法分析器;
  2. 简化Python语法高亮引擎。很多编辑器对于Python语法高亮都是利用正则表达式做的。同样的道理,正则式在遇到r'\'这样形式的字符串(只存在于Python中)就慌了神了。它不能做正确匹配进而做正确的高亮。

参考文献:

https://stackoverflow.com/questions/9993390/python-literal-r-not-accepted