课程进度 课程大纲 已发布 24/24 课
Python 基础
数据与集合
构建可靠的程序
使用对象建模
专业 Python
高级 Python
文本是一排小方块
Python 字符串就是放在引号中的文本,例如 "Python"、"こんにちは" 或 "👍🏽"。想象一下,它的各个组成部分排成一行。Python 把这些部分称为 Unicode 码点。
- 获取文本 姓名、笔记或标签
- 清理文本 处理空白和大小写形式
- 搜索文本 找到有用的部分
- 生成新文本 原字符串保持不变
Python 从 0 开始计算位置。len() 计算的是码点数量,而不是字节数,也不一定等于人眼所看到的符号数量。
word = "Python"
print(len(word))
print(word[0])
print(word[-1])
print(word[1:4])
6
P
n
yth
word[0] 是第一个方块,word[-1] 是最后一个。切片包含起始位置,但会在结束位置之前停止,因此 [1:4] 会取得位置 1、2 和 3。
索引、切片、拆分与连接
使用单个索引时,对应的位置必须存在。word[99] 会引发 IndexError。切片则更宽容:word[:99] 只会返回 word 的全部内容。空字符串没有第一个或最后一个位置,因此在使用索引之前要先检查它:
name = "".strip()
if name:
print(name[0])
else:
print("No first character")
split() 把文本拆成一个列表。join() 使用选定的分隔符,把多个字符串片段连接起来:
messy = " read code\tplay "
pieces = messy.split()
print(pieces)
print(" ".join(pieces))
['read', 'code', 'play']
read code play
- 拆分 文本变成多个片段
- 列表 片段按顺序排列
- 连接 片段重新变成文本
" ".join(text.split()) 会合并意外出现的多余空白。使用 split(",") 时,逗号就是确切的分隔符。两个紧挨着的逗号会产生一个空片段。另外,传给 join() 的每一项都必须是字符串。
清理方法会返回新字符串
字符串是不可变的:Python 不能替换现有字符串中的某一个方块。下面的代码会故意触发错误:
word = "python"
word[0] = "P"
它会引发 TypeError。应当改为构建一个新字符串:
word = "python"
title = "P" + word[1:]
print(word)
print(title)
python
Python
字符串方法也遵循同样的规则。strip() 会删除两端的空白,replace() 会替换完全匹配的片段,而 casefold() 会生成适合 Unicode 文本搜索的不区分大小写键。每个方法都会返回新文本。
raw = " Python makes TEXT "
clean = raw.strip().replace("makes", "cleans")
searchable = clean.casefold()
print(clean)
print("python" in searchable)
print(searchable.count("text"))
print(searchable.find("cleans"))
Python cleans TEXT
True
1
7
如果想问“它在里面吗?”,就使用 in;如果想问“有多少个互不重叠的匹配?”,就使用 count();如果想知道第一次出现的位置,就使用 find()(返回 -1 表示没有找到)。绝对不要使用 if text.find(term)::位于位置 0 的匹配会被当作假,而表示未找到的 -1 反而会被当作真。
一个图形可以使用多个码点
两个字符串看起来可能完全相同,却由不同的码点序列组成。Python 标准库中的 unicodedata 模块可以把规范等价的文本转换成相同的 Unicode 形式。
- 一个方块 一个预组合字母
- 两个方块 一个字母加一个重音符号
- NFC 让等价的序列能够匹配
import unicodedata
one = "café"
two = "cafe\u0301"
print(one == two)
print(len(one), len(two))
print(one == unicodedata.normalize("NFC", two))
print(len("👍🏽"))
False
4 5
True
2
这个表情符号看起来像一个符号,但实际上使用了两个码点。基本索引操作可能会把它拆开。因此,字符串位置既不是字节位置,也不能可靠地表示“可见字符”的边界。
小项目:学习笔记清理器
创建 note_cleaner.py。这个完整程序只使用标准库。它会规范化 Unicode、合并空白,并统计一个非空关键词出现的次数,同时不会改变原始字符串。
import unicodedata
def search_key(text):
return unicodedata.normalize("NFC", text).strip().casefold()
def clean_note(text):
normalized = unicodedata.normalize("NFC", text)
return " ".join(normalized.split())
raw_note = " CAFÉ practice, then cafe\u0301 review. "
raw_keyword = " Café "
note = clean_note(raw_note)
keyword = search_key(raw_keyword)
matches = search_key(note).count(keyword)
print(f"Original: <{raw_note}>")
print(f"Clean: <{note}>")
print(f"Matches: {matches}")
print(f"Original unchanged: {raw_note.startswith(' ')}")
运行 python3 note_cleaner.py 并检查:
Original: < CAFÉ practice, then café review. >
Clean: <CAFÉ practice, then café review.>
Matches: 2
Original unchanged: True
边界规则:关键词不能为空。text.count("") 会统计字符之间的空隙,并返回 len(text) + 1,这对于统计学习词汇没有实际意义。
三个小任务
- 安全提取首尾字符。 去除输入单词两端的空白。仅当它不为空时,才打印它的第一个和最后一个码点。
- 简短预览。 使用
text[:limit]编写preview(text, limit)。分别测试0、3,以及一个大于文本长度的限制值。 - 搜索侦探。 查找一个位于位置
0的词,以及一个不存在的词。正确使用in,或者将find()的结果与-1比较。
做到这些,你就可以开始第 6 章了……
- 你会使用
len()、正索引、负索引和切片; - 你能解释字符串是不可变的;
- 你会使用
strip()、split()、join()和casefold()清理文本; - 你能根据想解决的问题选择
in、count()或find(); - 你会使用
unicodedata.normalize()规范化等价的 Unicode 文本; - 你记得码点、可见符号和字节是不同的东西;
- 你能运行学习笔记清理器,并让四项输出检查全部匹配。
接下来,你会把 split() 生成的片段放进列表,然后比较可修改的列表和固定的元组。