Day07 · 字符串(一)

Day07 · 字符串(一)

阶段:阶段一 · 核心基础 | 前置:Day06 | 技术基准:Python 3.15.0

学习目标

  • 掌握字符串的三种定义方式:单引号、双引号、三引号
  • 理解字符串的不可变性,知道"修改字符串"其实是生成新串
  • 熟记常用转义字符,会用原始字符串 r'' 描述路径与正则
  • 掌握索引(正负)与切片 s[start:end:step],能做负步长逆序
  • 会用 len() 取长度,知道字符串与 +、* 运算的规则

一、什么是字符串

字符串(str)就是一串字符,是 Python 里最常用的文本数据类型。它属于序列类型(Sequence)------按顺序排成一列,可以逐个取出来。

二、定义字符串:三种引号

2.1、单引号与双引号

Python 里单引号 '...' 和双引号 "..." 完全等价,任选其一。选哪种的实用规则是:字符串内部需要出现哪种引号,就用另一种包外面。

python 复制代码
# Author: XiaoJia
s1 = 'Hello Python'        # 单引号
s2 = "Hello Python"        # 双引号,效果相同
s3 = '他说:"你好"'          # 内部有双引号,外面用单引号
s4 = "it's a book"         # 内部有单引号,外面用双引号

print(s1, s2, s3, s4, sep="\n")

输出:

text 复制代码
Hello Python
Hello Python
他说:"你好"
it's a book

2.2、三引号:多行字符串

三个引号 """...""" 或 '''...''' 可以跨行,保留换行和缩进,非常适合写大段文本、SQL、HTML、文档注释。

python 复制代码
poem = """床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。"""
print(poem)

html = """<html>
    <body>Hello Python</body>
</html>"""
print(html)

提示:Day04 讲过,函数/类开头的三引号字符串就是文档字符串(Docstring),本质也是普通字符串。

三、字符串是不可变的

字符串一旦创建,就不能原地修改。所谓"改字符串",其实是造一个新字符串。

python 复制代码
s = "hello"
# s[0] = "H"     # TypeError: 'str' object does not support item assignment

# 正确做法:造一个新字符串
s2 = "H" + s[1:]
print(s)           # hello    原串没变
print(s2)          # Hello    新串

记住:所有字符串方法都返回新字符串,不改动原串。

四、转义字符

有些字符有特殊含义(比如引号、换行),想表示它们本身就要用反斜杠 \ 转义。

转义符 含义
\n 换行
\t 横向制表符(Tab)
\\ 一个反斜杠
\' 单引号
\" 双引号
\r 回车(光标回到行首)
\b 退格(删除前一个字符)
\uXXXX Unicode 字符(按码点)
python 复制代码
print("他说:\"你好\"")        # 他说:"你好"
print('it\'s a book')         # it's a book
print("第一行\n第二行")        # \n 换行
print("姓名\t年龄")            # 姓名    年龄
print("C:\\Users\\XiaoJia")   # C:\Users\XiaoJia

4.1、转义字符到底解决什么问题

有些字符你想"原样打出来",但它们在 Python 里另有含义。比如字符串用双引号包着,里面再来一个双引号,解释器会误以为"字符串到这里结束了"。这时候就得用反斜杠 \ 告诉解释器:"后面这个字符只是普通字符,别当成语法符号"。

python 复制代码
# 不加转义会怎样?下面这行会报 SyntaxError
# print("他说:"你好"")     # 第二个 " 被当成字符串结尾

# 加了转义就正常
print("他说:\"你好\"")    # 他说:"你好"

\n、\t 这类则相反------它们是"看不见的控制字符",反斜杠让普通字母 n 变成"换行"这个动作。常见的还有 \u 加四个十六进制数,表示一个 Unicode 字符:

python 复制代码
print("\u4f60\u597d")     # 你好    按 Unicode 码点输出
print("\u2605")           # ★      一颗星

五、原始字符串 r''

写 Windows 路径或正则表达式时,每个反斜杠都要写成 \\,很麻烦。在引号前加 r(raw),反斜杠就"所见即所得",不再转义。

python 复制代码
path1 = "C:\\Users\\XiaoJia\\python"   # 普通写法,每个 \ 都要转义
path2 = r"C:\Users\XiaoJia\python"     # 原始字符串,直接写路径

print(path1)          # C:\Users\XiaoJia\python
print(path2)         # C:\Users\XiaoJia\python
print(path1 == path2)  # True

# 正则场景也常用 r
import re
print(re.findall(r"\d+", "编号123abc456"))   # ['123', '456']

注意:原始字符串里最后一个字符不能是单个反斜杠 (r"\" 会报错),因为反斜杠还在转义引号。路径末尾要反斜杠时写成 r"C:\path\\" 或 path + "\\"。

六、索引:按下标取字符

字符串最左端是第 0 个,向右依次 +1;也可以用负数从右往左数,最右是 -1。

python 复制代码
# Author: XiaoJia
s = "Hello Python"
#  索引:0123456789...(H=0, e=1, ..., P=6, ...)

print(s[0])        # H
print(s[6])        # P
print(s[-1])       # n   最后一个字符
print(s[-7])       # 空格(第 6 位)

长度为 L 的字符串,最后一个字符下标是 L-1;越界会报 IndexError。

python 复制代码
# print(s[100])    # IndexError: string index out of range

七、切片:截取一段

切片语法 s[start:end:step],从 start 取到 end 之前(不含 end),可带步长。三个参数都能省略。

python 复制代码
s = "Hello Python"
print(s[0:5])      # Hello    [0,5)
print(s[:5])       # Hello    省略 start = 从 0 开始
print(s[6:])       # Python   省略 end = 到最后
print(s[-6:])      # Python   负数从右数
print(s[::2])      # HloPto   步长 2,隔一个取
print(s[::-1])     # nohtyP olleH   步长 -1,字符串反转!
print(s[0:100])    # Hello Python   切片越界不报错,自动截到末尾

切片口诀:左含右不含,省略即到头;步长为负,从右往左走。

python 复制代码
# 实用小例:去首尾、取奇偶位
word = "abcdefg"
print(word[1:-1])      # bcdef    去掉首尾
print(word[::2])       # aceg     偶数位(0,2,4,6)
print(word[1::2])      # bdf      奇数位(1,3,5)

5.1、索引与切片的常见坑

新手在索引切片上常犯三个错,小嘉一次讲清:

  1. 下标从 0 而不是 1 开始 :第一个字符是 s[0],不是 s[1]。
  2. 切片右边界取不到 :s[0:5] 取的是下标 0、1、2、3、4,不包含 5,所以长度正好等于 5 - 0 = 5。
  3. 负索引从 -1 开始 :最后一个字符是 s[-1],不是 s[-0](-0 等于 0)。
python 复制代码
# Author: XiaoJia
word = "Python"
# 下标: P  y  t  h  o  n
# 正数: 0  1  2  3  4  5
# 负数:-6 -5 -4 -3 -2 -1

print(word[0])       # P     第一个
print(word[5])       # n     最后一个
print(word[-1])      # n     最后一个(等价写法)
print(word[0:6])     # Python  左含右不含,长度 6
print(word[:3])      # Pyt    省略 start
print(word[3:])      # hon    省略 end

还有一个反直觉但好用的点:切片越界不报错,索引越界才报错。

python 复制代码
s = "abc"
print(s[1:100])      # bc    切片自动截到末尾,不报错
# print(s[100])      # 索引越界 → IndexError

八、len()、+ 与 *

8.1、len() 取长度

python 复制代码
print(len("Hello"))      # 5
print(len(""))           # 0
print(len("你好"))        # 2   中文一个字算 1 个字符

8.2、字符串拼接 + 与重复 *

python 复制代码
s1 = "Hello"
s2 = "World"
print(s1 + " " + s2)     # Hello World
print("=" * 30)          # ==============================
print("Ha" * 3)           # HaHaHa

规则:

  • + 只能两个字符串相加;字符串和数字相加要先 str() 转换。
  • * 的另一个操作数必须是整数,表示重复次数。
  • 运算结果都是新字符串,原串不变。
python 复制代码
# 常见错误:字符串和数字直接相加
# print("年龄:" + 18)        # TypeError
print("年龄:" + str(18))     # 年龄:18
python 复制代码
# 常见错误:字符串和数字直接相加
# print("年龄:" + 18)        # TypeError
print("年龄:" + str(18))     # 年龄:18

# 乘 0 或负整数会得到空串
print("Ha" * 0)               # 空串
print("ab" * -3)              # 空串

+ 和 * 都不会改动原字符串,而是返回一个全新字符串。这跟 Day06 讲的"字符串不可变"是一回事------你看到的所有字符串运算,本质都是"读老串、造新串"。

8.3、字符串与数字相加为什么会报错

新手常写 print("第" + 1 + "名"),结果报 TypeError: can only concatenate str (not "int") to str。原因很简单:+ 在字符串里表示"拼接",它要求两边都是字符串;而 1 是整数,类型对不上,Python 不会自作主张帮你转换(这正是 Day01 讲的"强类型")。解决办法就是先用 str() 把数字转成字符串,或者干脆用 f-string:

python 复制代码
# 错误:str + int
# print("第" + 1 + "名")      # TypeError

# 对:先转字符串
print("第" + str(1) + "名")     # 第1名

# 更推荐:f-string,自动处理类型
print(f"第{1}名")                # 第1名

这个小坑本质上还是"字符串不可变、+ 只认字符串"这条规则的延伸。理解了类型边界,报错信息就不再可怕。

九、综合小例:手搓"首字母大写"

Day07 学的知识可以组合起来解决一个小问题:把 "hello python" 变成 "Hello Python",不调用现成的 title(),自己用切片和拼接实现。

python 复制代码
# Author: XiaoJia
s = "hello python"

# 第一步:把第一个单词首字母大写
result = s[0].upper() + s[1:6]      # "Hello "
# 第二步:找到第二个单词,把它的首字母大写
space_pos = s.find(" ")            # 6
result = result + s[space_pos + 1].upper() + s[space_pos + 2:]
print(result)                       # Hello Python

这个例子把"切片取一段、upper() 改大小写、+ 拼接、find() 定位空格"四个知识点串了起来。等 Day08 学了 split()、join(),这种需求会写得更简洁------但思路是一样的:切片取出要改的片段,改完再拼回去。

小嘉提醒:因为字符串不可变,你永远是"造新串",而不是"改老串"。理解这一点,后面学列表(可变)时,你才能体会两者的本质差别。

十、本章边界:今天没讲什么

为避免贪多,Day07 只解决"字符串怎么定义、取字符、切片"这三件事。字符串的格式化输出 (%、.format()、f-string)、编码解码 、以及 find/replace/split/join 等几十个方法,全部放在 Day08。今天你只要记住两件事:

  • 字符串是一串不可变的字符,用引号定义;
  • 靠"索引取值、切片取段"就能完成大部分基础操作。

把这两点用熟,Day08 的方法学起来会非常顺------那些方法本质上都是"在切片的基础上做加工"。

练习题

  1. 用三种引号各写一个字符串:含英文双引号的、含英文单引号的、跨三行的,分别打印。
  2. 给定 s = "PythonProgramming",用切片取出 "Python"、"Programming",再把整个字符串反转。
  3. 用原始字符串写一个 Windows 路径 D:\文档\教材\Python,并用 print 打印,观察和普通写法的区别。
  4. 写代码验证"字符串不可变":尝试 s[0]="x" 看报什么错,再用 + 和切片造一个首字母大写的新串。
  5. 给定 word = "abcdefgh",分别用切片取出偶数位字符和奇数位字符。
  6. 思考题:s[::-1] 为什么能反转字符串?步长为负时 start 和 end 默认指向哪一端?

小结

  • 字符串用 '...'、"..." 定义,三引号 """...""" 可跨行;单双引号等价,按"内部有什么引号就用另一种包"选择。
  • 字符串不可变:不能 s[i]=x,所有方法返回新串。
  • 转义字符 \n \t \\ \' \" 等;原始字符串 r"..." 所见即所得,适合路径与正则,但末尾不能是单个 \。
  • 索引从 0 开始,负数从右数(-1 是最后一个);越界索引报 IndexError,但切片越界不报错。
  • 切片 s[start:end:step]:左含右不含,省略即到头,[::-1] 反转字符串。
  • len() 取长度(中文按字符算 1);+ 拼接、* 重复,都产生新串;字符串不能直接和数字相加。

扩展阅读