一、字符串基础概述
1.1 字符串的定义与特性
不可变性(Immutability)原理
在 Python 中,字符串属于不可变对象,这意味着一旦字符串被创建,其内容就不能被修改。任何看似修改字符串的操作,实际上都会生成一个新的字符串对象。例如:
s = "hello"
s = s.upper() # 这里实际上创建了一个新字符串"HELLO",原字符串"hello"并未改变
这种不可变性带来了几个重要特性:
- 线程安全:在多线程环境下无需担心字符串被意外修改
- 哈希支持:字符串可以作为字典的键或集合的元素
- 内存优化:相同的字符串字面量在内存中只存储一份
但需要注意,频繁修改字符串会产生大量临时对象,影响性能。此时建议使用列表或io.StringIO
进行高效操作。
字符编码(ASCII/Unicode/UTF-8)基础
Python 字符串处理涉及三种核心编码概念:
- ASCII:最早的字符编码标准,使用 7 位二进制数表示 128 个字符,包括英文字母、数字和一些符号。无法表示非英语字符。
- Unicode:为了统一表示所有语言的字符,Unicode 应运而生,它为每个字符分配了唯一的码点(code point),范围从 0 到 0x10FFFF。
- UTF-8:是 Unicode 的一种可变长度编码方式,使用 1-4 个字节表示一个字符。英文字母用 1 个字节表示,中文通常用 3 个字节表示。
在 Python 中,字符串默认使用 Unicode 编码,而字节串(bytes)使用特定的编码方式(如 UTF-8)。可以通过encode()
和decode()
方法进行转换:
s = "你好"
b = s.encode('utf-8') # 将字符串编码为字节串
print(b) # 输出:b'\xe4\xbd\xa0\xe5\xa5\xbd'
s_decoded = b.decode('utf-8') # 将字节串解码为字符串
print(s_decoded) # 输出:你好
字符串与字节串(str vs bytes)的区别
特性 | 字符串(str) | 字节串(bytes) |
---|---|---|
表示方式 | 使用单引号、双引号或三引号 | 前缀加b ,如b'hello' |
编码方式 | Unicode(不可直接存储在磁盘或网络传输) | 具体编码(如 UTF-8、GBK) |
操作方法 | 丰富的字符串方法(如upper() 、split() ) |
部分方法与字符串类似,但更多涉及二进制操作 |
应用场景 | 文本处理、用户交互 | 文件读写、网络通信 |
1.2 字符串的创建方式
单引号 / 双引号 / 三引号创建
Python 提供了多种创建字符串的方式:
s1 = 'hello' # 单引号
s2 = "world" # 双引号
s3 = '''这是一个
多行字符串''' # 三引号(可用于创建多行字符串)
s4 = """这也是一个
多行字符串""" # 双三引号
当字符串中包含引号时,可以使用不同类型的引号来避免转义:
s1 = 'He said, "Hello!"'
s2 = "She replied, 'Hi!'"
转义字符(\n/\t/\ 等)的使用
转义字符用于表示一些特殊字符,例如换行符\n
、制表符\t
等:
s = "第一行\n第二行\t第三列"
print(s)
# 输出:
# 第一行
# 第二行 第三列
常见的转义字符包括:
\n
:换行符\t
:制表符\\
:反斜杠\'
:单引号\"
:双引号
原始字符串(r 前缀)的应用场景
原始字符串通过在字符串前加r
前缀来创建,其中的转义字符会被直接解释为普通字符,常用于正则表达式、文件路径等场景:
# 文件路径示例
path = r'C:\Users\Documents\file.txt'
print(path) # 输出:C:\Users\Documents\file.txt
# 正则表达式示例
pattern = r'\d+' # 匹配一个或多个数字
二、字符串索引与切片
2.1 索引操作
正向索引(从 0 开始)
Python 字符串的索引从 0 开始,从左到右依次递增:
s = "Python"
print(s[0]) # 输出:P
print(s[2]) # 输出:t
反向索引(从 - 1 开始)
除了正向索引,Python 还支持反向索引,从右到左依次递减,最后一个字符的索引为 - 1:
s = "Python"
print(s[-1]) # 输出:n
print(s[-3]) # 输出:h
索引越界异常(IndexError)处理
当使用的索引超出字符串的有效范围时,会引发IndexError
异常:
s = "Python"
# print(s[10]) # 会抛出IndexError异常
try:
print(s[10])
except IndexError:
print("索引超出范围")
2.2 切片操作
基本语法:[start:end:step]
切片操作可以从字符串中提取子串,语法为[start:end:step]
,其中:
start
:起始索引(包含),默认为 0end
:结束索引(不包含),默认为字符串长度step
:步长,默认为 1
s = "Python"
print(s[1:4]) # 输出:yth
print(s[:3]) # 输出:Pyt
print(s[3:]) # 输出:hon
print(s[::2]) # 输出:Pto
print(s[1:5:2]) # 输出:yh
省略参数的用法(start/end/step 默认值)
当省略start
时,默认从 0 开始;省略end
时,默认到字符串末尾;省略step
时,默认步长为 1:
s = "Python"
print(s[:]) # 输出:Python
print(s[::-1]) # 输出:nohtyP(字符串反转)
print(s[2::]) # 输出:thon
print(s[:4:]) # 输出:Pyth
字符串反转切片技巧([::-1])
通过设置步长为 - 1,可以实现字符串的反转:
s = "Python"
reversed_s = s[::-1]
print(reversed_s) # 输出:nohtyP
切片返回新字符串的特性(不修改原字符串)
切片操作不会修改原字符串,而是返回一个新的字符串:
s = "Python"
sub = s[1:4]
print(sub) # 输出:yth
print(s) # 输出:Python(原字符串未改变)
三、字符串拼接与重复
3.1 拼接操作
加号(+)拼接的性能问题
使用加号(+)可以拼接两个字符串:
s1 = "Hello"
s2 = "World"
s3 = s1 + " " + s2
print(s3) # 输出:Hello World
但在循环中频繁使用加号拼接字符串会导致性能问题,因为每次拼接都会创建一个新的字符串对象:
# 不推荐的写法(性能较差)
result = ""
for i in range(1000):
result += str(i)
join () 方法的高效拼接
对于大量字符串的拼接,使用join()
方法更为高效,因为它只需要创建一次字符串对象:
# 推荐的写法(性能较好)
lst = [str(i) for i in range(1000)]
result = ''.join(lst)
join()
方法的参数是一个可迭代对象(如列表、元组),它会将可迭代对象中的元素用指定的字符串连接起来:
words = ["Hello", "World", "!"]
s = ' '.join(words)
print(s) # 输出:Hello World !
f-string 拼接的最佳实践
Python 3.6 及以上版本支持 f-string(格式化字符串字面值),它提供了一种简洁且高效的字符串拼接方式:
name = "Alice"
age = 30
s = f"我的名字是{name},年龄是{age}"
print(s) # 输出:我的名字是Alice,年龄是30
f-string 还支持在花括号内使用表达式:
x = 10
y = 20
s = f"{x} + {y} = {x + y}"
print(s) # 输出:10 + 20 = 30
3.2 重复操作
乘号(*)的使用规则
使用乘号(*)可以重复字符串:
s = "abc"
print(s * 3) # 输出:abcabcabc
print("-" * 20) # 输出:--------------------
重复拼接的内存分配机制
字符串重复操作会一次性分配所需的内存空间,然后将原字符串复制到新的内存空间中。例如,"a" * 100
会直接分配 100