Python 语法篇 · 第5章

字符串详解

字符串是 Python 中使用频率最高的数据类型,更是后续 NLP 处理的基本单位。本章讲透切片、方法、格式化与字符编码。

🎯 学习目标

  • 理解字符串的不可变性与 Unicode 本质
  • 熟练掌握索引与切片(含逆序、步长)
  • 系统掌握字符串 30+ 常用方法,能按场景选择
  • 掌握三种格式化方式,重点精通 f-string
  • 理解字符编码(ASCII/Unicode/UTF-8)与乱码成因
  • 会用 encode/decode、原始字符串、转义字符

1字符串的创建与本质

s1 = '单引号'
s2 = "双引号"
s3 = """三引号支持
跨行字符串,换行和缩进都会保留"""
s4 = '''同样可以用三个单引号'''

empty = ""                    # 空字符串
ch = "A"                      # 单字符也是 str,没有 char 类型

# 构造
str(123)                      # '123'
str([1, 2])                   # '[1, 2]'

# 字符串是【不可变】序列:创建后内容不能修改
s = "abc"
# s[0] = "x"                 # TypeError!不能原地修改
s = "x" + s[1:]               # 只能拼接出新字符串
print(s)                      # xbc

2索引:按位置取字符

字符串中每个字符都有编号,从 0 开始;Python 还支持负索引,从末尾倒数(-1 是最后一个):

字符串:    P   y   t   h   o   n
正索引:    0   1   2   3   4   5
负索引:   -6  -5  -4  -3  -2  -1
s = "Python"
print(s[0])     # P
print(s[-1])    # n(最后一个,最常用)
print(s[-2])    # o
# print(s[10])  # IndexError:越界报错(与切片不同)

3切片:取子串的万能语法

切片是 Python 最优雅的语法之一,格式为 s[start:stop:step],记忆口诀"含头不含尾,省略取到头":

s = "0123456789"

s[2:5]      # '234'    下标 2、3、4(不含5)
s[:4]       # '0123'   从头开始到下标3
s[6:]       # '6789'   下标6到末尾
s[:]        # 完整复制(浅拷贝)
s[::2]      # '02468'  每隔一个取(步长2)
s[1:8:2]    # '1357'
s[::-1]     # '9876543210'  字符串逆序!经典写法
s[-3:]      # '789'    取最后3个字符
s[:-3]      # '0123456'去掉最后3个字符
s[8:2:-1]   # '876543' 反向切片:从8取到3(不含2)
# 切片【永不报越界错】,自动收敛到有效范围
s[5:100]    # '56789'

切片同样适用于列表、元组等所有序列类型,一次学会处处可用。NLP 中取前后缀、文本截断、反转都靠它。

4运算:拼接、重复、成员、长度

"a" + "b"          # 'ab'     拼接
"ab" * 3           # 'ababab' 重复
len("你好python")   # 8       长度(按字符数,不是字节数)
"yth" in "python"  # True    子串判断
"xyz" not in "python"  # True
max("abc")         # 'c'     按码点取最大
sorted("cab")      # ['a','b','c']  返回排序后的列表

循环中用 + 反复拼接大量字符串效率很低(每次都生成新对象)。正确做法是先把子串放入列表,最后用 "".join(list) 一次合并。

# 高效拼接
parts = ["2026", "10", "25"]
date = "-".join(parts)        # '2026-10-25'

# 字符与码点
ord("中")                    # 20013(Unicode 码点)
chr(20013)                   # '中'

5字符串方法大全

字符串方法极多且都不修改原字符串(不可变),而是返回新字符串。按用途分类如下。

5.1 查找与统计

方法作用示例
find(sub)返回首次出现的下标,找不到返回 -1"abcab".find("ab") → 0
rfind(sub)从右侧找,返回下标"abcab".rfind("ab") → 3
index(sub)同 find,但找不到抛异常"abc".index("b") → 1
count(sub)统计子串出现次数"aabaa".count("a") → 4
startswith(s)是否以某串开头"report.pdf".startswith("report")
endswith(s)是否以某串结尾(判断扩展名神器)"a.py".endswith(".py") → True
filename = "photo_2026.png"
if filename.endswith((".png", ".jpg", ".jpeg")):   # 可传元组匹配多个
    print("这是图片文件")

5.2 大小写转换

s = "Hello World"
s.upper()        # 'HELLO WORLD'
s.lower()        # 'hello world'      英文统一比较时先 lower
s.title()        # 'Hello World'      每个单词首字母大写
s.capitalize()   # 'Hello world'      整句首字母大写
s.swapcase()     # 'hELLO wORLD'      大小写互换
"  abc  ".strip().upper()    # 'ABC' 可链式调用

# 判断类(返回 bool)
"abc123".isalnum()    # True  全是字母或数字
"abc".isalpha()       # True  全是字母
"123".isdigit()       # True  全是数字
"   ".isspace()       # True  全是空白
"Hello".istitle()     # True
"ABC".isupper()       # True

5.3 清洗:strip / replace / translate

"  hello \n".strip()      # 'hello'   去掉两端空白(空格/\t/\n)
"xxhelloxx".strip("x")    # 'hello'   去掉两端指定字符集合
"  hi  ".lstrip()         # 'hi  '    只去左侧
"  hi  ".rstrip()         # '  hi'    只去右侧

"I like Java".replace("Java", "Python")   # 'I like Python' 全部替换
"aaaa".replace("a", "b", 2)               # 'bbaa' 只替换前2次

# NLP 数据清洗常用:批量替换多种字符
table = str.maketrans({",": ",", "。": ".", "!": "!"})
"你好,世界!".translate(table)            # '你好,世界!'

strip("abc") 去掉的是"由 a/b/c 任意排列组成的两端字符",不是去掉子串 "abc"!要删子串请用 replace("abc", "")。

5.4 分割与合并(NLP 高频)

# split:字符串 → 列表
"a,b,c".split(",")           # ['a', 'b', 'c']
"2026-10-25".split("-")      # ['2026', '10', '25']
"a  b   c".split()           # ['a','b','c'] 不带参数时按任意空白分割(自动合并连续空白)
"a,b,c".split(",", 1)        # ['a', 'b,c']  最多分割1次
"line1\nline2".splitlines()  # ['line1', 'line2'] 按行分割

# partition:分割一次且保留分隔符,返回三元组
"localhost:8080".partition(":")   # ('localhost', ':', '8080')

# join:列表 → 字符串(split 的逆操作)
"-".join(["2026", "10", "25"])    # '2026-10-25'
"".join(["P", "y", "thon"])       # 'Python'
",".join(str(i) for i in [1, 2, 3])  # '1,2,3'(元素必须是字符串)

5.5 对齐与填充

"42".center(6)        # '  42  ' 居中到宽度6
"42".zfill(6)         # '000042' 左侧补0(处理编号常用)
"hi".ljust(6, "-")    # 'hi----'
"hi".rjust(6, "*")    # '****hi'

5.6 判断与转换的 is* 家族

# 识别字符串包含的字符类别(中文场景)
"中文".isalpha()       # True!汉字也被视为字母
"123".isdigit()     # True!全角数字也算
"Ⅲ".isdigit()         # True!罗马数字也算
"⅔".isnumeric()       # True!分数也算
# 需要严格"ASCII 阿拉伯数字"时:
s = "123"
print(s.isascii() and s.isdigit())   # True

6字符串格式化

6.1 f-string(Python 3.6+,最推荐)

name, score = "小明", 95.468

# 基本插值:变量、表达式、函数调用都能放 {} 里
f"{name} 的成绩是 {score}"             # '小明 的成绩是 95.468'
f"{1 + 2}"                             # '3'
f"{name.upper()}"                      # '小明'

# 格式说明符:{值:格式}
f"{score:.2f}"                         # '95.47' 保留2位小数
f"{score:+.2f}"                        # '+95.47' 带正负号
f"{0.25:.1%}"                          # '25.0%' 百分比
f"{255:0>4d}"                          # '0255' 宽度4,右对齐补0
f"{255:b}"   /  f"{255:x}"  / f"{255:o}"   # '11111111' / 'ff' / '377'
f"{3.14:<10}|"                        # '3.14      |' 左对齐宽10
f"{3.14:^10}"                         # 居中

# 千分位(财务报表常用)
f"{1234567:,}"                        # '1,234,567'

# 日期
from datetime import datetime
f"{datetime(2026,10,25):%Y年%m月%d日}" # '2026年10月25日'

# Python 3.8+:= 说明符,连变量名一起输出(调试神器)
x = 42
print(f"{x=}")                        # 'x=42'

6.2 str.format()

"{}今年{}岁".format("小明", 18)        # 按位置
"{0}-{1}-{0}".format("a", "b")        # 'a-b-a' 可重复引用
"{name}={score}".format(name="语文", score=90)
"{:>10.2f}".format(3.14)              # '      3.14'

6.3 % 旧式格式化(老代码常见,能读懂即可)

"%s今年%d岁" % ("小明", 18)     # %s 字符串 %d 整数 %f 浮点
"价格:%.2f" % 9.9              # '价格:9.90'
"%5d|%-5d|" % (42, 42)          # 右对齐 / 左对齐

7转义字符与原始字符串

转义含义转义含义
\n换行\\反斜杠本身
\t制表符 Tab\'单引号
\r回车\"双引号
\b退格\u4e2dUnicode 字符(中)
print("第一行\n第二行")
print("他说:\"你好\"")          # 他说:"你好"
print("路径:C:\\Users\\name")  # 每个反斜杠要写两遍 —— 很丑

# 原始字符串 r"...":反斜杠一律不转义(文件路径、正则表达式必备)
path = r"C:\Users\name\Desktop\report.txt"
import re
re.findall(r"\d+", "a12b34")    # 正则一律加 r 前缀

8字符编码:彻底搞懂中文乱码

8.1 发展脉络

  • ASCII:美国人制定,128 个字符(英文字母、数字、符号),1 字节足够,但没有中文。
  • GB2312 / GBK:中国的中文编码(GBK 兼容 GB2312,收录 2 万多汉字),Windows 中文版默认代码页 936。
  • Unicode:全球统一字符集,给世界上每个字符一个唯一编号(码点),如"中"= U+4E2D。但它只规定编号,没规定怎么存。
  • UTF-8:Unicode 的一种存储实现(变长 1~4 字节),英文 1 字节、汉字通常 3 字节,是互联网与工程的事实标准。

乱码的唯一原因:用错误的编码去解码。 文件按 GBK 存的字节,却按 UTF-8 解读,必然乱码。记住"用什么编码写,就用什么编码读"。现代项目一律使用 UTF-8。

8.2 str 与 bytes 的转换

# str(人看的文本) ⇄ bytes(机器存的字节)
s = "中文Python"

# 编码 encode:字符串 → 字节
b_utf8 = s.encode("utf-8")
b_gbk = s.encode("gbk")
print(b_utf8)              # b'\xe4\xb8\xad\xe6\x96\x87Python'
print(len(b_utf8))         # 12  (2个汉字×3 + 6个英文 = 12 字节)
print(len(b_gbk))          # 10  (2个汉字×2 + 6 = 10 字节)
print(len(s))              # 8   (字符数)

# 解码 decode:字节 → 字符串(必须用相同编码!)
print(b_utf8.decode("utf-8"))   # 中文Python
# print(b_utf8.decode("gbk"))   # UnicodeDecodeError 或乱码

# 字节字面量
b = b"hello"            # ASCII 内容可直接 b 前缀书写

8.3 文件读写中的编码

# Windows 上 open 默认编码是 GBK(cp936)!读写中文文件务必显式指定 utf-8
with open("data.txt", "w", encoding="utf-8") as f:
    f.write("中文内容")

with open("data.txt", "r", encoding="utf-8") as f:
    print(f.read())

# 网页 requests 取到响应但编码猜错时:
# resp.encoding = "utf-8"  或  resp.apparent_encoding 自动探测

9综合案例:文本信息统计

text = """Python is powerful. Python is easy.
学习 Python 很有趣!"""

# 1) 词频(简单按空格,中文分词请看 NLP 篇第3章 jieba)
words = text.lower().replace(".", "").replace("!", "").split()
freq = {}
for w in words:
    freq[w] = freq.get(w, 0) + 1
print(freq)        # {'python': 2, 'is': 2, 'powerful': 1, ...}

# 2) 字符统计
total = len(text.replace("\n", ""))
chinese = sum(1 for ch in text if '\u4e00' <= ch <= '\u9fff')  # 统计汉字
print(f"总字符 {total},其中汉字 {chinese} 个")

# 3) 脱敏:保留首尾字符,中间用 * 代替
phone = "13812345678"
masked = phone[:3] + "*" * (len(phone) - 7) + phone[-4:]
print(masked)      # 138****5678

10本章小结与练习

📌 关键结论

  • 字符串是不可变的 Unicode 序列,所有方法都返回新串。
  • 切片 s[起:止:步长] 含头不含尾、永不越界;[::-1] 逆序。
  • 清洗三件套:strip / replace / split+join;判断扩展名用 endswith。
  • 格式化首选 f-string,:.2f、:,、:0>4d 最常用。
  • 路径与正则用 r"";文件读写显式写 encoding="utf-8"。
练习1:s = "Hello,Python世界",如何取出最后4个字符、逆序、统计字母 o 的个数?
最后4个字符 s[-4:];逆序 s[::-1];统计 s.count("o")。注意 len 按字符计,中文每个字算 1 个字符。
练习2:把字符串 "a,,b,,,c" 清理成列表 ['a','b','c']。
直接 split(",") 会产生空串:['a','','b','','','c']。应过滤空值:[x for x in "a,,b,,,c".split(",") if x];也可用正则 re.split(r",+", s)。
练习3:把 " I Love Python " 变成规范的 "I love python"(去空白、除首词外小写)。
words = s.strip().split(); words[0] + " " + " ".join(w.lower() for w in words[1:])。考察 strip、无参 split 合并多余空格、join 组合。