第11章-字符串


上周末你考完第一次周测,已经能独立跑通几个小程序了。从今天起,我们正式进入 Python 的数据世界,第一站是字符串。

字符串这东西你其实天天见:微信里发给朋友的话、程序里弹出来的"请输入姓名"、网页上显示的标题,全是字符串。而程序最常干的活,也就是处理文字——把用户输入的名字转成大写、把一段话里的词替换掉、把一句话按空格拆成一个个单词。这些操作 Python 都有现成的工具,今天就把它们一个个用熟。

学完今天,你就有了一个"文字加工厂":取一个字、截一段、改一改、拆一拆,随手就能干。

字符串就是带引号的一串文字

在 Python 里,用引号包起来的一串字符就叫字符串(英文 str)。单引号、双引号都行:

name = "小明"          # 双引号包起来的文字
city = '北京'          # 单引号也可以
year = "2025"          # 注意:这是字符串,不是数字
print(name, city, year)   # 小明 北京 2025
print(type(name))      # <class 'str'>,说明它是字符串类型

先看一个最容易搞混的地方:数字加上引号,就不是数字了

s1 = "你好,世界"
s2 = 'Hello'
s3 = "123"                 # 加引号就是字符串
s4 = 123                   # 不加引号才是数字

print(type(s1))            # <class 'str'>
print(type(s3))            # <class 'str'>
print(type(s4))            # <class 'int'>
print(s1 + s2)             # 你好,世界Hello(字符串拼接,后面讲)

输出长这样:

<class 'str'>
<class 'str'>
<class 'int'>
你好,世界Hello

123 是数字,能拿去做加减法;"123" 是三个字符组成的文字,拿去加减法会直接报错。这个区分以后会反复遇到,现在就记牢。

写字符串还有几条规矩,都是新手常踩的:

  • 引号必须成对:用 " 开头就得用 " 结尾,"abc' 这样混着写,直接报 SyntaxError
  • 字符串里想写引号,外层用双引号、内层用单引号,比如 "他说'你好'",两边不冲突。

字符串里的每个字符——字母、数字、空格、标点——都有自己固定的位置,就像排队的人都有自己站的位子。下面我们就给这些位子编号。

给每个字符编上号:索引

字符串里每个字符都有编号,用 字符串[编号] 就能取出那一个字符。这个编号叫索引(index),两条规则必须记牢:

规则 说明 例子
正数索引 从 0 开始数,第 1 个字符是 0 "abc"[0]a
负数索引 从 -1 开始往左数,最后一个字符是 -1 "abc"[-1]c
s = "Python"
#    P y t h o n
#    0 1 2 3 4 5   正数下标
#   -6-5-4-3-2-1   负数下标

print(s[0])      # P(第 1 个字符)
print(s[3])      # h(第 4 个字符)
print(s[5])      # n(最后一个字符)
print(s[-1])     # n(负数从右往左数,-1 就是最后一个)
print(s[-2])     # o

输出:

P
h
n
n
o

这里最容易错的就是"下标从 0 开始":"abc"[1]b,不是 a。第一次写错很正常,多取几次就习惯了。负数下标从 -1 开始,没有"负 0"这种说法——-0 就等于 0,也就是第一个字符。

拿真实场景练一下,比如 11 位手机号:

phone = "13800138000"
# 下标:  0 1 2 3 4 5 6 7 8 9 10

print(phone[0])      # 1(第 1 位)
print(phone[2])      # 8(第 3 位)
print(phone[-1])     # 0(最后 1 位)
print(len(phone))    # 11(一共 11 个字符,len 是长度)

输出:

1
8
0
11

len(字符串) 返回字符个数。它还是排查越界报错的好帮手:下标最多到 len - 1,取 s[len(s)] 会报 IndexError: string index out of range。拿不准长度时,先 len() 一下再取。

取出中间的一段:切片

索引取一个字符,切片取一段。写法是 字符串[开始:结束],把开始和结束的位置填进去就行。

切片只有一条铁律:含头不含尾——开始位置取得到,结束位置取不到。这跟 range(1, 6) 产出 1~5 是同一个道理。

s = "Hello Python"

print(s[0:5])     # Hello(第 0 到第 4 个,第 5 个不取)
print(s[6:12])    # Python(第 6 到第 11 个)
print(s[6:])      # Python(省略结束,表示一直取到末尾)
print(s[:5])      # Hello(省略开始,表示从 0 开始)
print(s[:])       # Hello Python(都不写,就是复制整个字符串)

输出:

Hello
Python
Python
Hello
Hello Python

[开始:结束] 还能再加第三个位置——步长,隔几个取一个:

s = "0123456789"

print(s[2:6])        # 2345(含头不含尾)
print(s[-4:])        # 6789(从倒数第 4 个取到末尾)
print(s[0:10:2])     # 02468(步长 2,隔一个取一个)
print(s[::-1])       # 9876543210(步长 -1,整个倒过来!)

输出:

2345
6789
02468
9876543210

[::-1] 是"从后往前全取",字符串倒序就靠它。两个容易混的地方:

  • 切片的结束位置取不到:"Hello"[0:4] 结果是 Hell,不是 Hello
  • 切片越界不报错"abc"[0:99] 返回 "abc"。这一点和索引正好相反——索引越界报错,切片越界不报错。

加工文字的小工具:方法

Python 给字符串配了一堆现成的小工具,叫方法(method),写法是 字符串.方法名()。今天必须会 6 个,个个都常用:

方法 作用 例子 结果
upper() 全部变大写 "abc".upper() ABC
lower() 全部变小写 "ABC".lower() abc
strip() 去掉首尾空格 " hi ".strip() hi
replace(a, b) 把 a 换成 b "Hi Tom".replace("Tom", "AI") Hi AI
split() 按空格拆成单词列表 "a b c".split() ['a', 'b', 'c']
count(x) 数 x 出现几次 "banana".count("a") 3

一次看个够:

s = "  Hello Python  "

print(s.upper())                #   HELLO PYTHON  (变大写)
print(s.lower())                #   hello python  (变小写)
print(s.strip())                # Hello Python(首尾空格去掉)
print(s.replace("Python", "AI"))  #   Hello AI    (换词)
print(s.split())                # ['Hello', 'Python'](按空格拆成列表)
print("banana".count("a"))      # 3(a 出现了 3 次)

输出:

  HELLO PYTHON  
  hello python  
Hello Python
  Hello AI  
['Hello', 'Python']
3

split() 拆出来的 ['Hello', 'Python'] 是个列表——明天的课就学它,今天先认识个脸。

这里有个特别反直觉的坑:upper() 之类的方法不会改掉原字符串,而是返回一个新字符串。看这段:

s = "hello"
s.upper()          # 这句话执行完,结果丢了
print(s)           # hello(s 还是小写,没变!)

s = s.upper()      # 把结果存回去才对
print(s)           # HELLO

输出:

hello
HELLO

原因是 Python 里字符串一旦创建就不能改(术语叫不可变)。所有加工方法都是返回一份新的,原字符串原封不动。想保留加工结果,就必须赋值接住:s = s.upper()

新手常在这三个地方翻车:

  • 忘写括号:s.upper 不报错,但打印出来是奇怪的东西——方法是函数,必须带 ()
  • 忘了接住结果:s.upper() 之后打印 s,发现没变,就是上面说的原因。
  • strip() 只去掉首尾的空格,中间的空格不动:"a b".strip() 还是 "a b"

把几段文字拼成一句:拼接和 f-string

程序里经常要把几段文字拼成一句话输出。有两种拼法:加号拼接,和 f-string(推荐)。

name = "小明"
age = 18

# 方式一:加号拼接(数字不能直接拼,要先转字符串)
print("我叫" + name + ",今年" + str(age) + "岁")

# 方式二:f-string(推荐,不用管类型)
print(f"我叫{name},今年{age}岁")

输出:

我叫小明,今年18岁
我叫小明,今年18岁

f-string 就是在字符串前面加个 f,把要插入的内容用 {} 包起来,像填空一样。第 2 周见过它,今天开始把它当主力用。

加号拼接有个经典报错:字符串和数字不能直接 +"我" + 18 会报 TypeError。要么 str(18) 转一下,要么直接用 f-string——这也是推荐 f-string 的原因,它不用管类型。

加工方法和拼接经常搭档着用:

word = "  python  "
word = word.strip()          # 去掉空格 → python
word = word.upper()          # 变大写 → PYTHON
print(f"我最喜欢的语言是:{word}")

输出:

我最喜欢的语言是:PYTHON

f-string 还有两个小讲究:{} 里放变量名,不要加引号——f"{'name'}" 输出的就是 name 这几个字母,不是变量的值;整个字符串要用引号包起来,且和内部的引号不冲突。

小实战:数一数一句话有几个单词

把今天学的串起来做一个真实小功能:输入一句话,输出这句话有几个单词。思路分三步:

  1. input() 收用户输入的一句话
  2. split() 按空格拆成一个单词列表
  3. len() 数列表里有几个元素,就是几个单词
text = input("请输入一句话:")
words = text.split()          # 按空格拆成单词列表
print(words)                  # 看看拆成了什么
print(f"一共有 {len(words)} 个单词")

假设输入 python is fun,输出:

请输入一句话:python is fun
['python', 'is', 'fun']
一共有 3 个单词

两个提醒:split() 不传参数时按"任意连续空白"拆分,比 split(" ") 更稳——用户多敲了几个空格也拆不错;统计要用 len(words)(单词个数),别写成 len(text)(那是字符个数)。用户输入两个空格或一个中文空格时,split() 可能拆出空字符串,统计就不准——这个坑留到明天的进阶练习,我们拉上 AI 一起解决。

随堂练习

练习一:把这句话加工一遍

对字符串 "python is fun" 依次做 4 件事:取第 0 个字符、取前 6 个字符、全部变大写、把 fun 换成 cool

提示:第 0 个字符用索引 s[0];前 6 个用切片 s[:6];大写用 upper();换词用 replace()

练习二:手机号打码

输入一个 11 位手机号,输出它的前 3 位和最后 4 位,中间用 **** 代替。比如输入 13800138000,输出 138****8000

提示:前 3 位用 phone[:3],后 4 位用 phone[-4:],最后用 f-string 拼起来。

进阶题(可选):数单词

输入一句话,用 split() 拆开,输出单词个数;再升级一步:把每个单词单独打印出来(用第 2 周学的 for 循环遍历)。

写不出来时,让 AI 搭把手

场景:手机号打码这类"截取 + 拼接"的题目写不出来时
提示词:
我是 Python 初学者,正在学字符串切片。请帮我写一个程序:
输入一个 11 位手机号,输出前 3 位和后 4 位,中间用 **** 代替。
要求用切片实现,代码加注释,每行注释解释在干什么。
场景:想对字符串做某种加工但不知道用什么方法
提示词:
我在学 Python 字符串。我有一个字符串变量 s,想要去掉它首尾的空格、
把所有字母变小写、再把里面的 "old" 替换成 "new"。
请告诉我分别用什么方法,并给我一段完整的示例代码。

常见报错速查

报错信息 什么意思 怎么办
IndexError: string index out of range 下标超了,取了一个不存在的字符 len() 确认长度,下标范围是 0 到 len-1
TypeError: can only concatenate str (not "int") to str 字符串和数字不能直接 + str(数字) 转换,或直接改用 f-string
SyntaxError: EOL while scanning string literal 引号不配对 / 引号里换行了 检查引号是否成对,字符串别跨行
打印出来没变化 方法返回了新字符串但没保存 赋值接住:s = s.upper()

收个尾

今天的内容概括成一句话:字符串是一串带引号的文字,索引和切片让你能"取",六个方法让你能"改",f-string 让你能"拼",split + len 让你能"数"。最要紧的那句别忘了——字符串不可变,加工结果要赋值接住。

明天学列表,字符串的"大号兄弟"。今天 split() 拆出来的 ['python', 'is', 'fun'] 就是一个列表,明天我们会学怎么往里面加东西、删东西、改东西,还要写一个购物车小程序。