第 3 章:字符串的秘密
本章内容限 Python 3.12+ 用户
0. 什么是字符串?
本节目标:初步了解字符串
预计用时:5~10 分钟
完成标志:能初步理解字符串的作用及意义
0.1 本节正文
字符串(String),顾名思义,就是一串字符。本质上是一个 由 Unicode 字符组成的、不可变的有序序列。
它是最常用的文本表示方式——你写的名字、地址、聊天内容,在程序里都是用字符串来存放的。
“由 Unicode 字符组成的、不可变的有序序列”,这句话有点绕,我们拆开来理解。
| 关键词 | 人话 |
|---|---|
| Unicode 字符 | 几乎所有文字都能装——中文、英文、日文、Emoji,全包 |
| 不可变 | 一旦创建,内容就不能原地修改。你看着像是在改,实际上是创建了一个新字符串 |
| 有序序列 | 每个字符都有位置编号,可以按位置取出来,顺序固定 |
Unicode 你不陌生。前面章节提到编码时说过:Python 内部用 Unicode 表示所有字符。这就意味着你可以直接在字符串里写中文,不用做任何特殊声明。
比如这样:
name = "张三"
greeting = "Hello,世界"
emoji = "🎉"
字符串的“不可变”听起来抽象,但你早就见过它。第2章 2.2 小节里提过:不可变对象不能原地修改,任何“修改”操作都会创建新对象。字符串就是不可变对象之一。
s = "Hello"
s = s + "World!" # 看似是修改,实则是原地创建了个新的字符串
至于“有序序列”——意思是字符串里的每个字符都有编号(索引),从 0 开始。你能通过位置取到特定字符,后面会单独讲。
现在你只需要记住一句话:字符串就是一段能直接写中文的文本。至于“不可变”和“有序序列”的含义,会在后续用到时自然展开。先有个印象就行。
0.2 本节小结
- 字符串就是文本:名字、地址、聊天内容,凡是跟文字打交道的地方就有字符串。
- Unicode 是它的底气:你可以直接写中文、英文、日文、Emoji,不用做任何特殊处理。
- 不可变、有顺序:这两个特性你现在不需要背。知道它们存在就行,后面用到时会自然熟悉。
这一节你只需要记住一句话:字符串就是一段能直接写中文的文本。 下一节,我们开始实际操作——怎么造一个字符串、怎么把它们拼在一起、怎么知道它有多长。
1. 字符串的创建与拼接
本节目标:学会创建字符串,并掌握几种拼接字符串的方法
预计用时:15~20 分钟
完成标志:能根据不同场景选择合适的引号创建字符串,并能用+拼接字符串
1.1 创建字符串:三种引号怎么选?
创建字符串最简单的方式,就是把文本放在引号里。
Python 提供了三种写法,它们的用途各不相同:
name = "张三" # 双引号
father = '李四' # 单引号
text = """I'm LanWan
I like coding
code make me happy
Python is world most great language one of.
""" # 三引号(保留换行)
那么问题来了:我什么时候用哪一种?需要看情况讨论:
| 场景 | 用什么 | 理由 |
|---|---|---|
| 普通字符串 | 单引号 / 双引号 | 选一个,保持一致,尽量选择双引号 |
| 文本里包含单引号 | 双引号 | 不用转义,直接写 "it's ok!" |
| 文本里包含双引号 | 单引号 | 不用转义,直接写 '他说"你好"' |
| 多行文本 | 三引号 | 换行、缩进都保留,不用写 \n |
选一种引号作为主力(比如双引号),遇到冲突才换另一种。这样既统一,又能避免转义符号满天飞。
要避免的写法:msg = "it's ok" 和 msg = '他说"你好"' 混着用,项目里风格不一致。
# 推荐
name = "张三"
msg = "it's ok" # 内含单引号,用双引号括住
msg = '他说"你好"' # 内含双引号,用单引号括住
# 不推荐(风格不统一)
name = '张三'
msg = "it's ok"
关于三引号,你只要知道它能保留换行就行。遇到多行文本(比如 HTML 模板、SQL 语句)时再回来查。
1.2 转义字符:引号冲突的解决方式
如果你看这代码不顺眼,非要用单引号括住带单引号的文本,Python 也不是不能处理——它可以用 转义字符 来解决。
msg = 'it\'s ok' # \' 表示“这不是结束符,这是个单引号字符”
\ 是转义符号,它的意思是:“把后面的字符当成普通字符,不要按语法规则解释。”
除了引号,\n 是换行,\t 是 Tab,\\ 是普通的反斜杠。
比如这样:
path = "C:\\Users\\name" # 两个斜杠才表示一个斜杠
但问题是:这种写法可读性很差。所以项目内统一用双引号作为主力,就可以避免 90% 的反斜杠污染。
1.3 拼接字符串:+ 与隐式拼接
+ 运算符拼接
最直接的拼接方式是用 +:
first = "张"
last = "三"
full = first + last # "张三"
But啊!如果你尝试拼接字符串和整数,解释器就不答应了,会报错:
age = 18
msg = "年龄:" + age # TypeError: can only concatenate str (not "int") to str
这时候需要用 str() 把数字转成字符串:
msg = "年龄:" + str(age) # "年龄:18"
是的没错,常见类型都有对应的函数,能将其他类型转换过来
Python 特有的隐式拼接
Python 有一个不多见但实用的特性:相邻的字符串字面量会自动拼接。
比如这样:
text = "Hello" " " "World" # "Hello World"
这个特性通常用来拆分长字符串,方便阅读:
long_text = ("这是第一行长文本"
"这是第二行长文本"
"这是第三行长文本")
注意:这种拼接只对字面量有效,对变量无效。
a = "Hello"
b = "World"
c = a b # SyntaxError: invalid syntax(不行)
那什么是字面量呢?字面量(Literal),你可以理解为定义变量时,等号右侧写的东西,也就是值,比如 42、17,这是整数字面量,"Hello"、"World",这是字符串字面量。
1.4 为什么需要掌握这些?
- 引号冲突:字符串里包含引号时,你知道怎么处理而不报错。
+拼接:最基础的组合方式,后面写日志、拼消息时天天用。- 隐式拼接:偶尔能帮你把长字符串拆成多行,让代码更干净
1.5 实操演练
第一步:看例子
先观察下面这段代码,注意每一行的写法:
# 1. 创建字符串
name = "张三"
print(name)
# 2. 处理引号冲突
msg1 = "it's ok"
msg2 = '他说"你好"'
print(msg1, msg2)
# 3. 拼接字符串
first = "张"
last = "三"
full = first + last
print(full)
# 4. 数字转字符串后拼接
age = 18
print("年龄:" + str(age))
注意观察:
msg1为什么能用双引号包住带单引号的文本?msg2为什么能用单引号包住带双引号的文本?str(age)把数字变成了什么?
第二步:猜结果
不看上面的输出,先猜一下每行 print() 会打印什么,写在注释后面:
print(name) # 我猜:____
print(msg1, msg2) # 我猜:____
print(full) # 我猜:____
print("年龄:" + str(age)) # 我猜:____
然后把代码敲进编辑器跑一遍,看看你的猜测对不对。如果猜错了,想想为什么。
第三步:自己写
完成以下三个小任务:
任务1:定义一个变量 city,值是“北京”,然后打印它。
任务2:把 msg1 = "it's ok" 改成用单引号包住的写法(也就是 'it\'s ok'),看看输出有没有变。
任务3:定义一个变量 score = 95,打印 "你的分数是:95"。提示:不能直接写 "你的分数是:" + score,你知道为什么吗?
1.6 本节小结
- 三种引号:单引号、双引号、三引号。选一种做主力,另一种处理冲突,三引号保留多行格式。
- 转义字符:
\让特殊字符变普通字符,但能不用就不用——靠引号策略避免转义更干净。 - 拼接方式:
+是主力,只能拼字符串,数字要先用str()转。隐式拼接是 Python 特性,用来拆行长文本。
下一节我们讲字符串的索引与切片——怎么从一串字符里“剪”出你需要的那一段。
2. 字符串的工具
本节目标:掌握字符串的常用方法,了解索引和切片
预计用时:20~25 分钟
完成标志:能熟练使用len()、find()、split()、join()等常用字符串方法,能掌握切片的技巧
上一节我们学会了怎么创建字符串、怎么把它们拼在一起。但实际开发中,你更多时候需要的是从字符串里找东西、切东西、改东西——就像厨师拿到食材后,需要用刀、镊子、漏勺来处理它。
这一节我们就来认识处理字符串的几件常用工具。
2.1 len()函数:字符串的“尺子”
想知道一个字符串有多长(有多少个字符),用 len():
name = "张三"
print(len(name)) # 2
msg = "Hello, 世界!"
print(len(msg)) # 10(逗号、空格、感叹号都算)
len() 是个通用函数,后面学列表、字典时也用它。
2.2 in运算符:判断存在
想检查某段文字是否在字符串里,用 in:
text = "我喜欢用 Python 写代码"
print("Python" in text) # True
print("Java" in text) # False
它返回的是 True 或 False,很适合放在 if 条件里。
2.3 find()函数:字符串的“地图”
想找到某段文字在字符串里的位置,用 find():
text = "Hello, world!"
print(text.find("world")) # 7(w 在第 7 个位置)
print(text.find("Python")) # -1(找不到就返回 -1)
注意:位置从 0 开始数。-1 表示“没找到”。
2.4 split()函数:字符串的“菜刀”
想把一个字符串按某种规则切成多段,用 split():
s = "苹果,香蕉,橙子"
fruits = s.split(",") # ["苹果", "香蕉", "橙子"]
print(fruits[0]) # 苹果
不传参数时,默认按空白(空格、换行、Tab)切分:
words = "hello world python".split() # ["hello", "world", "python"]
split() 返回的是列表(后面会专门讲,现在只需要知道“它能切出一组数据”)。
2.5 join()函数:字符串的“胶水”
split() 的反向操作——把一组字符串拼成一个:
fruits = ["苹果", "香蕉", "橙子"]
s = ",".join(fruits) # "苹果,香蕉,橙子"
注意写法:分隔符写在前面,要拼接的列表写在括号里。
2.6 strip()函数:去除空白
处理用户输入时,经常会多出多余的空格,用 strip() 可以去掉首尾的空白:
user_input = " 你好 "
clean = user_input.strip()
print(clean) # "你好"
还有 lstrip()(掐头)和 rstrip()(去尾),需要时再查就行。
2.7 replace()函数:替换
想把字符串里的某段文字换成别的,用 replace():
text = "我喜欢用 Python"
new_text = text.replace("Python", "C嘎嘎")
print(new_text) # "我喜欢用 C嘎嘎"
2.8 按位置取字符:索引
字符串是有序序列,每个字符都有固定位置。位置从 0 开始编号,这叫索引。请务必记住,字 符 串 索 引 从 0 开 始!
s = "Python"
# ↑↑↑↑↑↑
# 012345
print(s[0]) # P
print(s[3]) # h
print(s[5]) # n
负数索引表示从末尾开始数,-1 是最后一个字符:
print(s[-1]) # n
print(s[-3]) # h
print(s[-6]) # P
索引超出范围会报错:
s[100]→IndexError
2.9 截取片段:切片
想取出一段连续的内容,用切片。语法是 [起始:结束:步长],顾头不顾尾。一定要记住:[0:5]实际取的是 0~4!
s = "Hello, world!"
# 0 1 2 3 4 5 6 7 8 9 10 11 12
print(s[0:5]) # Hello(取索引 0 到 4)
print(s[7:12]) # world(取索引 7 到 11)
省略写法:
print(s[:5]) # Hello(开头到 4,等价于 s[0:5])
print(s[7:]) # world!(7 到末尾)
print(s[:]) # 完整复制一份
负数切片同样有效:
print(s[-6:-1]) # world(-6 到 -2)
print(s[-6:]) # world!(-6 到末尾)
步长可以控制取的方向和间隔:
print(s[::2]) # Hlo ol!(每隔一个取一个)
print(s[::-1]) # !dlrow ,olleH(倒序,常用)
2.10 实操一下!
先写出你的猜想,再放入编辑器验证:
# 1. 索引
s = "Python"
print(s[0]) # ?
print(s[-1]) # ?
# print(s[10]) # 会发生什么?
# 2. 切片
s = "Hello, world!"
print(s[0:5]) # ?
print(s[7:]) # ?
print(s[::-1]) # ?
# 3. len()
print(len("你好世界")) # ?
# 4. in
print("Py" in "Python") # ?
print("java" in "Python") # ?
# 5. find()
print("Hello".find("e")) # ?
print("Hello".find("a")) # ?
# 6. split()
csv = "apple,banana,orange"
print(csv.split(",")) # ?
# 7. join()
words = ["a", "b", "c"]
print("-".join(words)) # ?
# 8. strip()
msg = " hello "
print(msg.strip()) # ?
# 9. replace()
msg = "I like Java"
print(msg.replace("Java", "Python")) # ?
2.11 本节小结
| 工具 | 作用 | 示例 |
|---|---|---|
索引 s[i] | 按位置取一个字符 | "Hello"[1] → "e" |
切片 s[start:end:step] | 截取一段 | "Hello"[1:4] → "ell" |
len() | 测量长度 | len("Hello") → 5 |
in | 判断是否存在 | "Py" in "Python" → True |
find() | 查找位置 | "Hello".find("e") → 1 |
split() | 切分成列表 | "a,b".split(",") → ["a","b"] |
join() | 拼接成字符串 | ",".join(["a","b"]) → "a,b" |
strip() | 去除首尾空白 | " hi ".strip() → "hi" |
replace() | 替换内容 | "Hi".replace("i","o") → "Ho" |
3. 特殊的字符串
本节目标:了解几种带前缀的特殊字符串
预计用时:15~20 分钟(不含f-string)
完成标志:能区分 f、r、t、b、u 五种字符串前缀的用途,并知道什么时候该用哪一个
如果你身边有写 Python 的朋友,那你可能见过这样的代码:
meta = f"名字:{name},年龄:{age},职业:{job}"
你可能会想,这我熟悉!meta 是一个字符串,可是前面那个 f 是什么意思?比较好看吗?
其实,Python 的字符串还有一些带前缀的变体——它们在引号前面加一个字母,改变字符串的行为或含义。
这一节我们来认识五种前缀:f、r、t、b、u。
3.1 r-string
r 是 raw(原始)的缩写。在字符串前面加 r,反斜杠 \ 不再被当作转义符,而是当成普通字符原样保留。
先看普通字符串的问题:
path = "C:\new\file.txt"
print(path) # \n 被当成换行了,输出乱掉
\n 被解释成了换行,路径就乱了。用 r 可以避免这个问题:
path = r"C:\new\file.txt"
print(path) # C:\new\file.txt 原样输出
r-string 最常见的用途有两个:
文件路径(尤其是 Windows 路径):
path = r"C:\Users\name\Documents"
正则表达式:正则里大量使用 \,用 r-string 可以少写一堆转义符。
import re
pattern = r"\d{3}-\d{4}" # 匹配类似 123-4567 的格式
注意:r-string 不能以奇数个反斜杠结尾。比如 r"C:\" 会报错,需要写成 r"C:\" "\\" 或者干脆不用 r。
3.2 t-string:模板字符串(Python 3.15+)
注意:Python 3.15 目前未正式发布,了解即可
t 是 template(模板)的缩写。这是 Python 3.15 的新特性,由 PEP 750 引入。
t-string 的语法和 f-string 几乎一样——用 t 前缀代替 f 前缀,花括号里写表达式。
pi = 3.14
t't-strings are new in Python {pi}'
但关键区别在于:f-string 会立即计算并返回一个字符串;而 t-string 返回的是一个 Template 对象,不会立即求值。
可以这样理解:
- f-string:厨师做好菜直接端上来,你马上就能吃。
- t-string:厨师给你一份菜谱(Template 对象),你拿到后可以自己决定什么时候做、怎么做、甚至加什么料。
这种“延迟处理”的设计让 t-string 在以下场景特别有用:
- SQL 安全:可以安全地处理插值,防止 SQL 注入
- HTML 模板:自动转义,防止 XSS 攻击
- Shell 命令:安全地拼接命令参数
- 自定义 DSL:你可以自己决定如何处理插值
由于 t-string 是 Python 3.15 的新特性,目前大多数环境还不支持。现阶段你只需要知道它的存在和基本概念,等 3.15 普及后再深入使用。
那么问题来了,什么是 f-string ?不必着急,小节 3.4 将深入讲解
3.3 b-string:字节串
b 是 bytes(字节)的缩写。在字符串前面加 b,得到的是 bytes 对象,而不是普通的 str 字符串。
s = "hello" # str 类型
b = b"hello" # bytes 类型
print(type(s)) #
print(type(b)) #
bytes 对象里存放的是原始字节(0~255 的整数),而不是 Unicode 字符。它主要用于:
- 文件读写(二进制模式)
- 网络传输(发送和接收原始数据)
- 与 C 语言库交互
# 写入二进制文件
with open("image.jpg", "wb") as f:
f.write(b"\xff\xd8\xff\xe0") # JPEG 文件头
# 字符串和 bytes 互转
text = "你好"
bytes_data = text.encode("utf-8") # str → bytes
text_back = bytes_data.decode("utf-8") # bytes → str
注意:
b-string里只能写 ASCII 字符(字母、数字、标点),不能直接写中文。要表示中文需要先 encode。
3.4 f-string:嵌入变量
本小节目标:了解
f-string的用途,学会使用f-string
预计用时:20~25 分钟
完成标志:能够运用f-string来拼接字符串
3.4.1 f-string 是什么?
f-string 的全称是 格式化字符串字面量(formatted string literals),是 Python 3.6 引入的一种字符串写法。
它的语法很简单:在字符串前面加一个字母 f,然后在字符串内部用 {变量名} 把变量嵌进去。
比如这样:
name = "张三"
age = 18
print(f"我叫{name},今年{age}岁")
这种写法直观、简洁、性能也好。前面我们学过的 + 拼接,写起来像是“把碎片粘在一起”,而 f-string 更像“在一个完整的句子里留出空位填东西”——后者更接近人类自然的表达方式。
3.4.2 为什么需要格式控制?
你可能觉得奇怪:变量嵌进去就行了,为什么还要专门学“格式控制”?
因为现实世界的数据展示没那么简单。
比如你从数据库里拿到一个商品价格 19.9,直接 f"价格:{price}" 输出是 价格:19.9——这没问题。但如果价格是 19.900000000000002(浮点数运算的经典误差),直接输出就难看了。
再比如你想做一个成绩单:
name = "张三"
score = 98.5
print(f"{name}:{score}分")
看起来还行。但如果名字长度不一样:
name1 = "张三"
name2 = "李小明"
print(f"{name1}:{score1}分")
print(f"{name2}:{score2}分")
输出会参差不齐:
张三:98.5分
李小明:97.0分
这时候你可能希望它们对齐,方便阅读。格式控制就是用来解决这类问题的——它让你能精确控制数字的小数位数、文本的对齐方式、千位分隔符、甚至进制显示。
简单说:值管“存什么”,格式管“怎么显示”。
3.4.3 花括号里可以放什么
f-string 的花括号 {} 里可以放各种 Python 表达式,比如:
name = "张三"
age = 18
print(f"我叫{name}") # 变量
print(f"明年{age + 1}岁") # 算术运算
print(f"我的名字有{len(name)}个字") # 函数调用
print(f"我叫{name.upper()}") # 方法调用
print(f"{[1,2,3][0]}") # 索引取值
3.4.4 格式控制
f-string 的格式控制写在花括号里,用冒号 : 分隔变量名和格式说明符。基本结构是:{变量名:格式说明符}。
pi = 3.1415926
print(f"{pi:.2f}") # 3.14
# ↑ ↑
# 变量名 格式说明符(冒号后面全是格式控制)
下面按功能分组,逐一讲解:
一、对齐与填充
控制字符串在指定宽度内的对齐方式和填充字符。语法:{变量:填充符 对齐方式 宽度},但注意顺序是固定的:填充符 → 对齐符 → 宽度。
| 对齐符 | 含义 |
|---|---|
< | 左对齐 |
> | 右对齐 |
^ | 居中对齐 |
= | 数字填充(仅在数字类型有效,填充符写在符号和数字之间) |
name = "Python"
print(f"{name:<10}") # 'Python '(左对齐,宽度10)
print(f"{name:>10}") # ' Python'(右对齐)
print(f"{name:^10}") # ' Python '(居中)
指定填充字符(默认是空格):
print(f"{name:*<10}") # 'Python****'
print(f"{name:*>10}") # '****Python'
print(f"{name:*^10}") # '**Python**'
= 对齐符单独说明一下。它主要用于数字,作用是让填充字符出现在符号(+/-)和数字之间,而不是数字前面。适用于带符号的数值对齐场景。默认情况下,<、>、^ 会把填充符放在符号前面;而 = 会把填充符放在符号和数字之间:
num = -42
print(f"{num:0=6}") # '-00042'(0 填在负号和数字之间)
# 不加 = 的话:print(f"{num:0>6}") # '000-42'(0 填在最前面,负号被挤到后面去了)
二、符号控制
控制正负号的显示方式:
| 符号 | 含义 |
|---|---|
+ | 正负数都显示符号 |
- | 仅负数显示符号(默认行为) |
| 正数前显示空格,负数前显示负号 |
pos = 42
neg = -42
print(f"{pos:+}") # '+42'
print(f"{neg:+}") # '-42'
print(f"{pos: }") # ' 42'(正数前有一个空格,用于对齐正负号)
print(f"{neg: }") # '-42'
三、数字格式化
小数精度
用 .n 控制浮点数的小数位数:
pi = 3.1415926
print(f"{pi:.0f}") # 3(不保留小数,四舍五入)
print(f"{pi:.2f}") # 3.14
print(f"{pi:.5f}") # 3.14159
千位分隔符
用 , 或 _ 作为千位分隔符:
num = 1234567890
print(f"{num:,}") # 1,234,567,890(逗号分隔)
print(f"{num:_}") # 1_234_567_890(下划线分隔,适合大数字)
百分比
用 % 把小数转成百分比显示:
ratio = 0.875
print(f"{ratio:.1%}") # 87.5%
print(f"{ratio:.0%}") # 88%
四、进制转换
用格式类型控制数字的显示进制:
| 类型 | 含义 | 示例(数字 255) |
|---|---|---|
b | 二进制 | 11111111 |
o | 八进制 | 377 |
d | 十进制(默认) | 255 |
x | 小写十六进制 | ff |
X | 大写十六进制 | FF |
num = 255
print(f"{num:b}") # 11111111
print(f"{num:o}") # 377
print(f"{num:x}") # ff
print(f"{num:X}") # FF
加 # 前缀可以显示进制符号(0b、0o、0x):
print(f"{num:#b}") # 0b11111111
print(f"{num:#x}") # 0xff
五、指数表示法
用于表示非常大或非常小的数:
| 类型 | 含义 |
|---|---|
e | 小写科学计数法(如 1.23e+10) |
E | 大写科学计数法(如 1.23E+10) |
g | 自动选择普通表示或科学计数法(小写 e) |
G | 自动选择普通表示或科学计数法(大写 E) |
num = 12345678.9
print(f"{num:e}") # 1.234568e+07
print(f"{num:E}") # 1.234568E+07
print(f"{num:.2e}") # 1.23e+07
六、日期时间格式化
f-string 支持 datetime 对象 的格式化,格式符和 strftime 一致:
from datetime import datetime
now = datetime.now()
print(f"{now:%Y-%m-%d}") # 2026-08-11
print(f"{now:%Y年%m月%d日}") # 2026年08月11日
print(f"{now:%H:%M:%S}") # 14:30:25
print(f"{now:%Y-%m-%d %H:%M}") # 2026-08-11 14:30
完整格式符参考(常用):
| 格式符 | 含义 | 示例 |
|---|---|---|
%Y | 四位年份 | 2026 |
%m | 两位月份 | 08 |
%d | 两位日期 | 11 |
%H | 24小时制小时 | 14 |
%M | 分钟 | 30 |
%S | 秒 | 25 |
%A | 星期几(全称) | Monday |
%a | 星期几(缩写) | Mon |
%B | 月份(全称) | August |
%b | 月份(缩写) | Aug |
七、格式控制完整语法
把上面所有内容汇总成一张图:
{变量名: [填充符][对齐方式][宽度][,][.精度][类型]}
| 部分 | 可选值 | 说明 |
|---|---|---|
| 填充符 | 任意字符(默认空格) | 用于填充空位 |
| 对齐方式 | < > ^ = | 左/右/居中/符号后填充 |
| 宽度 | 数字 | 输出最小宽度 |
| 千位分隔符 | , 或 _ | 数字千位分隔 |
| 精度 | .n | 小数位数 |
| 类型 | b o d x X e E f F % g G | 格式类型 |
3.4.5 实操验证
# 1. 对齐
name = "Python"
print(f"{name:*<10}") # ?
print(f"{name:*>10}") # ?
print(f"{name:*^10}") # ?
# 2. 符号控制
pos = 42
neg = -42
print(f"{pos:+}") # ?
print(f"{neg:+}") # ?
# 3. 小数精度
pi = 3.1415926
print(f"{pi:.2f}") # ?
print(f"{pi:.4f}") # ?
# 4. 千位分隔符
print(f"{1234567890:,}") # ?
print(f"{1234567890:_}") # ?
# 5. 百分比
print(f"{0.875:.0%}") # ?
# 6. 进制转换
print(f"{255:b}") # ?
print(f"{255:#x}") # ?
# 7. 科学计数法
print(f"{12345678.9:.2e}") # ?
# 8. 日期时间
from datetime import datetime
now = datetime.now()
print(f"{now:%Y-%m-%d %H:%M}") # ?
3.4.6 f-string 完整速查表
| 用途 | 写法 | 示例输入 | 输出 |
|---|---|---|---|
| 保留两位小数 | {x:.2f} | 3.14159 | 3.14 |
| 左对齐,宽度10 | {x:<10} | "Hi" | "Hi " |
| 右对齐,宽度10 | {x:>10} | "Hi" | " Hi" |
| 居中,宽度10 | {x:^10} | "Hi" | " Hi " |
| 填充星号居中 | {x:*^10} | "Hi" | "****Hi****" |
| 千位逗号分隔 | {x:,} | 1234567 | "1,234,567" |
| 显示正负号 | {x:+} | 42 | "+42" |
| 百分比 | {x:.0%} | 0.875 | "88%" |
| 二进制 | {x:b} | 255 | "11111111" |
| 十六进制(带前缀) | {x:#x} | 255 | "0xff" |
| 科学计数法 | {x:.2e} | 12345678.9 | "1.23e+07" |
| 日期时间 | {%Y-%m-%d} | datetime.now() | "2026-08-11" |
3.4.7 f-string 本小节总结
f-string 是 Python 3.6 引入的一种字符串写法,在字符串前面加 f,用 {变量名} 嵌入值。它的核心优势是直观——你不需要用 + 把碎片拼起来,而是直接在一句话里留空填东西。
这一小节讲了 f-string 的几层用法:
- 基础:花括号里放变量名、算术运算、函数调用、方法调用,任意表达式都行。
- 格式控制:用冒号
:指定对齐、填充、宽度、小数精度、千位分隔符、进制转换、科学计数法、百分比、日期时间格式——基本上你能想到的显示格式,f-string 都直接支持。
你不需要记住所有格式符。遇到“我要把这个数字显示成特定格式”的需求时,回来查速查表就行。用多了自然记住常用的那几种。
总结:日常写字符串,先想 f-string。它比 + 拼接更直观,比传统格式化方法更简洁,是 Python 现在推荐的首选字符串格式化方式。
3.5 u-string:Python 2 时代的遗产
u 是 Unicode 的缩写。在字符串前面加 u,表示这是一个 Unicode 字符串。
s1 = "你好"
s2 = u"你好"
print(s1 == s2) # True
在 Python 3 中,所有字符串默认就是 Unicode,所以 u 前缀实际上什么也不做。你加不加,结果完全一样。
那为什么还要提它?两个原因:
第一,为了兼容 Python 2。 在 Python 2 中,普通字符串是字节串(类似 Python 3 的 b-string),处理中文时需要加 u 才能正确处理。所以如果你看到一些老代码里到处都是 u"...",那是因为它们要兼容 Python 2。
第二,为了“认识它”。 你在阅读老项目或第三方库源码时,可能会遇到 u"text" 这种写法。知道它是什么、知道它现在没作用,就不会被它搞懵。
如果你不需要兼容 Python 2,永远不需要主动写
u-string。它只是一个历史遗留标记。
3.6 前缀组合
某些前缀可以组合使用,同时具备两种前缀的特性。
常见的组合:
| 组合 | 含义 | 示例 |
|---|---|---|
rf 或 fr | 原始格式化字符串 | rf"C:\Users\{name}" |
rb 或 br | 原始字节串 | rb"\x89PNG" |
rf / fr:原样保留反斜杠 + 嵌入变量
r 让反斜杠原样保留,f 让花括号里的变量被替换。两者互不冲突,可以同时生效。
name = "LanWan"
path = rf"C:\Users\{name}\Documents"
print(path) # C:\Users\LanWan\Documents
顺序不影响结果,fr"..." 和 rf"..." 等价。
rb / br:原始字节串
r 让反斜杠原样保留,b 生成 bytes 对象。主要用于处理包含反斜杠的二进制数据。
data = rb"\x89PNG\r\n\x1a\n"
print(data) # b'\\x89PNG\\r\\n\\x1a\\n'
不能组合的前缀
f 不能和 b 组合,f 也不能和 u 组合:
fb"..." # SyntaxError
fu"..." # SyntaxError
因为 f-string 处理的是文本(str),而 b-string 处理的是字节(bytes),两者在底层数据模型上不兼容。u-string 在 Python 3 中无实际作用,组合也没有意义。
需要组合时,记住一个原则:r 可以和任何前缀组合,f 只能和 r 组合,b 只能和 r 组合。
3.7 实操一下
下面我们来验证 r、t、b、u 四种特殊字符串的行为。先写出你猜测的结果,再把代码敲进编辑器跑一遍。
# 1. r-string:路径中的反斜杠
path1 = "C:\new\file.txt"
path2 = r"C:\new\file.txt"
print(path1) # ?
print(path2) # ?
# 2. r-string:字符串结尾反斜杠的处理
# print(r"C:\") # 取消注释,看看会发生什么
# print(r"C:\" "\\") # 取消注释,看看这种写法能不能解决?
# 3. r-string:正则表达式中的用法
import re
# 用 r-string 写一个匹配电话号码的正则(3位-4位,如 123-4567)
pattern = r"\d{3}-\d{4}"
print(pattern) # ?
print(re.match(pattern, "123-4567") is not None) # ?
# 4. b-string:类型和内容
s = "hello"
b = b"hello"
print(type(s)) # ?
print(type(b)) # ?
print(b[0]) # ?(注意:b-string 索引返回的是整数还是字符?)
# 5. b-string:str 和 bytes 的转换
text = "中文"
bytes_data = text.encode("utf-8")
print(bytes_data) # ?
text_back = bytes_data.decode("utf-8")
print(text_back) # ?
# 6. b-string:b-string 里能不能写中文?
# b"中文" # 取消注释,看看会发生什么
# 7. t-string:验证类型(Python 3.15+ 才支持)
# 如果你的 Python 版本低于 3.15,这一行会报错
# template = t"Hello {name}"
# print(type(template)) # ?
# 8. u-string:Python 3 中是否还有意义?
s1 = "你好"
s2 = u"你好"
print(s1 == s2) # ?
print(type(s1)) # ?
print(type(s2)) # ?
把输出结果记录下来,和你的猜测对比。如果结果不一样,想想为什么。
3.8 本节小结
这一节我们认识了五种带前缀的字符串。它们本质上是同一种东西——字符串,只是在前面加了一个字母,行为就变了。
r-string:反斜杠原样保留,不转义。写 Windows 路径和正则表达式时最有用。
t-string:Python 3.15 的新特性,返回 Template 对象而不是字符串。现阶段了解即可,等 3.15 普及后再深入。
b-string:生成 bytes 对象而不是 str 对象。处理二进制数据(文件、网络)时才会用到。
f-string:日常使用频率最高的一个。在字符串里用 {变量名} 嵌入值,直观、简洁、性能好。花括号里可以放任何表达式,冒号后面可以跟格式控制符——对齐、填充、小数精度、千位分隔符、进制转换、科学计数法、百分比、日期时间格式化,基本上你需要的显示格式它都支持。
u-string:Python 2 时代的遗产。在 Python 3 中没有任何作用,你不需要主动写它,但看到老代码里有它,知道是什么意思就行。
一张表记住五种前缀
| 前缀 | 全称 | 作用 | 什么时候用 |
|---|---|---|---|
r | raw | 反斜杠原样保留 | 文件路径、正则表达式 |
t | template | 返回 Template 对象 | Python 3.15+ 的安全模板场景 |
b | bytes | 生成 bytes 对象 | 二进制文件、网络传输 |
f | formatted | 嵌入变量和表达式 | 日常字符串拼接,首选 |
u | Unicode | Python 3 中无实际作用 | 不用主动写,认识即可 |
前缀可以组合使用
某些前缀可以组合在一起,同时具备两种特性:
| 组合 | 含义 | 示例 |
|---|---|---|
rf 或 fr | 原始格式化字符串 | rf"C:\Users\{name}" |
rb 或 br | 原始字节串 | rb"\x89PNG" |
f 不能和 b 组合,也不能和 u 组合。原因很简单:f-string 处理的是文本(str),b-string 处理的是字节(bytes),两者底层数据模型不同,没法混用。
总结
- 日常写代码:用
f - 路径和正则:用
r - 二进制数据:用
b - Python 3.15 以后:关注
t u不用管,认识就行
4. 本章小结
这一章我们从“字符串是什么”一路走到了“字符串能怎么玩”。回头看一下走过的路:
第0节:概念。 字符串是一串文本,Unicode 加持让你直接写中文,不可变意味着每次“修改”都是创建新字符串,有序序列意味着每个字符都有编号。
第1节:创建与拼接。 三种引号应对不同场景,转义字符解决冲突但能不用就不用,+ 和隐式拼接各有各的用处。
第2节:工具。 len() 量长度,in 查存在,find() 找位置,split() 切分,join() 拼接,strip() 去空白,replace() 替换,再加上索引和切片——这九样东西基本覆盖了你日常对字符串的所有操作。
第3节:特殊字符串。 r 留着反斜杠,t 是未来的模板,b 处理二进制,f 是日常首选,u 是历史遗迹。记住组合规则:r 是万能搭子,f 只搭 r,b 也只搭 r。
字符串的内容就到这里。它是你往后每一段代码都会遇到的东西,但它的核心规则并不多。记住一句话:字符串就是文本,工具都在本章,回来查就行。
5. 附录
5.1 转义字符速查表
| 转义写法 | 实际字符 |
|---|---|
\' | 单引号 |
\" | 双引号 |
\\ | 反斜杠 |
\n | 换行 |
\t | Tab |
\r | 回车 |
5.2 常用字符串方法速查表
| 方法/操作 | 作用 | 示例 | 结果 |
|---|---|---|---|
len(s) | 长度 | len("你好") | 2 |
s.find(sub) | 查找位置 | "hello".find("e") | 1 |
s.split(sep) | 切分 | "a,b".split(",") | ["a","b"] |
sep.join(list) | 拼接 | ",".join(["a","b"]) | "a,b" |
s.strip() | 去首尾空白 | " hi ".strip() | "hi" |
s.replace(a,b) | 替换 | "hi".replace("i","o") | "ho" |
5.3 字符串前缀速查表
| 前缀 | 全称 | 作用 |
|---|---|---|
f | formatted | 嵌入变量和表达式,立即求值 |
r | raw | 反斜杠原样保留,不转义 |
t | template | 返回 Template 对象,延迟处理(3.15+) |
b | bytes | 生成 bytes 对象而非 str |
u | Unicode | Python 3 中无实际作用 |
5.4 前缀组合规则速查表
| 组合 | 含义 | 示例 |
|---|---|---|
rf / fr | 原始格式化字符串 | rf"C:\{name}" |
rb / br | 原始字节串 | rb"\x89PNG" |
| 组合 | 是否允许 |
|---|---|
f + r | 可以 |
f + b | 不可以 |
f + u | 不可以 |
b + r | 可以 |
b + u | 不可以 |
5.5 索引与切片速查表
| 写法 | 含义 | 示例 | 结果 |
|---|---|---|---|
s[i] | 取第 i 个字符 | "Hello"[1] | "e" |
s[-i] | 取倒数第 i 个字符 | "Hello"[-1] | "o" |
s[start:end] | 取 start 到 end-1 | "Hello"[1:4] | "ell" |
s[start:] | 取 start 到末尾 | "Hello"[2:] | "llo" |
s[:end] | 取开头到 end-1 | "Hello"[:3] | "Hel" |
s[start:end:step] | 带步长切片 | "Hello"[::2] | "Hlo" |
s[::-1] | 倒序 | "Hello"[::-1] | "olleH" |
5.6 f-string 格式控制速查表
| 用途 | 写法 | 示例 | 结果 |
|---|---|---|---|
| 保留两位小数 | {x:.2f} | 3.14159 | 3.14 |
| 左对齐,宽度10 | {x:<10} | "Hi" | "Hi " |
| 右对齐,宽度10 | {x:>10} | "Hi" | " Hi" |
| 居中,宽度10 | {x:^10} | "Hi" | " Hi " |
| 填充星号居中 | {x:*^10} | "Hi" | "****Hi****" |
| 千位逗号分隔 | {x:,} | 1234567 | "1,234,567" |
| 显示正负号 | {x:+} | 42 | "+42" |
| 百分比 | {x:.0%} | 0.875 | "88%" |
| 二进制 | {x:b} | 255 | "11111111" |
| 十六进制(带前缀) | {x:#x} | 255 | "0xff" |
| 科学计数法 | {x:.2e} | 12345678.9 | "1.23e+07" |
| 日期 | {%Y-%m-%d} | datetime.now() | "2026-08-11" |
5.7 引号与转义速查表
| 场景 | 推荐写法 | 说明 |
|---|---|---|
| 普通文本 | "hello" | 双引号为主力 |
| 含单引号 | "it's ok" | 双引号避免转义 |
| 含双引号 | '他说"你好"' | 单引号避免转义 |
| 多行文本 | """...""" | 保留换行和缩进 |
| Windows 路径 | r"C:\Users" | 用 r-string 避免转义 |
| 正则表达式 | r"\d{3}" | 用 r-string 避免转义 |
5.8 类型与互转速查表
| 函数 | 作用 | 示例 | 结果 |
|---|---|---|---|
str() | 转成字符串 | str(123) | "123" |
int() | 转成整数 | int("42") | 42 |
float() | 转成浮点数 | float("3.14") | 3.14 |
bool() | 转成布尔 | bool("") | False |
encode() | 字符串→字节 | "你好".encode() | b'\xe4\xbd\xa0' |
decode() | 字节→字符串 | b'\xe4\xbd\xa0'.decode() | "你好" |
5.9 字符串常用方法速查表(完整版)
| 方法 | 作用 | 示例 | 结果 |
|---|---|---|---|
s.count(sub) | 统计子串出现次数 | "hello".count("l") | 2 |
s.startswith(prefix) | 是否以某字符串开头 | "hello".startswith("he") | True |
s.endswith(suffix) | 是否以某字符串结尾 | "hello".endswith("lo") | True |
s.upper() | 转大写 | "hi".upper() | "HI" |
s.lower() | 转小写 | "HI".lower() | "hi" |
s.title() | 每个单词首字母大写 | "hello world".title() | "Hello World" |
s.capitalize() | 首字母大写 | "hello".capitalize() | "Hello" |
s.swapcase() | 大小写互换 | "Hello".swapcase() | "hELLO" |
s.isdigit() | 是否全为数字 | "123".isdigit() | True |
s.isalpha() | 是否全为字母 | "abc".isalpha() | True |
s.isalnum() | 是否全为字母或数字 | "abc123".isalnum() | True |
s.isspace() | 是否全为空白字符 | " ".isspace() | True |
s.lstrip() | 去除左侧空白 | " hi ".lstrip() | "hi " |
s.rstrip() | 去除右侧空白 | " hi ".rstrip() | " hi" |
s.center(width) | 居中,两边填充空格 | "hi".center(5) | " hi " |
s.ljust(width) | 左对齐,右侧填充空格 | "hi".ljust(5) | "hi " |
s.rjust(width) | 右对齐,左侧填充空格 | "hi".rjust(5) | " hi" |
s.zfill(width) | 右侧对齐,左侧补零 | "42".zfill(5) | "00042" |
s.join(iterable) | 用 s 拼接可迭代对象 | "-".join(["a","b"]) | "a-b" |
s.split(sep) | 按 sep 切分 | "a,b".split(",") | ["a","b"] |
s.rsplit(sep) | 从右侧开始切分 | "a,b,c".rsplit(",",1) | ["a,b","c"] |
s.partition(sep) | 按 sep 分成三部分 | "a-b-c".partition("-") | ("a","-","b-c") |
s.rpartition(sep) | 从右侧按 sep 分成三部分 | "a-b-c".rpartition("-") | ("a-b","-","c") |
s.find(sub) | 查找子串位置,找不到返回 -1 | "hello".find("l") | 2 |
s.rfind(sub) | 从右侧查找子串位置 | "hello".rfind("l") | 3 |
s.index(sub) | 查找子串位置,找不到报错 | "hello".index("e") | 1 |
s.rindex(sub) | 从右侧查找,找不到报错 | "hello".rindex("l") | 3 |
s.replace(old, new) | 替换所有匹配项 | "hi".replace("i","o") | "ho" |
s.strip() | 去除首尾空白 | " hi ".strip() | "hi" |
s.removeprefix(prefix) | 移除前缀 | "hello".removeprefix("he") | "llo" |
s.removesuffix(suffix) | 移除后缀 | "hello".removesuffix("lo") | "hel" |
下一章,我们进入条件判断与循环。下一章,我们进入条件判断与循环。它们让程序有了“脑子”——能根据情况做决定,能重复干活不嫌累。
by 澜湾
by LanWan