程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Python 基础

    • 环境搭建
    • 标识符
    • 变量
    • 缩进和注释
    • 数据类型
    • 数据类型转换
    • 运算符
    • 字符串
      • 1. 核心特性:不可变的序列
      • 2. 创建字符串:多种方式与选择
        • 2.1 引号的使用
        • 2.2 转义字符:表达特殊含义
        • 2.3 原始字符串 (Raw String):忽略转义
      • 3. 基础操作:拼接与重复
        • 3.1 字符串拼接 (Concatenation)
        • 使用 + 运算符
        • 使用 += 进行累积拼接
        • 3.2 字符串重复 (Repetition)
      • 4. 访问与切片:精准定位字符
        • 4.1 索引 (Indexing):访问单个字符
        • 4.2 切片 (Slicing):提取子字符串
      • 5. 常用字符串方法
        • 5.1 查找与定位
        • 查找首次出现: find() 和 index()
        • 统计出现次数: count()
        • 检查开头与结尾: startswith() 和 endswith()
        • 5.2 修改与变换
        • 替换子串: replace()
        • 去除首尾字符: strip(), lstrip(), rstrip()
        • 大小写转换
        • 5.3 分割与连接
        • 分割字符串: split()
        • 连接字符串: join()
        • 5.4 字符串类型判断
        • 5.5 对齐与填充
      • 6. 字符串格式化
        • 6.1 f-string (格式化字符串字面量) - 首选推荐
        • 6.2 str.format() 方法
      • 7. 编码与解码:让计算机读懂你的文字
        • 7.1 为什么需要编码?
        • 7.2 核心概念:Unicode 与 UTF-8
        • 7.3 Python中的编码与解码操作
        • 7.4 常见错误与乱码的根源
        • 7.5 错误处理策略
      • 8. 性能优化
      • 9. 实战应用案例
        • 9.1 解析简单的URL参数
        • 9.2 清理并标准化用户输入
        • 9.3 构建CSV行
    • 列表
    • 元组
    • 字典
    • 集合
    • if判断
    • for循环
    • while循环
    • 循环综合练习
    • Python函数
    • 函数与循环实战
    • 生成式
    • 文件读写
    • 面向对象
    • 面向对象综合案例
  • Python 进阶

  • Python
  • Python 基础
scholar
2025-07-20
目录

字符串

# 字符串 (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', 内存地址: ...(一个与上面不同的新地址)
1
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"
1
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
1
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.
1
2
3
4
5

性能警告:在循环中大量使用 + 或 += 拼接字符串效率很低。因为每次拼接都会创建一个新的字符串对象。在这种情况下,请务必使用后文将介绍的 "".join() 方法。

# 3.2 字符串重复 (Repetition)

使用 * 运算符可以将一个字符串重复多次。

# 创建一条简单的分隔线
separator = "-" * 30
print(separator) # 输出: ------------------------------

# 创建一个醒目的标题
header = "* " * 5 + " 年度报告 " + "* " * 5
print(header) # 输出: * * * * *  年度报告  * * * * *
1
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
1
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'
1
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
1
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 次
1
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
1
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.
1
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!'
1
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
1
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']
1
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
1
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() vs isdigit() vs isnumeric() 的深度辨析:
    示例 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'
1
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="上海")) # 输出: 你好, 李四。欢迎来到 上海。
1
2
3
4
5
6
7

# 7. 编码与解码:让计算机读懂你的文字

你是否见过乱码(如 `` 或 æ¾å½)?这通常是编码或解码错误导致的。

# 7.1 为什么需要编码?

核心原因:计算机的底层只认识数字(0和1),不认识人类的字符(如 'A', '你', '😀')。

因此,我们需要一个“翻译系统”来将字符与数字对应起来。这个系统就是字符编码。

  • 字符 (Character): 我们在屏幕上看到和输入的文本符号。
  • 码点 (Code Point): 在一个标准(如Unicode)中,为每个字符分配的唯一编号。例如,'A' 的码点是 65。
  • 字节 (Byte): 计算机存储和传输数据的基本单位。码点最终需要被转换成字节序列才能被计算机处理。

整个过程就像这样:

  1. 编码 (Encode): 字符 '你' -> Unicode码点 20320 -> UTF-8编码规则 -> 字节序列 b'\xe4\xbd\xa0'
  2. 解码 (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 对应的字符是: 你
1
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!'
1
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'>
1
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)
1
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
1
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}") # 输出: 错误解码但替换后的结果: 好
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

最佳实践总结:

  • 在程序内部,始终使用 str (Unicode) 处理文本。
  • 只有在与外部系统交互时(如读写文件、网络传输)才进行编码和解码。
  • 除非有特殊要求,一律使用 utf-8 作为你的编码标准。
  • 当读取外部数据不确定其编码时,需要小心处理,可以尝试用 utf-8 解码,如果失败,再考虑 gbk 等其他可能。

# 8. 性能优化

  1. 拼接大量字符串时,永远使用 "".join()。
  2. 检查开头/结尾时,使用 startswith() 和 endswith(),它们比切片更清晰、更高效。
  3. 检查空字符串时,利用其布尔值为 False 的特性。
    my_string = ""
    # 推荐
    if not my_string:
        print("字符串为空") # 输出: 字符串为空
    # 不推荐
    if len(my_string) == 0:
        print("字符串为空") # 输出: 字符串为空
    
    1
    2
    3
    4
    5
    6
    7
  4. 优先使用 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
1
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'
1
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
1
2
3
4
编辑此页 (opens new window)
上次更新: 2025/07/23, 06:33:16
运算符
列表

← 运算符 列表→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式