一、字符串是什么
字符串就是一段文字。只要用引号包起来,不管是中文、英文、数字还是符号,都算字符串。
它是我们打交道最多的一种类型——网页上的内容、表格里的数据、用户输入的东西,拿到手里几乎都是字符串。
1.1 三种引号,随便挑
a = "小满" # 双引号
b = '小满' # 单引号,和双引号完全一样
c = "他说:'你好'" # 里面是单引号,外面就用双引号
d = '他说:"你好"' # 里面是双引号,外面就用单引号
还有一种情况很常见:文字里面本来就有引号时,外用另一种引号就行,不用操心转义。
还有第三种:三引号,专门用来写跨好几行的文字。
e = """第一行
第二行
第三行"""
print(e)
# 输出:
# 第一行
# 第二行
# 第三行
1.2 数一数有几个字:len()
print(len("Python")) # 6
print(len("你好世界")) # 4 一个汉字算 1 个字符
print(len(" ab ")) # 6 空格也算一个字符
len() 数的是字符个数,不是字节数。一个汉字算 1 个,所以“你好世界”是 4 而不是 8。这一点在检查输入长度时会用到。1.3 问一句“里面有没有”:in
s = "Python编程"
print("thon" in s) # True 包含这一段
print("Java" in s) # False 不包含
print("Py" not in s) # False 反过来问“不包含”
print("p" in s) # False 注意大小写敏感:小写 p 不在里面
in 是判断连着的几个字符有没有出现,不只是单个字。"thon" in "Python" 也是 True。要特别注意它是区分大小写的。1.4 字符串改不了(很重要)
字符串属于不可变类型:创建出来之后,里面任何一个字符都不能单独修改。
s = "hello"
s[0] = "H"
# 报错:TypeError: 'str' object does not support item assignment
想改只能重新拼一个新字符串,再让变量指向它:
s = "hello"
s = "H" + s[1:] # 取第 1 个到最后,前面接个 H
print(s) # Hello
s.upper() 写完 s 本身没变,想用结果得接住——s2 = s.upper()。后面第六、七节的方法全都遵守这条。二、引号与转义字符
有些字符没法直接打出来,比如“换行”“制表符”;还有些字符在字符串里有特殊身份,比如引号本身。这时候就要用反斜杠 \ 加一个字母来表示它,这叫转义字符。
| 写法 | 名字 | 作用 | 实际效果 |
|---|---|---|---|
\n | 换行 | 从这里换到下一行 | 相当于按一次回车 |
\t | 制表符 | 空出一段,用来对齐 | 相当于按一次 Tab |
\\ | 反斜杠 | 表示一个反斜杠本身 | 显示为 \ |
\" | 双引号 | 表示一个双引号本身 | 显示为 " |
\' | 单引号 | 表示一个单引号本身 | 显示为 ' |
print("第一行\n第二行") # \n 在这里换行
print("姓名\t年龄") # \t 空一段,用来对齐
print("C:\\Users\\笔记") # \\ 表示一个反斜杠
print("他说:\"你好\"") # \" 表示引号本身
运行结果:
第一行
第二行
姓名 年龄 ← 这里是一个 Tab(制表符),不是一串空格
C:\Users\笔记
他说:"你好"
"C:\new\test",里面的 \n 会被当成“换行”、\t 会被当成“Tab”,结果路径被切得乱七八糟。print("C:\new\test")
# 实际输出(已经不是你想要的路径了):
# C:
# ew est
这个问题有个更省事的解法:在引号前面加个 r,让反斜杠恢复成普通字符。这是第 11 节的内容,先记住有这么回事。
课程思政|工匠精神与严谨求实 · 看不见的字符最惹麻烦
反斜杠加一个字母,就变成了「换行」「制表」这些看不见的角色。打印一个 C:\new\test 路径,\n、\t 会悄悄把路径切得七零八落,程序还不报错。越是看不见的地方,越要盯紧;出了怪结果先怀疑这些隐身的字符,是程序员该有的警觉。
三、索引:取出一个字符
字符串是一串排好队的字符,每个字符都有一个位置编号。用 方括号 + 编号 就能把它单独取出来,这叫索引。
编号规则只有两条:从左往右从 0 开始,从右往左从 -1 开始。
| 字符 | P | y | t | h | o | n |
| 从左数(正索引) | 0 | 1 | 2 | 3 | 4 | 5 |
| 从右数(负索引) | -6 | -5 | -4 | -3 | -2 | -1 |
上表是 s = "Python" 的编号表。记不住从 0 开始,是新手最常犯的错。
s = "Python"
print(s[0]) # P 第 0 个(也就是第一个)
print(s[1]) # y 第 1 个
print(s[5]) # n 最后 1 个(一共 6 个字符,编号到 5 为止)
print(s[-1]) # n 从右数第 1 个,也就是最后一个
print(s[-2]) # o 倒数第二个
s[-1] 是“取最后一个字符”的标准写法,非常好用——字符串多长都不用管。取第 1 个字符就写 s[0]、取最后一个就写 s[-1],别看元素总数。3.1 越界会报错
编号超出范围就会报错:
s = "Python"
print(s[6])
# 报错:IndexError: string index out of range
IndexError 的意思是“编号超出范围了”。"Python" 只有 6 个字符、编号是 0~5,写 s[6] 就多了一位。看到这个报错,先数一遍字符个数,再看看是不是把“从 1 开始”的直觉带进来了。3.2 实战:从身份证号里取一位
身份证号是 18 位的字符串,每一位都有含义。想单独看某一位,索引最直接——比如第 17 位表示性别(奇数男、偶数女):
id_no = '440583198704201234'
print('第 17 位(性别位):', id_no[16])
print('倒数第 2 位:', id_no[-2])
print('最后一位(校验码):', id_no[-1])
输出:
第 17 位(性别位): 3
倒数第 2 位: 3
最后一位(校验码): 4
id_no[16] 和 id_no[-2] 取到的是同一个字符:「第 17 位」的下标是 16,差一位不报错、只会悄悄取错,这是这一段最常见的坑。想判性别还差一步,把字符转成整数再看奇偶:
id_no = '440583198704201234'
print('男' if int(id_no[16]) % 2 else '女')
输出:
男
还有个细节:最后一位可能是字母 X(代表校验值 10),所以它不能直接当数字用。完整的位序表、出生日期怎么切、X 怎么处理,都在《18-切片操作》第九节。
四、切片:取出一段
索引一次只能取一个字符。想取一串,就用切片。写法是在方括号里用冒号分开三个数字:
字符串[起始位置 : 结束位置 : 步长]
最重要的一条规则:含头不含尾。起始位置要,结束位置不要。
4.1 先记住“含头不含尾”
data = "0123456789"
print(data[2:5]) # 234 取编号 2、3、4,不含 5
print(data[0:3]) # 012 取编号 0、1、2,不含 3
print(data[-3:]) # 789 取最后三个
4.2 三种省略写法
| 写法 | 含义 | 例子 | 结果 |
|---|---|---|---|
s[start:end] | 从 start 取到 end(不含 end) | "0123456789"[2:5] | 234 |
s[:end] | 从头取到 end(不含 end) | "0123456789"[:3] | 012 |
s[start:] | 从 start 一直取到最后 | "0123456789"[7:] | 789 |
s[:] | 整段照抄一份 | "0123456789"[:] | 0123456789 |
4.3 步长:隔几个取一个
第三个数字是步长,默认是 1(一个一个取)。改成 2 就是每隔一个取一个:
s = "0123456789"
print(s[::2]) # 02468 从头到尾,隔一个取一个
print(s[1::2]) # 13579 从 1 开始,隔一个取一个
print(s[1:8:2]) # 1357 从 1 取到 7,隔一个取一个
s[2:5] 和 s[2:5:1] 完全一样。日常用得最多的是 [::2](隔一个取)和下面的 [::-1](反转)。4.4 反转:把整串倒过来
步长写成 -1,就是从后往前一个一个取,等于把字符串倒过来。这是 Python 里最简洁的反转写法:
s = "Python"
print(s[::-1]) # nohtyP 整个倒过来
print(s[::-2]) # nhy 倒着取,隔一个取一个
"上海自来水来自海上"),一行就够:
word = "上海自来水来自海上"
print(word == word[::-1]) # True
[::-1] 对列表也管用,第 8 篇里反转列表用的就是它。4.5 实战:从身份证号里切出一段
还是那个 18 位的号码:出生年月日在第 7~14 位,后四位是第 15~18 位。两段都是切片一行的事:
id_no = '440583198704201234'
print('出生年月日:', id_no[6:14])
print('后四位:', id_no[-4:])
输出:
出生年月日: 19870420
后四位: 1234
[6:14] 按「含头不含尾」取到下标 6~13,正好 8 位;[-4:] 是从右数第 4 位一直取到末尾,不用先知道号码多长。取出来的 8 位还是字符串,所以能接着往下切:
id_no = '440583198704201234'
birth = id_no[6:14]
print(f'{birth[0:4]}-{birth[4:6]}-{birth[6:8]}')
输出:
1987-04-20
切片的结果可以接着切、接着拼——这正是「切片返回的还是同类型」带来的方便。位序表和性别判断的完整写法见《18-切片操作》第九节。
五、连接与重复
两个动作最常用:加号 + 把两段接起来,乘号 * 把一段重复几遍。
print("珍珠" + "奶茶") # 珍珠奶茶 接起来
print("Hello" + ", " + "World") # Hello, World
print("-" * 10) # ---------- 重复 10 遍
print("哈" * 3) # 哈哈哈
print("Ab" * 4) # AbAbAbAb
5.1 想拼数字,必须先转成字符串
age = 18
print("我今年" + age + "岁")
# 报错:TypeError: can only concatenate str (not "int") to str
print("我今年" + str(age) + "岁") # 我今年18岁 ✓ 先转换
print(f"我今年{age}岁") # 我今年18岁 ✓ 更好的办法
str() 把别的东西变成字符串,这是最直接的解法。但如果要拼的内容比较多,用 f 字符串(第九节)会清爽得多,也不用一个个 str(),建议养成习惯。5.2 一个容易误会的地方:print 里的逗号
有些人看到 print 里用逗号也能把几样东西连起来,就以为逗号是“字符串拼接”。不是的。逗号是 print 的分隔符,它只是在输出时把几样东西依次打印、中间自动加个空格:
| 写法 | 本质是什么 | 结果 |
|---|---|---|
print("我今年", 18, "岁") | print 收到 3 个参数,依次打印、中间加空格 | 我今年 18 岁 |
print("我今年" + str(18) + "岁") | 先用 + 拼成一个字符串,再交给 print | 我今年18岁 |
msg = "我今年", 18 得到的是元组不是字符串。想存下来、想精确控制格式,就用 + 或 f 字符串。5.3 相邻的字符串会自动拼起来
还有一个语法小特性:两个字符串字面量挨着写,Python 会自动把它们拼成一个。
msg = "Py" "thon"
print(msg) # Python
"a" "b"(拼成 "ab")和 "a", "b"(元组)就差一个逗号,意思完全不同。课程思政|诚信与职业操守 · 一段一段拼,才拼得完整
加号能把两段文字接起来,乘号能把一段重复几遍,拼的时候还得先把数字转成字符串。团队做一份报告也是这样,各写各的一段,格式和口径统一了才接得上。谁也别图省事省掉自己那一段,一个人的疏漏,整份材料都得返工。
六、常用方法之一:大小写与清理
方法是“跟着字符串后面用点叫出来”的功能,写法是 字符串.方法名(参数)。下面这些是日常用得最多的。
先看改大小写的四个:
| 方法 | 作用 | 例子 | 结果 |
|---|---|---|---|
lower() | 全部变小写 | "Hello PY".lower() | hello py |
upper() | 全部变大写 | "hello py".upper() | HELLO PY |
capitalize() | 整个句子的首字母大写 | "hello python".capitalize() | Hello python |
title() | 每个单词的首字母都大写 | "hello python".title() | Hello Python |
再看清理两端空白的三个。名字里的 l 是 left(左),r 是 right(右):
| 方法 | 清掉哪儿 | 例子(原串带前后空格) | 结果 |
|---|---|---|---|
strip() | 两端都清 | " hi ".strip() | "hi" |
lstrip() | 只清左边 | " hi ".lstrip() | "hi " |
rstrip() | 只清右边 | " hi ".rstrip() | " hi" |
s = " Hello World "
print(s.lower()) # " hello world "
print(s.upper()) # " HELLO WORLD "
print(s.strip()) # "Hello World" 两端空格没了
print(s.lstrip()) # "Hello World " 只清左边
print(s.rstrip()) # " Hello World" 只清右边
print(s.title()) # " Hello World " 每个单词首字母大写
s,只是返回一个新字符串。s.strip() 写完 s 还是带空格的,必须写 s = s.strip() 才会真的生效——这是新手最容易漏的一步。strip() 在处理用户输入时是必备的。用户在 input() 里很容易多打一个空格,不清理就会影响后面的比较和转换:
name = input("请输入姓名:").strip() # 顺手清掉两端的空格
input().strip() 再指望原变量变了。七、常用方法之二:查找、替换、分割、合并
7.1 找位置:find() 和 count()
t = "珍珠,椰果,珍珠,布丁"
print(t.find("椰果")) # 3 返回它从第几个字符开始
print(t.find("西瓜")) # -1 找不到返回 -1(不会报错)
print(t.count("珍珠")) # 2 出现过几次
| 方法 | 作用 | 找不到时 |
|---|---|---|
find(x) | 返回 x 第一次出现的位置编号 | 返回 -1,不报错 |
index(x) | 同上 | 报 ValueError 让程序中断 |
count(x) | 数一数 x 出现了几次 | 返回 0 |
x in s | 判断有没有 x | 返回 False |
find() 更安全——找不到给你个 -1,程序还能往下走。index() 找不到直接崩,除非你确定它一定存在,否则别用。7.2 换内容:replace()
t = "珍珠,椰果,珍珠,布丁"
print(t.replace("珍珠", "西米")) # 西米,椰果,西米,布丁
print(t.replace("珍珠", "西米", 1)) # 西米,椰果,珍珠,布丁 只换第 1 处
replace() 默认全换;加上第三个参数可以限制只换前几处。它同样是返回新字符串,原串不动。7.3 分割:split()
把一个字符串按某个符号切开,变成列表。这是处理表格数据的第一步。
line = "奶茶,大杯,三分糖"
print(line.split(",")) # ['奶茶', '大杯', '三分糖']
data = "2026-09-20"
print(data.split("-")) # ['2026', '09', '20']
| 写法 | 含义 | 结果 |
|---|---|---|
"a,b,c".split(",") | 按逗号切 | ['a', 'b', 'c'] |
"a b c".split() | 不写参数:按空白切,并自动去掉连续空格 | ['a', 'b', 'c'] |
" a b ".split() | 不写参数时,两端空白自动丢掉 | ['a', 'b'] |
"a,b,,c".split(",") | 中间是空的,也会留下一项空字符串 | ['a', 'b', '', 'c'] |
"a-b-c".split("-", 1) | 限制只切 1 次 | ['a', 'b-c'] |
split() 按任意空白切、还顺手把多余空格吃掉,处理用户输入最省事;split(",") 是严格按逗号切,逗号之间是空的就是空字符串。看见结果里出现空字符串 '',通常是数据里多打了分隔符。7.4 合起来:join()
join() 是 split() 的反动作:把一个列表用某个分隔符合成一个字符串。
words = ["珍珠", "椰果", "布丁"]
print(" ".join(words)) # 珍珠 椰果 布丁 用空格连
nums = ["2026", "09", "20"]
print("-".join(nums)) # 2026-09-20 用横线连
print("".join(["奶", "茶"])) # 奶茶 不用分隔符就直接贴
join() 的写法容易记反:分隔符在前、列表在后。是 "-".join(列表),不是 列表.join("-")。另外,列表里的元素必须全是字符串,装的是数字会报错,要先用 str() 转一遍。print("-".join([1, 2, 3]))
# 报错:TypeError: sequence item 0: expected str instance, int found
print("-".join(str(n) for n in [1, 2, 3])) # 1-2-3 ✓ 先转成字符串
split() 和 join() 是一对,合起来能干很多活。比如把 "2026-09-20" 换成斜杠格式:
d = "2026-09-20"
print("/".join(d.split("-"))) # 2026/09/20
八、常用方法之三:判断类
这一类方法的共同点是:返回值是 True 或 False,专门用来检查字符串“符不符合某种样子”。常配合 if 使用。
| 方法 | 它问的是 | 是 True 的例子 | 是 False 的例子 |
|---|---|---|---|
isdigit() | 是不是全是数字 | "12345" | "abc123"、"12.5" |
isalpha() | 是不是全是字母 | "abc" | "abc123"、"ab c" |
isalnum() | 是不是字母或数字(都行) | "abc123" | "ab c"、"a-b" |
isspace() | 是不是全是空白 | " " | "a " |
startswith(x) | 是不是以 x 开头 | "hello".startswith("he") | "hello".startswith("lo") |
endswith(x) | 是不是以 x 结尾 | "hello".endswith("lo") | "hello".endswith("he") |
print("12345".isdigit()) # True 全是数字
print("abc123".isdigit()) # False 有字母
print("12.5".isdigit()) # False 有小数点!
print("abc123".isalpha()) # False 有数字
print("abc123".isalnum()) # True 字母数字都有,算通过
print("hello".startswith("he")) # True
print("hello".endswith("lo")) # True
"12.5".isdigit() 是 False,这是最坑的一条。因为它带小数点,而小数点不是“数字字符”。同样,带负号的 "-5".isdigit() 也是 False。所以别用 isdigit() 去验证“是不是合法的小数”——那种情况应该直接 try 一下能不能转成 float。isdigit() 很好用:
age_text = input("请输入年龄:").strip()
if age_text.isdigit():
age = int(age_text)
print(f"明年你 {age + 1} 岁")
else:
print("请输入纯数字,不要带小数点和符号")
AI 助手小贴士|字符串方法十几个,让 AI 收成一张速查表再挑出易错的。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:把 str 最常用的 12 个方法整理成表:方法名、作用、一行示例;再挑 3 个最容易拼错的方法提醒我
九、格式化:f 字符串(重点)
把变量塞进一句话里,最方便的办法就是在引号前面加一个 f,然后在句子里用花括号 {} 把它圈起来。这就是 f 字符串(f 是 format 的缩写)。
name = "小满"
age = 18
print(f"我的名字是{name},今年{age}岁")
# 我的名字是小满,今年18岁
str():
f"我今年{age}岁" # 清爽
"我今年" + str(age) + "岁" # 繁琐
f 必须紧贴在引号前面,中间不能有空格,写成 f "..." 是错的。9.1 花括号里能放什么
只要是一段能算出结果的表达式,都可以放进去:
| 放什么 | 写法 | 结果 |
|---|---|---|
| 变量 | f"你好{name}" | 你好小满 |
| 算式 | f"明年{age + 1}岁" | 明年19岁 |
| 函数调用 | f"名字长度{len(name)}" | 名字长度2 |
| 方法调用 | f"大写{name.upper()}" | 大写小满 |
score = 92.56789
print(f"明年我就{age + 1}岁了") # 花括号里直接算
print(f"名字有{len(name)}个字") # 花括号里调用函数
9.2 控制显示的格式(很有用)
在变量后面加个冒号,再写格式要求,就能控制小数位、对齐、千分位这些。写法是 {变量:格式}:
| 格式 | 作用 | 例子 | 结果 |
|---|---|---|---|
.2f | 保留 2 位小数 | f"{92.56789:.2f}" | 92.57 |
.1f | 保留 1 位小数 | f"{92.56789:.1f}" | 92.6 |
.0f | 不要小数 | f"{92.56789:.0f}" | 93 |
.1% | 变成百分比 | f"{0.856:.1%}" | 85.6% |
, | 加千分位逗号 | f"{1234567:,}" | 1,234,567 |
05d | 补零到 5 位(整数) | f"{50:05d}" | 00050 |
>6 | 右对齐,宽度 6 | f"[{name:>6}]" | [ 小满] |
<6 | 左对齐,宽度 6 | f"[{name:<6}]" | [小满 ] |
^6 | 居中,宽度 6 | f"[{name:^6}]" | [ 小满 ] |
score = 92.56789
price = 50
total = 1234567
print(f"得分:{score:.2f}") # 得分:92.57 保留 2 位小数
print(f"得分:{score:.1f}") # 得分:92.6
print(f"正确率:{0.856:.1%}") # 正确率:85.6% 变成百分比
print(f"总额:{total:,}") # 总额:1,234,567 加千分位
print(f"编号:{price:05d}") # 编号:00050 补零到 5 位
.2f 几乎是必写的——不然会打印出 21.77777777777778 这种一长串。写 :.2f 就干净了。9.3 想输出花括号本身
因为 {} 有特殊用途,想真的打印出花括号,要写两个:
print(f"{{}}") # {} 两个花括号变成一个字面花括号
print(f"{{{age}}}") # {18} 外面两个当字面、里面那个还是变量
课程思政|科技自立自强 · 新工具要学,老代码要读
f 字符串让变量直接嵌进句子里,比一堆引号和加号清爽太多,这是新版本 Python 给我们的好工具。好工具要主动学,旧写法也得看得懂——技术一直在往前走,愿意换新工具、也肯回头读老代码的人,才不会被时代落下。
十、老写法:.format() 与 %
f 字符串是较新的 Python 3 版本 才有的。在那之前,大家用另外两种写法。你自己写代码请优先用 f 字符串,但看别人的代码、或者遇到老教材时会碰到这两种,认识就行。
下面同一个例子的三种写法对照:
| 写法 | 代码 | 说明 |
|---|---|---|
| f 字符串(推荐) | f"我是{name},今年{age}岁" | {} 里直接写变量,最直观 |
.format() | "我是{},今年{}岁".format(name, age) | 用 {} 占位,参数按顺序填 |
% | "我是%s,今年%d岁" % (name, age) | 老式写法,类似 C 语言 |
name = "小满"
age = 19
score = 92.56789
# 写法一:f 字符串(推荐)
print(f"我是{name},今年{age}岁,得分{score:.2f}")
# 写法二:.format()
print("我是{},今年{}岁。".format(name, age))
print("我是{nm},今年{ag}岁。".format(ag=age, nm=name)) # 也能起名字,顺序可换
print("得分:{:.2f}".format(score))
# 写法三:% 占位符
print("我是%s,今年%d岁。" % (name, age))
print("得分:%.2f" % score)
| % 占位符 | 对应的类型 |
|---|---|
%s | 字符串(也能塞别的,会自动转成字符串) |
%d | 整数 |
%.2f | 浮点数,保留 2 位小数 |
.format() 和 % 只需要能看懂——看到 %s 知道那是“这里要填个字符串”,看到 {0} 知道那是“填第一个参数”。十一、r:原始字符串
回到第二节留下的问题:写文件路径时,路径里的 \n、\t 会被当成转义符,把路径搞得面目全非。
print("C:\new_folder\temp")
# 你以为会打印路径,实际打印出:
# C:
# ew_folder temp
解决的第一个办法是把每个反斜杠都写成两个(\\),但这又长又容易漏。
更省事的办法是在引号前面加一个 r。加了 r 之后,字符串里的反斜杠就变回普通字符,什么转义都不认了——这就是原始字符串(r 是 raw 的缩写)。
| 写法 | 代码 | 打印出来 |
|---|---|---|
| 不加 r | "C:\new\temp" | 被吃掉了,变成换行和 Tab |
| 加 r | r"C:\new\temp" | C:\new\temp ✓ 原样显示 |
| 手动双反斜杠 | "C:\\new\\temp" | C:\new\temp ✓ 但要写一倍的多,容易漏 |
# 加了 r,\n 和 \t 就是普通的两个字符
print(r"C:\new_folder\temp") # C:\new_folder\temp
# 写文件路径:直接原样抄路径就行,不用一个个加反斜杠
path = r"C:\Users\小满\Desktop\笔记.txt"
print(path) # C:\Users\小满\Desktop\笔记.txt
r。直接从“地址栏”或者“复制文件路径”里粘过来,前面补个 r 就完事,不用管里面有几个 \、也不用担心哪个字母组合会被当转义符。11.1 r 也有管不到的地方
有一条限制:原始字符串不能以单个反斜杠结尾,因为那个反斜杠会把结尾的引号“挡住”,Python 就不知道字符串在哪结束了。
p = r"C:\Users\" # 报错 SyntaxError: unterminated string literal
# 解决办法:单独拼一个反斜杠,或者干脆不加 r 用双反斜杠
p = r"C:\Users" + "\\"
11.2 r 还常用在哪儿
另一个高频场景是正则表达式——里面全是反斜杠,不加 r 会疯掉。不过正则属于进阶内容,这里知道“r 是为大量反斜杠准备的”就够了。
import re
# 正则里 \d 表示数字,不加 r 就要写成 \\d
print(re.findall(r"\d+", "订单号 20260920"))
# ['20260920']
十二、f 和 r 一起用(fr)
两个前缀解决的问题不一样:f 管“能不能嵌变量”,r 管“反斜杠要不要转义”。它们可以叠着写,写成 fr"..." 或 rf"...",两种顺序都行。
| 前缀 | 解决的问题 | 路径里的反斜杠 | {} 能嵌变量吗 |
|---|---|---|---|
| 不加 | —— | 会被当转义符,出问题 | 不能 |
f | 能嵌入变量和表达式 | 仍然会被转义 | 能 |
r | 反斜杠不转义 | 原样保留 | 不能 |
fr / rf | 两个都要 | 原样保留 | 能 |
看实际效果——变量是文件夹名,路径又带反斜杠,正好需要两个一起:
folder = "文档"
print(f"C:\temp\{folder}.csv")
# ✗ 出事:\t 是「跳到下一个制表位」,路径里凭空多了一个 Tab
print(r"C:\temp\{folder}.csv")
# ✗ 也不行:原样输出,{folder} 不会被替换成"文档"
print(fr"C:\temp\{folder}.csv")
# ✓ 正确:C:\temp\文档.csv
folder = "文档"
path1 = fr"C:\Users\小满\Desktop\{folder}\报告.csv"
path2 = rf"C:\Users\小满\Desktop\{folder}\报告.csv"
print(path1) # C:\Users\小满\Desktop\文档\报告.csv
print(path2) # C:\Users\小满\Desktop\文档\报告.csv
fr 和 rf 都是“既要嵌入变量、又要反斜杠不转义”,不用纠结哪个对。前面说的不能以单个反斜杠结尾的限制,fr 一样适用。大写 FR、Rf 也照样能跑,但习惯上小写更常见。12.1 一个容易忘的细节
用了 fr 之后,{} 仍然会被当成变量占位符。如果路径里真的有个花括号(很少见),还是要写成两个:
folder = "文档"
print(fr"C:\data\{{临时}}\{folder}.txt") # C:\data\{临时}\文档.txt
十三、综合例子
把前面学的串起来,做两个贴近实际的练习。
13.1 处理一行表格数据
假设从文件里读进来一行学生数据,格式是 学号,姓名,成绩,但里面有些地方多打了空格,需要整理出来:
line = " 2026001, 小满 ,85.5 "
# 第一步:先清掉两端多余的空白
line = line.strip()
print(f"清理后:{line!r}") # '2026001, 小满 ,85.5'
# 第二步:按逗号切开
parts = line.split(",")
print(f"切开后:{parts}") # ['2026001', ' 小满 ', '85.5']
# 第三步:把每一项两端空格清掉
sid = parts[0].strip()
name = parts[1].strip()
score = float(parts[2].strip()) # 成绩要能算,转成小数
# 第四步:格式化输出
print(f"学号:{sid}")
print(f"姓名:{name}")
print(f"成绩:{score:.1f}")
print(f"是否及格:{score >= 60}")
运行结果:
清理后:'2026001, 小满 ,85.5'
切开后:['2026001', ' 小满 ', '85.5']
学号:2026001
姓名:小满
成绩:85.5
是否及格:True
strip() → split() → 逐项 strip() → 该转的转。注意第三步里每一项都单独 strip() 了一遍,因为外层的 strip() 管不到逗号里面的空格。13.2 生成带变量的文件路径
name = "小满"
year = 2026
month = 9
filename = fr"D:\报表\{year}年\{month}月\{name}的工资表.csv"
print(filename)
# D:\报表\2026年\9月\小满的工资表.csv
# 想补零成 09 月,用格式规格
filename2 = fr"D:\报表\{year}年\{month:02d}月\{name}的工资表.csv"
print(filename2)
# D:\报表\2026年\09月\小满的工资表.csv
{month:02d} 的意思是“按整数补零到 2 位”,9 就变成 09。生成文件名、编号、日期的时候很常用。13.3 统计一句话里的信息
sentence = "Python 是一种很好用的编程语言"
print(f"总长度:{len(sentence)}")
print(f"有无空格:{sentence.count(' ')} 个")
print(f"是否以 Python 开头:{sentence.startswith('Python')}")
# 按空格切开,看看有几个词
words = sentence.split(" ")
print(f"词数:{len(words)}")
print(f"第一个词:{words[0]}")
# 倒过来看
print(f"倒过来:{sentence[::-1]}")
print(f"前 6 个字:{sentence[:6]}")
AI 助手小贴士|字符串处理是练手感的好题材,让 AI 出题并逐行点评。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:出 3 道字符串处理小题:统计元音字母个数、把手机号中间四位换成星号、判断回文。我写完你逐行点评
课程思政|数据安全与法治素养 · 清洗别人数据,更要守规矩
这一节的例子是把一行 学号,姓名,成绩 里的多余空格清理干净,看着只是做整理,手里握的却是别人的信息。数据能随手处理,不等于能随手传播;练手时用假数据、把真实的姓名和联系方式脱敏,是数据从业者的底线。
十四、常见问题
下面这些报错和坑,初学阶段几乎每个人都会碰一遍。看到眼熟的那条,照着“怎么办”改就行。
| 现象 / 报错 | 原因 | 怎么办 |
|---|---|---|
TypeError: can only concatenate str (not "int") to str | 字符串和数字直接用 + 拼 | 先 str(n) 转换,或用 f 字符串 f"年龄{age}" |
IndexError: string index out of range | 索引超出范围(比如 6 个字符却取 s[6]) | 编号是 0 ~ len(s)-1;取最后一个用 s[-1] |
TypeError: 'str' object does not support item assignment | 想改字符串里的某个字符 | 字符串不可变。重新拼一个:s = "H" + s[1:] |
len(5) 报 TypeError: object of type 'int' has no len() | len() 只能量字符串、列表这些,不能量数字 | 先转成字符串:len(str(5)) |
s.upper() 写完 s 却没变 | 方法返回新串,不会改原串 | 接住返回值:s = s.upper()(strip() 同理) |
int("3.5") 报 ValueError | int() 不认小数点 | 两步走:int(float("3.5"));判断时也别用 isdigit() |
"12.5".isdigit() 是 False | 小数点不是“数字字符” | 别用 isdigit() 验证小数,直接 try 转 float |
"abc".index("z") 报 ValueError: substring not found | index() 找不到会报错 | 不确定就用 find(),找不到返回 -1 |
| 切片取多或取少了一位 | 忘了“含头不含尾” | s[0:5] 取到的是第 0~4 个,共 5 个字符 |
| 切片结果和预想差一个字符,多出空格 | 编号没数准(空格也占一位) | 把字符串和编号一起打出来核对:for i, ch in enumerate(s): print(i, repr(ch)) |
"-".join([1, 2, 3]) 报 TypeError | 列表里是数字,join 只接受字符串 | 先转:"-".join(str(n) for n in [1, 2, 3]) |
split() 结果里出现空字符串 '' | 数据里多打了分隔符(如 "a,,b") | 清掉空项:parts = [p for p in parts if p] |
路径里的 \t、\n 被吃掉了 | 反斜杠被当成转义符 | 引号前加 r:写成 r"C:\new\test" |
f"C:\data\{name}.txt" 路径不对 | 只加了 f,反斜杠仍在转义 | 两个一起加:写成 fr"C:\data\{name}.txt" |
f"{{}}" 想输出花括号却报错/没输出 | 花括号是占位符,有特殊含义 | 字面花括号要写两个:f"{{}}" |
TypeError 就去查 type()(是不是字符串和数字混着用了),看到 IndexError 就去数编号,看到 ValueError 就去查要转换的内容合不合法。AI 助手小贴士|让 AI 给代码逐行加注释是它最擅长的活之一——连注释风格都能指定,这是「用 AI 学注释」最直接的练法。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:给下面这段字符串处理代码的每一行加上简洁的中文行注释,说明这行在做什么,不要复述代码本身:<粘贴代码>
十五、随堂自测(12 题)
点击选项即可判分,答错的题会立刻显示解析。做完全部题目会给出正确率和错题清单。
1关于字符串,下面哪句话是对的?
- A. 可以用
s[0] = "H"把第一个字符改掉 - B. 字符串里的字符可以随意增删
- C. 字符串一旦创建就不能修改,想改只能重新生成一个新字符串
- D. 只有用单引号定义的字符串才不可变
s[0] = "H" 会报 TypeError。而且 s.upper()、s.replace() 这些方法都不会改动原字符串,只是返回一个新的给你。2len("你好世界") 的结果是多少?
- A.
4 - B.
8 - C.
2 - D. 报错
len() 数的是字符个数,不是字节数。一个汉字算 1 个字符,所以“你好世界”是 4。空格也占一位,len(" ab ") 是 6。3s = "Python",s[::-1] 的结果是什么?
- A.
"Python" - B.
"Pto" - C.
"yhn" - D.
"nohtyP"
-1 表示从后往前一个一个取,就是把整串反过来。s[::2] 是隔一个取一个得 "Pto",s[1::2] 得 "yhn"。4" hello ".strip() 的结果是什么?
- A.
" hello "(没变化) - B.
"hello"(两端空格都去掉) - C.
"hello "(只去左边) - D.
" hello"(只去右边)
strip() 去掉两端的空白。只去左边用 lstrip()、只去右边用 rstrip()。处理用户输入时最常用它清掉多打的空格。5"苹果,香蕉,苹果".split(",") 的结果是什么?
- A.
"苹果香蕉苹果" - B.
"苹果 香蕉 苹果" - C.
['苹果', '香蕉', '苹果'] - D.
('苹果', '香蕉', '苹果')
split() 按你给的分隔符切开,返回的是列表(方括号),既不是字符串也不是元组。这是读 CSV 表格数据的第一步。6有一批日期 ["2026", "09", "20"],想拼成 "2026-09-20",怎么写?
- A.
"-".join(["2026", "09", "20"]) - B.
["2026", "09", "20"].join("-") - C.
"-".split(["2026", "09", "20"]) - D.
"-" + ["2026", "09", "20"]
join() 是用分隔符去连接一个列表,写法是 分隔符.join(列表)——分隔符在前、列表在后。B 把顺序写反了,C 用错了方法(split 是切的),D 字符串不能直接和列表相加。7f"{3.14159:.2f}" 的结果是什么?
- A.
"3.14159" - B.
"3.1" - C.
"3.142" - D.
"3.14"
:.2f 表示保留 2 位小数,结果是 3.14。:.1f 是 1 位、:.0f 是不留小数。算平均分、BMI、折扣率时几乎都要写上它。8想判断用户输入的一串字符是不是全是数字,用哪个方法?
- A.
"12345".isalpha() - B.
"12345".isdigit() - C.
"12345".isalnum() - D.
"12345".startswith("1")
isdigit() 判断是不是全是数字。isalpha() 是判断全是字母,isalnum() 是字母或数字都算通过(所以 "abc123".isalnum() 也是 True),startswith() 只看开头第一个字符。9Windows 路径 "C:\new\test" 里的 \n、\t 老被当成转义符,在引号前加什么能解决?
- A.
f - B.
b - C.
r - D.
u
r 是 raw(原始)的缩写,加了它字符串里的反斜杠就是普通字符,\n 不会再变成换行。f 是管嵌入变量的,解决不了转义问题。10变量 name = "统计",想拼出路径 C:\data\统计.csv,下面哪种写法最好?
- A.
fr"C:\data\{name}.csv" - B.
f"C:\data\{name}.csv" - C.
r"C:\data\{name}.csv" - D.
f"C:\\data\\{name}.csv"
fr 把两个前缀的好处合起来:f 让 {} 能嵌入变量,r 让反斜杠不被转义。B 只加 f,路径里若出现 \t、\n 这类真转义(比如 \temp)就会出问题;C 只加 r,{} 不会被替换成变量值;D 要手写双反斜杠,容易漏。11身份证号 id_no = '440583198704201234',id_no[-2] 取到的是哪个字符?
- A.
'4'(最后一位校验码) - B.
'3'(倒数第二位,也就是性别位) - C.
'2'(倒数第四位) - D. 报
IndexError
'4',-2 就是倒数第二位 '3',也正是第 17 位的性别位(奇数男、偶数女)。18 位号码用 -1、-2 这样的下标一定不会越界。1218 位的身份证号,写 id_no[-4:] 取到的是哪几位?
- A. 前四位
- B. 出生年份
- C. 整个号码
- D. 后四位
[-4:] 表示「从右数第 4 位开始,一直取到末尾」,也就是后四位(第 15~18 位)。等价的正索引写法是 [14:]——负索引的好处是不用先数总长度。