Python 的 str 是不可变的 Unicode 文本序列,bytes 是字节序列。字符串方法通常返回结果,不会原地改写原字符串。本文按 Python 3.11 说明,并把原笔记的查阅表、转义说明和交互记录整理为可独立运行的程序。
字面量、访问、拼接与转义
跳转到“字面量、访问、拼接与转义”单引号和双引号都可以定义字符串。索引从零开始,负索引从结尾计数;切片包含起点、排除终点。len(s) 计算 Unicode 码点数,不保证等于用户眼中的字符个数或屏幕宽度。例如字母与组合重音可以由两个码点组成。
var1 = 'Hello World!'var2 = "Runoob"assert var1[0] == "H" and var1[-1] == "!"assert var1[:6] + var2 + "!" == "Hello Runoob!"assert var1[0:2] == "He" and "World" in var1assert var1[::-1] == "!dlroW olleH"assert "a" * 5 == "aaaaa" and 2 * "ab" == "abab"assert len("e\u0301") == 2assert len("é") == 1
continued = "line1 \line2 \line3"assert continued == "line1 line2 line3"assert "\a\b\000\n\r" == "\x07\x08\x00\x0a\x0d"assert "\000" != "" and len("\000") == 1assert r'\n' == R'\n' == "\\n"assert len(r'\n') == 2 and len("\n") == 1print(repr(continued), repr("\a\b\000\n\r"))反斜杠接源代码换行会忽略这次换行;它不同于字符串内的 \n。原稿中把自然语言、>>> 提示符和输出放在 Python 围栏内的片段,不能整体作为脚本执行。
| 写法 | 字符含义或词法作用 | 使用边界 |
|---|---|---|
\a | 响铃控制字符 | 终端是否发声取决于宿主与设置 |
\b | 退格控制字符 | 不等于从 Python 字符串中删除前一个字符 |
\000、\0 | NUL,码点为零 | 是一个字符,不是空字符串 |
\n | 换行 | 文件和终端的显示行为还与环境有关 |
\r | 回车 | 某些终端会移到行首并覆盖显示,日志界面可能显示控制标记 |
r'...'、R'...' | 原始字符串字面量 | 反斜杠通常保留,但引号和结尾反斜杠仍受词法规则限制 |
因此 'google runoob taobao\r123456' 的内容仍含有完整文字和一个回车,不能把某个终端上显示的 123456 runoob taobao 当作字符串本身。原始字符串也不能以奇数个反斜杠结束;r 不会把一切非法字面量变为合法。上面的程序只打印控制字符的 repr,避免依赖声音或光标效果。字符串字面量规则
大小写、清理、替换与字符映射
跳转到“大小写、清理、替换与字符映射”| 方法 | 用途 | 需要注意 |
|---|---|---|
upper、lower、swapcase | 大写、小写、互换大小写 | Unicode 映射不保证一字符对应一字符,也不保证两次互换总能恢复 |
capitalize | 首字符标题化,其余小写 | 不是寻找第一个字母再大写 |
title、casefold | 简单标题化、适合无大小写比较的折叠 | 标题化不懂人名或自然语言语法;casefold 不替代 Unicode 规范化 |
strip、lstrip、rstrip | 移除两侧、左侧、右侧的字符 | 默认按空白字符;参数是字符集合,不是一个完整前后缀 |
removeprefix、removesuffix | 移除一个精确前缀或后缀 | Python 3.9 起提供;不匹配时保留原内容 |
replace | 替换子串,可限制次数 | 返回结果,不原地修改 |
maketrans、translate | 构建映射并逐码点转换 | 可替换成多个字符,映射为 None 可删除字符 |
text = "abcd12efg"assert text.upper() == "ABCD12EFG"assert text.lower() == "abcd12efg"assert text.upper().title() == text.title() == "Abcd12Efg"assert text.capitalize() == "Abcd12efg"assert "12abc".capitalize() == "12abc"assert "Hello".swapcase() == "hELLO"assert "Straße".casefold() == "STRASSE".casefold() == "strasse"assert text == "abcd12efg"
assert " \thello\n".strip() == "hello"assert " hello ".lstrip() == "hello "assert " hello ".rstrip() == " hello"assert "000020".strip("0") == "2"assert "000020".lstrip("0") == "20"assert "abc/def".removeprefix("abc/") == "def"assert "edit#text".removesuffix("text") == "edit#"assert "abcabcX".lstrip("abc") == "X"assert "abcabcX".removeprefix("abc") == "abcX"assert "Hello World".replace("World", "Python") == "Hello Python"assert "one one".replace("one", "two", 1) == "two one"
vowels = str.maketrans("aeiou", "12345")assert "hello".translate(vowels) == "h2ll4"mapping = str.maketrans({"a": "xy", "!": None})assert "a!b".translate(mapping) == "xyb"print("text transformation checks passed")查找、分割、连接与行
跳转到“查找、分割、连接与行”| 方法 | 返回结果 | 失败或特殊情况 |
|---|---|---|
find、rfind | 首次、最后一次匹配的起始索引 | 未找到返回 -1 |
index、rindex | 相应匹配的起始索引 | 未找到抛出 ValueError,不能直接当作 find 替换 |
count | 不重叠的出现次数 | 不是统计所有重叠窗口 |
startswith、endswith | 是否匹配前缀、后缀 | 可接受字符串元组,并用 start、end 限制检查范围 |
split、rsplit | 分割后的列表 | maxsplit 限制次数;有分隔符与默认空白规则不同 |
partition、rpartition | (前段, 分隔符, 后段) | 总是三元组;找不到时左右版本的空段位置不同 |
splitlines | 按行边界分割 | 可保留行结束符;不只识别单独的 \n |
join | 以当前字符串连接各项 | 接受可迭代对象,但各项必须是字符串 |
s = "Hello World"assert s.find("World") == 6 and s.rfind("l") == 9assert s.index("World") == 6 and s.rindex("l") == 9assert s.find("missing") == -1try: s.index("missing")except ValueError: passelse: raise AssertionError("index must reject a missing substring")assert "aaaa".count("aa") == 2assert s.startswith("He") and s.endswith(("ld", "!"))assert s.endswith("World", 6, 11)assert s.startswith("World", 6)
assert " a b\t".split() == ["a", "b"]assert " a b ".split(" ") == ["", "a", "", "b", ""]assert "one,two,three".split(",") == ["one", "two", "three"]assert "one#two#three".split("#", 1) == ["one", "two#three"]assert "one#two#three".rsplit("#", 1) == ["one#two", "three"]assert "part#a#partition".partition("#") == ("part", "#", "a#partition")assert "part#a#partition".rpartition("#") == ("part#a", "#", "partition")assert "abc".partition("#") == ("abc", "", "")assert "abc".rpartition("#") == ("", "", "abc")assert "Line 1\r\nLine 2\n".splitlines() == ["Line 1", "Line 2"]assert "a\nb\n".splitlines(keepends=True) == ["a\n", "b\n"]assert " ".join(["Hello", "World"]) == sassert ",".join(str(n) for n in [1, 2]) == "1,2"print("search and split checks passed")find() 返回 -1 后直接拿它索引,会读到最后一个字符,应先判断。空分隔符不能传给 split 或 partition;"".split() 得到空列表,而 "".split(",") 得到包含一个空字符串的列表。
格式化、对齐、零填充与制表符
跳转到“格式化、对齐、零填充与制表符”str.format 和 format_map 在模板中替换字段;format(value, spec) 与 f-string 则把格式说明交给该值类型的 __format__。这几项有关联,但不是同一个方法。% 是另一套字符串格式化协议 __mod__,不能把 C 的所有 printf 转换都直接搬过来。
% 转换 | Python 字符串格式化中的含义 |
|---|---|
%c | 一个字符或有效 Unicode 码点整数,不限 ASCII |
%s | 取字符串表示 |
%d、%u | 十进制整数表示;%u 是保留的旧别名,不建立无符号整数类型 |
%o、%x、%X | 八进制、十六进制小写/大写;负数可以带负号 |
%f | 定点格式,可指定小数位 |
%e、%E | 科学计数法,指数标记大小写不同 |
%g、%G | 按精度和数值选择常规表示,精度通常指有效数字 |
%p | Python str 的 % 不支持;不能据此格式化对象地址 |
%% | 输出字面百分号 |
assert "我叫 %s 今年 %d 岁!" % ("小明", 10) == "我叫 小明 今年 10 岁!"assert "%c" % ord("中") == "中"assert "%s" % 20 == "20"assert "%u" % -2 == "-2"assert "%o" % -8 == "-10"assert "%x %X" % (255, 255) == "ff FF"assert "%.2f" % 3.14159 == "3.14"assert "%.2e %.2E" % (123, 123) == "1.23e+02 1.23E+02"assert "%.3g %.3G" % (1e8, 1e8) == "1e+08 1E+08"assert "%d%%" % 50 == "50%"try: "%p" % object()except ValueError: passelse: raise AssertionError("%p is unsupported")
assert "{0} {1}".format("Hello", "World") == "Hello World"assert "{name} {greeting}".format_map({"name": "Alice", "greeting": "Hello"}) == "Alice Hello"assert format("Hi", ">5") == "Hi".__format__(">5") == " Hi"assert f"{3.14159:.2f}" == "3.14"assert "Hi".center(6, "-") == "--Hi--"assert "Hi".ljust(5, ".") == "Hi..."assert "Hi".rjust(5, ".") == "...Hi"assert "5".zfill(3) == "005" and "-5".zfill(3) == "-05"assert "1234".zfill(3) == "1234"assert "Hello\tWorld".expandtabs(4) == "Hello World"print("formatting checks passed")center、ljust、rjust 和 zfill 的宽度是最低要求,不会截断过长内容;填充字符需要是单个字符。zfill 会照顾前导正负号。expandtabs(4) 按当前列到下一个四列制表位补空格,不是把每个制表符机械替换为四个空格。
文本分类与标识符
跳转到“文本分类与标识符”| 方法 | 判断规则概要 |
|---|---|
isupper、islower | 至少有一个有大小写的字符,且这些字符都符合要求;数字等无大小写字符不直接使结果为假 |
istitle | 是否符合字符串方法采用的标题化规则 |
isalpha、isalnum | Unicode 字母,或字母与数字;不是只检查英文字母 |
isdecimal、isdigit、isnumeric | 分别覆盖十进制数字、更广的数字、再更广的数值字符 |
isascii | 每个码点是否属于 ASCII;空字符串也为真 |
isprintable | 是否全部为 Python 定义的可打印字符;空字符串也为真 |
isspace | 非空且全部是空白字符 |
isidentifier | 是否符合标识符词法规则;还要单独排除关键字 |
import keyword
assert "ABC123".isupper() and "abc123".islower()assert not "123".isupper() and not "123".islower()assert "Hello World".istitle()assert "中文".isalpha() and "abc123".isalnum()assert "١".isdecimal() and int("١") == 1assert "²".isdigit() and not "²".isdecimal()assert "⅕".isnumeric() and not "⅕".isdigit()assert not "-2".isnumeric() and not "1.5".isnumeric()assert "ASCII".isascii() and not "中".isascii()assert "".isascii() and "".isprintable()assert not "".isalpha() and not "".isspace()assert " \t\n".isspace() and not "\n".isprintable()assert "name_1".isidentifier()assert "for".isidentifier() and keyword.iskeyword("for")assert not "2name".isidentifier()print("classification checks passed")isdigit() 或 isnumeric() 为真,不保证 int() 接受该字符串;若实际任务是解析数值,直接调用对应转换并处理异常通常更准确。判断可作为名字还可能涉及特定语法上下文中的软关键字,不能把 isidentifier() 当成完整 Python 程序验证器。
编码、对象协议与原表查阅入口
跳转到“编码、对象协议与原表查阅入口”encode 把文本按指定编码转换为字节;字节的 decode 执行逆向转换。长度不同很常见,编码名称与错误处理策略应写清楚。
text = "中文"encoded = text.encode("utf-8")assert isinstance(encoded, bytes)assert len(text) == 2 and len(encoded) == 6assert encoded.decode("utf-8") == texttry: text.encode("ascii")except UnicodeEncodeError: passelse: raise AssertionError("ASCII cannot represent this text")try: b"\xff".decode("utf-8")except UnicodeDecodeError: passelse: raise AssertionError("invalid UTF-8 must be rejected in strict mode")print("encoding checks passed")普通使用优先写 len(s)、s[index]、hash(s) 等公开操作。以下完整保留原表中的特殊方法名称,同时区分协议和常用方法;拼错的 _sizeof__ 在这里明确改为 __sizeof__。
| 名称 | 推荐操作或说明 |
|---|---|
__class__、__doc__、__dir__、__getattribute__ | 类型、文档、dir(s)、属性获取;可用 type、getattr 等常规入口 |
__len__、__getitem__、__iter__、__contains__ | len、下标/切片、迭代、成员检测 |
__repr__、__str__ | repr(s) 展示引号/转义,str(s) 取得字符串表示 |
__add__、__mul__、__rmul__ | 字符串拼接、重复及反射乘法 |
__eq__、__ne__、__lt__、__le__、__gt__、__ge__ | 相等与按码点进行的词典序比较;不是自然语言排序 |
__format__、__mod__ | 单个字符串值的格式说明与 % 格式化;另见上文模板 format、format_map |
__hash__ | 可哈希字符串用于键或集合;哈希值不应当作跨进程稳定指纹 |
__new__ | 创建字符串实例的类型入口,普通代码用 str(...) |
__reduce_ex__ | 序列化协议使用的重建信息;普通持久化应选择合适格式 |
_sizeof__(原拼写)、__sizeof__ | 正确方法是双下划线版本;sys.getsizeof 的数值依实现,只描述对象浅层开销 |
import pickleimport sys
s = "Hello World"assert s.__class__ is str and isinstance(s.__doc__, str)assert "lower" in s.__dir__()assert getattr(s, "lower")() == "hello world"assert s.__getattribute__("lower")() == "hello world"assert len(s) == 11 and list(iter(s)) == list(s)assert repr("a\nb") == "'a\\nb'"assert str(s) == s and str.__new__(str, "abc") == "abc"assert "A" < "a" and "10" < "2"assert "abc" <= "abc" and "abc" >= "abc"assert "b" > "a" and "b" != "a"assert hash(s) == hash(" ".join(["Hello", "World"]))assert isinstance(s.__sizeof__(), int) and sys.getsizeof(s) > 0assert not hasattr(s, "_sizeof__")assert isinstance(s.__reduce_ex__(4), tuple)assert pickle.loads(pickle.dumps(s, protocol=4)) == sprint("string protocol checks passed")序列化示例只处理本程序生成的数据;不能把从外部拿到的 pickle 当成普通文本载入。需要交换文本时,应优先考虑明确编码的文本文件或 JSON。字符串与其他对象的通用协议见特殊方法。方法规则参见 Python 字符串方法、% 格式化转换和 Unicode 文本说明。