字符串
# 字符串 (String)
字符串(String)是 Python 中最核心、最常用的数据类型之一。它是由零个或多个字符组成的不可变序列,用于存储和处理所有文本数据。本指南将用最详尽的方式,带你从零开始,深入探索字符串的每一个角落。
# 1. 核心特性:不可变的序列
在学习任何操作之前,必须掌握字符串最重要的特性:不可变性 (Immutability)。
- 定义:一个字符串一旦被创建,它的内容就永远不能被修改。
- 比喻:想象一块刻好字的石碑,你无法擦掉上面的字来修改它。如果你想改,只能拿一块新石碑,刻上新的内容。
- 影响:任何看似“修改”字符串的操作(如拼接、替换)实际上都会创建一个全新的字符串对象,而原字符串保持不变。
我们可以用 id()
函数(获取对象的内存地址)来证明这一点:
s = "Hello"
print(f"原始字符串: '{s}', 内存地址: {id(s)}") # 输出: 原始字符串: 'Hello', 内存地址: ...
# 拼接操作创建了一个新字符串
s = s + " World"
# s 现在指向了一个全新的对象
print(f"拼接后字符串: '{s}', 内存地址: {id(s)}") # 输出: 拼接后字符串: 'Hello World', 内存地址: ...(一个与上面不同的新地址)
2
3
4
5
6
7
开发者提示:理解字符串的不可变性,是理解 Python 内存管理和性能优化的关键第一步。
# 2. 创建字符串:多种方式与选择
Python 提供了多种灵活的方式来创建字符串。
# 2.1 引号的使用
单引号 (
'...'
) 和 双引号 ("..."
):功能完全相同。选择哪种通常取决于字符串内容,目的是为了避免使用转义字符。# 字符串内有双引号,就用单引号包围 text1 = 'He said, "Python is great!"' print(text1) # 输出: He said, "Python is great!" # 字符串内有单引号,就用双引号包围 text2 = "It's a beautiful day." print(text2) # 输出: It's a beautiful day.
1
2
3
4
5
6
7三引号 (
'''...'''
或"""..."""
):用于创建多行字符串。在三引号内的所有格式,包括换行和缩进,都会被完整保留。# 非常适合写 SQL 查询、代码文档(Docstrings)等 doc_string = """ 这是一个多行文档字符串。 它可以包含多行内容。 """ print(doc_string) # 输出: # 这是一个多行文档字符串。 # 它可以包含多行内容。
1
2
3
4
5
6
7
8
9
# 2.2 转义字符:表达特殊含义
反斜杠 \
是转义字符,用于在字符串中表示一些特殊含义的字符。
转义符 | 描述 | 示例 | 输出结果 |
---|---|---|---|
\n | 换行符 | print("Line1\nLine2") | Line1 Line2 |
\t | 水平制表符 | print("Name:\tAlice") | Name: Alice |
\\ | 反斜杠本身 | print("C:\\Users\\") | C:\Users\ |
\' | 单引号 | print('It\'s good.') | It's good. |
\" | 双引号 | print("Say \"Hi\".") | Say "Hi". |
# 2.3 原始字符串 (Raw String):忽略转义
在字符串前加上 r
或 R
,其中的所有字符都按其字面意思解释,转义字符会失效。
# 处理 Windows 文件路径时极其有用
# 错误方式: path = "C:\Users\new_folder" (这里的 \n 会被错误地解释为换行符)
correct_path = r"C:\Users\new_folder"
print(f"原始字符串路径: {correct_path}") # 输出: 原始字符串路径: C:\Users\new_folder
# 处理正则表达式时可以避免"斜杠地狱" (avoiding backslash plague)
import re
regex_pattern = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
2
3
4
5
6
7
8
# 3. 基础操作:拼接与重复
# 3.1 字符串拼接 (Concatenation)
# 使用 +
运算符
这是最直观的拼接方式,像数字加法一样连接字符串。
first_name = "张"
last_name = "三"
# 使用 + 连接多个字符串
full_name = first_name + " " + last_name
print(f"全名: {full_name}") # 输出: 全名: 张 三
# 新手陷阱:不同类型不能直接用 + 拼接
age = 25
# message = "年龄: " + age # 这会引发 TypeError,因为字符串不能和整数直接相加
# 正确方式:先用 str() 函数将其他类型转换为字符串
message = "年龄: " + str(age)
print(message) # 输出: 年龄: 25
2
3
4
5
6
7
8
9
10
11
12
13
# 使用 +=
进行累积拼接
这是一种简写形式,a += b
等同于 a = a + b
。
text = "学习: "
text += "Python" # text 现在是 "学习: Python"
text += ", Java" # text 现在是 "学习: Python, Java"
text += ", Go." # text 现在是 "学习: Python, Java, Go."
print(text) # 输出: 学习: Python, Java, Go.
2
3
4
5
性能警告:在循环中大量使用
+
或+=
拼接字符串效率很低。因为每次拼接都会创建一个新的字符串对象。在这种情况下,请务必使用后文将介绍的"".join()
方法。
# 3.2 字符串重复 (Repetition)
使用 *
运算符可以将一个字符串重复多次。
# 创建一条简单的分隔线
separator = "-" * 30
print(separator) # 输出: ------------------------------
# 创建一个醒目的标题
header = "* " * 5 + " 年度报告 " + "* " * 5
print(header) # 输出: * * * * * 年度报告 * * * * *
2
3
4
5
6
7
# 4. 访问与切片:精准定位字符
字符串是一个序列,意味着它的每个字符都有一个固定的位置(索引),我们可以通过这个位置来访问它。
# 4.1 索引 (Indexing):访问单个字符
- 正向索引:从
0
开始,s[0]
是第一个字符。计算机世界的计数习惯通常从0开始。 - 负向索引:从
-1
开始,s[-1]
是最后一个字符。这在获取末尾字符时非常方便。
图解:对于字符串 text = "Python"
字符 | P | y | t | h | o | n |
---|---|---|---|---|---|---|
正向索引 | 0 | 1 | 2 | 3 | 4 | 5 |
负向索引 | -6 | -5 | -4 | -3 | -2 | -1 |
text = "Python"
print(f"第一个字符 (text[0]): {text[0]}") # 输出: 第一个字符 (text[0]): P
print(f"最后一个字符 (text[-1]): {text[-1]}") # 输出: 最后一个字符 (text[-1]): n
print(f"倒数第二个字符 (text[-2]): {text[-2]}") # 输出: 倒数第二个字符 (text[-2]): o
# 新手陷阱:索引越界
# 试图访问一个不存在的位置会引发 IndexError
try:
print(text[10])
except IndexError as e:
print(f"错误: {e}") # 输出: 错误: string index out of range
2
3
4
5
6
7
8
9
10
11
# 4.2 切片 (Slicing):提取子字符串
切片是获取一部分字符串的强大工具,其完整语法为 [start:end:step]
。
start
: 起始索引(包含),如果省略,默认为 0。end
: 结束索引(不包含),如果省略,默认为字符串末尾。step
: 步长,即每隔多少个字符取一个,如果省略,默认为 1。
常用切片模式
text = "Hello, Python!"
# 模式1: [start:end] - 提取指定区间的子串
# 获取从索引 0 到索引 4 (5-1) 的部分
print(f"text[0:5] -> '{text[0:5]}'") # 输出: text[0:5] -> 'Hello'
# 模式2: [:end] - 从头开始提取到指定位置
# 获取从开头到索引 4 的部分
print(f"text[:5] -> '{text[:5]}'") # 输出: text[:5] -> 'Hello'
# 模式3: [start:] - 从指定位置提取到末尾
# 获取从索引 7 到末尾的部分
print(f"text[7:] -> '{text[7:]}'") # 输出: text[7:] -> 'Python!'
# 模式4: [:] - 复制整个字符串
print(f"text[:] -> '{text[:]}'") # 输出: text[:] -> 'Hello, Python!'
# 模式5: 使用负数索引
# 获取从倒数第7个字符到倒数第1个字符之前的部分
print(f"text[-7:-1] -> '{text[-7:-1]}'") # 输出: text[-7:-1] -> 'Python'
# 模式6: [start:end:step] - 指定步长
# 从索引 0 到 9,每隔 2 个字符取一个
print(f"text[0:10:2] -> '{text[0:10:2]}'") # 输出: text[0:10:2] -> 'Hlo,P'
# 模式7: [::-1] - 反转字符串
# 这是一个非常常用且简洁的技巧
print(f"text[::-1] -> '{text[::-1]}'") # 输出: text[::-1] -> '!nohtyP ,olleH'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
切片小贴士:切片操作非常宽容。如果
start
或end
越界,它不会报错,而是会优雅地处理到字符串的边界。
# 5. 常用字符串方法
字符串对象拥有大量内置方法,它们就像是字符串自带的“工具箱”。这些方法不会修改原始字符串(因为字符串是不可变的),而是返回一个新的、经过处理的字符串。
# 5.1 查找与定位
# 查找首次出现: find()
和 index()
这两个方法都用于查找子串第一次出现的位置。
find(sub)
: 如果找到,返回起始索引;如果找不到,安全地返回-1
。index(sub)
: 如果找到,返回起始索引;如果找不到,会抛出ValueError
异常。
text = "I love Python, Python is my favorite language."
# 使用 find()
print(f"find('Python'): {text.find('Python')}") # 输出: find('Python'): 7
print(f"find('Java'): {text.find('Java')}") # 输出: find('Java'): -1 (未找到)
# 使用 index()
print(f"index('Python'): {text.index('Python')}") # 输出: index('Python'): 7
try:
print(f"index('Java'): {text.index('Java')}") # 会在这里抛出异常
except ValueError:
print("使用 index() 未找到 'Java', 程序抛出 ValueError。") # 输出: 使用 index() 未找到 'Java', 程序抛出 ValueError。
# 在指定范围内查找
# 从索引 10 之后开始查找 'Python'
print(f"find('Python', 10): {text.find('Python', 10)}") # 输出: find('Python', 10): 15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
何时使用:
- 如果你不确定子串是否存在,或者找不到是正常情况,请使用
find()
。- 如果你认为子串必须存在,找不到就意味着程序出错了,请使用
index()
,它会通过异常来强制你处理这个错误。
# 统计出现次数: count()
text = "apple,apple,orange,apple"
print(f"'apple' 出现了 {text.count('apple')} 次") # 输出: 'apple' 出现了 3 次
print(f"'banana' 出现了 {text.count('banana')} 次") # 输出: 'banana' 出现了 0 次
2
3
# 检查开头与结尾: startswith()
和 endswith()
filename = "document.pdf"
print(f"文件名是否以 'doc' 开头? {filename.startswith('doc')}") # 输出: 文件名是否以 'doc' 开头? True
print(f"文件名是否以 '.pdf' 结尾? {filename.endswith('.pdf')}") # 输出: 文件名是否以 '.pdf' 结尾? True
# 也可以检查多种可能性 (提供一个元组)
url = "http://example.com"
print(f"URL是否为安全链接? {url.startswith(('https://', 'ftp://'))}") # 输出: URL是否为安全链接? False
2
3
4
5
6
7
# 5.2 修改与变换
# 替换子串: replace()
replace(old, new, [count])
返回一个新字符串,其中所有的 old
子串都被 new
替换。可选的 count
参数可以限制替换的次数。
text = "I like Java. Java is good."
# 替换所有 'Java'
new_text = text.replace("Java", "Python")
print(f"替换所有后: {new_text}") # 输出: 替换所有后: I like Python. Python is good.
# 只替换第1个 'Java'
one_replace = text.replace("Java", "Python", 1)
print(f"只替换1次: {one_replace}") # 输出: 只替换1次: I like Python. Java is good.
2
3
4
5
6
7
8
9
# 去除首尾字符: strip()
, lstrip()
, rstrip()
strip()
: 去除字符串两边的指定字符(默认为所有空白字符:空格、\n
,\t
等)。lstrip()
: 只去除左边的。rstrip()
: 只去除右边的。
text = " -- hello, world! -- "
print(f"原始字符串: '{text}'") # 输出: 原始字符串: ' -- hello, world! -- '
# 默认去除空白
print(f"strip(): '{text.strip()}'") # 输出: strip(): '-- hello, world! --'
# 指定要去除的字符集
# 它会去除两端所有在 ' -' 这个集合里的字符
print(f"strip(' -'): '{text.strip(' -')}'")# 输出: strip(' -'): 'hello, world!'
# 单独使用 lstrip 和 rstrip
print(f"lstrip(' -'): '{text.lstrip(' -')}'") # 输出: lstrip(' -'): 'hello, world! -- '
print(f"rstrip(' -'): '{text.rstrip(' -')}'") # 输出: rstrip(' -'): ' -- hello, world!'
2
3
4
5
6
7
8
9
10
11
12
13
# 大小写转换
text = "pyTHON proGRamming"
print(f"原始: {text}") # 输出: 原始: pyTHON proGRamming
print(f"upper(): {text.upper()}") # 输出: upper(): PYTHON PROGRAMMING
print(f"lower(): {text.lower()}") # 输出: lower(): python programming
print(f"capitalize(): {text.capitalize()}") # 输出: capitalize(): Python programming
print(f"title(): {text.title()}") # 输出: title(): Python Programming
print(f"swapcase(): {text.swapcase()}") # 输出: swapcase(): PYthon PROgrAMMING
2
3
4
5
6
7
# 5.3 分割与连接
# 分割字符串: split()
split(sep=None, maxsplit=-1)
按 sep
(分隔符) 分割字符串,返回一个包含子串的列表。
- 如果
sep
未指定或为None
,它会按任意连续的空白字符分割,并丢弃空字符串。 maxsplit
指定最大分割次数,分割出的列表将有maxsplit + 1
个元素。
# 按指定分隔符分割
csv_line = "apple,banana,orange"
print(f"按','分割: {csv_line.split(',')}") # 输出: 按','分割: ['apple', 'banana', 'orange']
# 按默认空白分割
text = "Hello World\nPython"
print(f"按空白分割: {text.split()}") # 输出: 按空白分割: ['Hello', 'World', 'Python']
# 限制分割次数
csv_line = "apple,banana,orange,grape"
print(f"最多分割2次: {csv_line.split(',', 2)}") # 输出: 最多分割2次: ['apple', 'banana', 'orange,grape']
2
3
4
5
6
7
8
9
10
11
# 连接字符串: join()
'separator'.join(iterable)
是最高效的字符串连接方式。
新手陷阱:
join
是由分隔符字符串调用的,而不是由列表调用的。可以记作:“用我(分隔符)把他们(列表元素)连起来”。
words = ["Python", "is", "awesome"]
# 正确用法
sentence = " ".join(words)
print(f"用' '连接: {sentence}") # 输出: 用' '连接: Python is awesome
# 错误用法
# words.join(" ") # 这会引发 AttributeError
# 连接包含非字符串元素的列表
items = ["ID", 101, True, 99.9]
# str_items = ",".join(items) # 这会引发 TypeError
# 正确方式:先用生成器表达式将所有元素转为字符串
str_items = ",".join(str(item) for item in items)
print(f"连接混合类型: {str_items}") # 输出: 连接混合类型: ID,101,True,99.9
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 5.4 字符串类型判断
这类方法都以 is
开头,返回布尔值 True
或 False
。
s.isspace()
: 是否只包含空白字符。s.isalpha()
: 是否只包含字母。s.isalnum()
: 是否只包含字母和数字。isdecimal()
vsisdigit()
vsisnumeric()
的深度辨析:示例 isdecimal()
isdigit()
isnumeric()
备注 "123"
True True True 标准数字 "¹²³"
False True True 上标数字 "½"
False False True 分数形式 "二"
False False True 汉字数字 "IV"
False False False 罗马数字(是字母)
# 5.5 对齐与填充
text = "Python"
# 居中对齐,总宽度为20,用'*'填充
print(f"center: '{text.center(20, '*')}'") # 输出: center: '*******Python*******'
# 左对齐
print(f"ljust: '{text.ljust(20, '-')}'") # 输出: ljust: 'Python--------------'
# 右对齐
print(f"rjust: '{text.rjust(20, '=')}'") # 输出: rjust: '==============Python'
# zfill: 右对齐,左边用'0'填充,常用于数字
number = "42"
print(f"zfill: '{number.zfill(5)}'") # 输出: zfill: '00042'
2
3
4
5
6
7
8
9
10
11
# 6. 字符串格式化
格式化是将变量的值嵌入到字符串中的过程。f-string 是现代Python中最推荐的方式。
# 6.1 f-string (格式化字符串字面量) - 首选推荐
自 Python 3.6 起,f-string 提供了最强大、最直观、性能最好的格式化方式。
- 基本语法:
f"..."
,在字符串前加f
,用{}
包围变量。name = "Alice" age = 30 print(f"她是 {name},今年 {age} 岁。") # 输出: 她是 Alice,今年 30 岁。
1
2
3 - 内嵌表达式:
{}
内可以放任何合法的 Python 表达式。print(f"明年她就 {age + 1} 岁了。") # 输出: 明年她就 31 岁了。 print(f"她的名字大写是: {name.upper()}") # 输出: 她的名字大写是: ALICE
1
2 - 格式说明符:
f"{value:specifier}"
,用于控制输出格式。pi = 3.1415926 print(f"Pi (保留2位小数): {pi:.2f}") # 输出: Pi (保留2位小数): 3.14 rate = 0.85 print(f"成功率: {rate:.1%}") # 输出: 成功率: 85.0% num = 1234567 print(f"金额 (带千位分隔符): {num:,}") # 输出: 金额 (带千位分隔符): 1,234,567
1
2
3
4
5
6
7
8 - 自记录表达式 (Python 3.8+): 绝佳的调试工具,会自动打印变量名和值。
user_name = "Bob" print(f"调试信息: {user_name=}") # 输出: 调试信息: user_name='Bob'
1
2
# 6.2 str.format()
方法
在 f-string 出现之前是标准方法,在某些需要动态构建格式字符串的场景下仍然有用。
# 按位置
template = "你好, {0}。欢迎来到 {1}。"
print(template.format("张三", "北京")) # 输出: 你好, 张三。欢迎来到 北京。
# 按关键字
template = "你好, {name}。欢迎来到 {city}。"
print(template.format(name="李四", city="上海")) # 输出: 你好, 李四。欢迎来到 上海。
2
3
4
5
6
7
# 7. 编码与解码:让计算机读懂你的文字
你是否见过乱码(如 `` 或
æ¾å½
)?这通常是编码或解码错误导致的。
# 7.1 为什么需要编码?
核心原因:计算机的底层只认识数字(0和1),不认识人类的字符(如 'A', '你', '😀')。
因此,我们需要一个“翻译系统”来将字符与数字对应起来。这个系统就是字符编码。
- 字符 (Character): 我们在屏幕上看到和输入的文本符号。
- 码点 (Code Point): 在一个标准(如Unicode)中,为每个字符分配的唯一编号。例如,'A' 的码点是 65。
- 字节 (Byte): 计算机存储和传输数据的基本单位。码点最终需要被转换成字节序列才能被计算机处理。
整个过程就像这样:
- 编码 (Encode):
字符 '你'
->Unicode码点 20320
->UTF-8编码规则
->字节序列 b'\xe4\xbd\xa0'
- 解码 (Decode):
字节序列 b'\xe4\xbd\xa0'
->UTF-8解码规则
->Unicode码点 20320
->字符 '你'
# 7.2 核心概念:Unicode 与 UTF-8
- Unicode: 是一本巨大的字典,它为世界上几乎所有的字符都分配了一个独一无二的编号(码点)。它解决了“哪个数字代表哪个字”的问题。你可以用
ord()
函数查看一个字符的Unicode码点。 - UTF-8: 是目前最流行、最通用的编码规则(实现方式)。它定义了如何将Unicode码点高效地转换成字节序列。
- 优点:它是可变长度的。编码英文字符时只用1个字节(与ASCII兼容),编码常用汉字时用3个字节,非常节省空间。
- 其他编码: 还有
GBK
,Big5
等,主要用于处理特定语言,但现在推荐始终优先使用 UTF-8。
# 查看 'A' 和 '你' 的 Unicode 码点
print(f"'A' 的 Unicode 码点是: {ord('A')}") # 输出: 'A' 的 Unicode 码点是: 65
print(f"'你' 的 Unicode 码点是: {ord('你')}") # 输出: '你' 的 Unicode 码点是: 20320
# 从码点反向获取字符
print(f"码点 65 对应的字符是: {chr(65)}") # 输出: 码点 65 对应的字符是: A
print(f"码点 20320 对应的字符是: {chr(20320)}") # 输出: 码点 20320 对应的字符是: 你
2
3
4
5
6
7
# 7.3 Python中的编码与解码操作
在Python 3中,文本和二进制数据被严格区分:
str
类型:用于存储文本,内部使用Unicode。bytes
类型:用于存储二进制数据,其字面量以b''
表示。
编码: str.encode(encoding='utf-8')
将字符串(str
)转换为字节序列(bytes
)。
text = "你好, World!"
print(f"原始字符串 (str): {text}") # 输出: 原始字符串 (str): 你好, World!
# 使用 UTF-8 编码
utf8_bytes = text.encode('utf-8')
print(f"UTF-8 编码后 (bytes): {utf8_bytes}") # 输出: UTF-8 编码后 (bytes): b'\xe4\xbd\xa0\xe5\xa5\xbd, World!'
print(f"类型是: {type(utf8_bytes)}") # 输出: 类型是: <class 'bytes'>
# 使用 GBK 编码
gbk_bytes = text.encode('gbk')
print(f"GBK 编码后 (bytes): {gbk_bytes}") # 输出: GBK 编码后 (bytes): b'\xc4\xe3\xba\xc3, World!'
2
3
4
5
6
7
8
9
10
11
\xe4\xbd\xa0
是 “你” 字在UTF-8编码下的三个字节的十六进制表示。
解码: bytes.decode(encoding='utf-8')
将字节序列(bytes
)转换回字符串(str
)。
utf8_bytes = b'Hello, \xe4\xbd\xa0\xe5\xa5\xbd!' # 这是一个字节序列
# 使用 UTF-8 解码
decoded_text = utf8_bytes.decode('utf-8')
print(f"原始字节: {utf8_bytes}") # 输出: 原始字节: b'Hello, \xe4\xbd\xa0\xe5\xa5\xbd!'
print(f"UTF-8 解码后 (str): {decoded_text}") # 输出: UTF-8 解码后 (str): Hello, 你好!
print(f"类型是: {type(decoded_text)}") # 输出: 类型是: <class 'str'>
2
3
4
5
6
7
# 7.4 常见错误与乱码的根源
乱码的根本原因是编码和解码时使用的标准不一致。就像用英语的密码本去解一本法语的密文。
UnicodeEncodeError
: 编码时出错
当你试图用一个无法表示某些字符的编码标准去编码时,就会发生这个错误。
text = "你好"
try:
# ASCII 编码表里根本没有中文字符,所以无法编码
ascii_bytes = text.encode('ascii')
except UnicodeEncodeError as e:
print(f"编码错误: {e}") # 输出: 编码错误: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
2
3
4
5
6
UnicodeDecodeError
: 解码时出错
这是最常见的乱码原因:用错误的编码标准去解码字节序列。
# 1. 将 "你好" 用 UTF-8 编码
text = "你好"
utf8_bytes = text.encode('utf-8') # b'\xe4\xbd\xa0\xe5\a5\xbd'
# 2. 错误地尝试用 GBK 解码
try:
wrong_text = utf8_bytes.decode('gbk')
print(f"错误解码的结果: {wrong_text}")
except UnicodeDecodeError as e:
# 在更严格的系统上,这会直接报错
print(f"解码错误: {e}") # 输出: 解码错误: 'gbk' codec can't decode byte 0xa0 in position 2: illegal multibyte sequence
2
3
4
5
6
7
8
9
10
11
# 7.5 错误处理策略
在 .encode()
和 .decode()
中,可以通过 errors
参数来处理无法转换的字符。
errors='strict'
(默认值): 遇到错误就抛出异常。errors='ignore'
: 直接忽略掉无法处理的字符。errors='replace'
: 用一个替代符号(通常是?
或 ``)来替换无法处理的字符。
text = "你好, Python"
# 编码时处理错误
# 使用 'ascii' 编码,它不认识中文字符
ascii_ignored = text.encode('ascii', errors='ignore')
print(f"errors='ignore' 编码结果: {ascii_ignored}") # 输出: errors='ignore' 编码结果: b', Python'
ascii_replaced = text.encode('ascii', errors='replace')
print(f"errors='replace' 编码结果: {ascii_replaced}") # 输出: errors='replace' 编码结果: b'??, Python'
# 解码时处理错误
gbk_bytes = '你好'.encode('gbk')
# 假设我们收到了 gbk_bytes,但误以为是 utf-8,并且想避免程序崩溃
utf8_decoded_replaced = gbk_bytes.decode('utf-8', errors='replace')
print(f"错误解码但替换后的结果: {utf8_decoded_replaced}") # 输出: 错误解码但替换后的结果: 好
2
3
4
5
6
7
8
9
10
11
12
13
14
15
最佳实践总结:
- 在程序内部,始终使用
str
(Unicode) 处理文本。- 只有在与外部系统交互时(如读写文件、网络传输)才进行编码和解码。
- 除非有特殊要求,一律使用
utf-8
作为你的编码标准。- 当读取外部数据不确定其编码时,需要小心处理,可以尝试用
utf-8
解码,如果失败,再考虑gbk
等其他可能。
# 8. 性能优化
- 拼接大量字符串时,永远使用
"".join()
。 - 检查开头/结尾时,使用
startswith()
和endswith()
,它们比切片更清晰、更高效。 - 检查空字符串时,利用其布尔值为
False
的特性。my_string = "" # 推荐 if not my_string: print("字符串为空") # 输出: 字符串为空 # 不推荐 if len(my_string) == 0: print("字符串为空") # 输出: 字符串为空
1
2
3
4
5
6
7 - 优先使用 f-string 进行格式化,除非有特殊需求(如处理用户提供的模板)。
# 9. 实战应用案例
# 9.1 解析简单的URL参数
url = "https://example.com/search?q=python&page=2"
# 找到 '?' 的位置
query_string_part = url.split('?')[1]
# 分割参数对
params = query_string_part.split('&') # ['q=python', 'page=2']
for param in params:
key, value = param.split('=')
print(f"参数: {key}, 值: {value}")
# 输出:
# 参数: q, 值: python
# 参数: page, 值: 2
2
3
4
5
6
7
8
9
10
11
# 9.2 清理并标准化用户输入
def normalize_username(username: str) -> str:
"""将用户名标准化:转为小写,去除首尾空白"""
return username.lower().strip()
print(f"清理前: ' Bob_Smith ', 清理后: '{normalize_username(' Bob_Smith ')}'") # 输出: 清理前: ' Bob_Smith ', 清理后: 'bob_smith'
2
3
4
5
# 9.3 构建CSV行
data = ["Alice", 30, "alice@example.com"]
# 确保所有元素都是字符串,并用逗号连接
csv_row = ",".join(str(item) for item in data)
print(f"生成的CSV行: {csv_row}") # 输出: 生成的CSV行: Alice,30,alice@example.com
2
3
4