
文章目录
📖 开篇导读
前几节课我们学习了变量、运算符、分支和循环,你已经能写出一些简单的程序了。但在实际开发中,我们处理最多的数据类型是什么?不是整数,不是布尔值,而是字符串。
字符串无处不在:用户输入的文字、网页上的内容、文件的读写、数据库中的文本字段、日志信息……几乎任何需要与人交互的地方都离不开字符串。可以说,字符串处理能力是衡量程序员基本功的重要标尺。
💡 工作场景:后端开发中,你需要验证用户输入的格式(邮箱、手机号)、清洗爬虫抓取的脏数据、拼接SQL语句、生成JSON响应;数据分析中,你需要从日志中提取关键信息、处理CSV文件;自动化脚本中,你需要解析命令行参数……这些统统都是字符串操作。
这一课,我们将彻底讲透Python中的字符串:从底层的存储原理(不可变对象)、内存驻留机制,到索引、切片,再到几十个常用方法,以及三种格式化输出的终极对比。学完本课,你将能游刃有余地处理任何文本数据。
🎯 学习目标
| 目标编号 | 具体掌握内容 | 对应面试/工作价值 |
|---|---|---|
| 1️⃣ | 理解字符串的底层存储(Unicode、不可变、驻留) | 面试必考,防止内存误用 |
| 2️⃣ | 熟练使用索引和切片操作字符串 | 提取子串、反转字符串等 |
| 3️⃣ | 掌握常用字符串方法(大小写转换、查找、替换、分割、拼接等) | 日常开发的80%字符串任务 |
| 4️⃣ | 深度对比三种格式化方式(% 、format()、f-string) | 写出高效、可读的代码 |
| 5️⃣ | 了解原始字符串和转义字符 | 处理文件路径、正则表达式 |
🔥 面试考点:面试官常问“字符串为什么是不可变的?”“如何高效拼接大量字符串?”“
join和+的区别?”“f-string比format快在哪里?”本课全解。
📚 知识点理论精讲
一、字符串的定义与底层原理
1.1 字符串的定义
字符串是字符的序列。在Python中,使用单引号、双引号或三引号括起来的文本。
s1 = 'hello' # 单引号
s2 = "world" # 双引号
s3 = '''多行
字符串''' # 三引号,可换行
s4 = """也是多行""" # 双三引号
单引号和双引号没有区别,只是为了方便在字符串中包含另一种引号。
text1 = 'It\'s OK' # 需要转义
text2 = "It's OK" # 不需要转义,更简洁
1.2 字符串的底层存储:Unicode
Python 3中的字符串使用Unicode编码,这意味着你可以直接处理任何语言的字符。
chinese = "你好,世界"
japanese = "こんにちは"
emoji = "😊🎉"
print(chinese) # 正常输出
💡 面试考点:Python 2中字符串分为
str(字节串)和unicode,Python 3统一为str(Unicode),内部的编码细节无需关心,默认使用UTF-8。
1.3 字符串的不可变性(Immutable)
字符串是不可变对象。这意味着一旦创建,你无法修改字符串中的某个字符。任何看似“修改”的操作,实际上都是创建了一个新的字符串对象。
s = "hello"
# s[0] = "H" # TypeError: 'str' object does not support item assignment
# 正确方式:创建新字符串
s = "H" + s[1:] # 新字符串 "Hello"
为什么要设计成不可变?
- 安全性:字符串作为字典的键、集合的元素时,如果可变会破坏哈希值。
- 性能:不可变性允许字符串驻留(interning)和哈希缓存。
- 线程安全:无需加锁。
内存示意图:
a = "hello"
b = a # b指向同一个对象
a = a + " world" # 创建新对象,a指向新对象,b仍指向原对象
print(b) # "hello" 不变
1.4 字符串驻留(String Interning)
Python内部会缓存一些短字符串(通常包含字母、数字、下划线),使得相同内容的字符串复用同一个对象,节省内存并加快比较速度。
a = "hello"
b = "hello"
print(a is b) # True(驻留)
c = "hello world"
d = "hello world"
print(c is d) # 通常为False(含空格不驻留)
# 手动驻留(很少需要)
import sys
e = sys.intern("hello world!")
f = sys.intern("hello world!")
print(e is f) # True
⚠️ 高频坑点:不要依赖字符串驻留来判断相等!始终使用
==比较内容,is只用于判断是否是同一个对象(如if x is None)。
二、索引与切片:灵活访问子串
2.1 索引(Indexing)
字符串中的每个字符都有位置编号,也叫索引。索引从0开始。
s = "Python"
# 索引: P y t h o n
# 0 1 2 3 4 5
print(s[0]) # 'P'
print(s[3]) # 'h'
负索引:从右向左,从-1开始。
print(s[-1]) # 'n'(最后一个字符)
print(s[-2]) # 'o'
正向索引和负索引的对应关系:
s[0]↔s[-len(s)]s[1]↔s[-5]s[5]↔s[-1]
2.2 切片(Slicing)
切片用于提取子字符串,语法:[start:end:step]
start:起始索引(包含),默认为0end:结束索引(不包含),默认为字符串长度step:步长,默认为1
s = "PythonProgramming"
# 基本切片
print(s[0:6]) # "Python"(索引0到5,不包含6)
print(s[6:]) # "Programming"(从索引6到末尾)
print(s[:6]) # "Python"(从头到索引5)
# 使用负索引
print(s[-11:-1]) # "Programmin"(不含最后一个)
print(s[-11:]) # "Programming"
# 步长
print(s[::2]) # "Pto rgamn"(每隔一个取一个)
print(s[::-1]) # "gnimmargorPnohtyP"(反转字符串,面试常考)
# 省略start/end
print(s[:]) # 完整复制字符串
📌 记忆口诀:切片左闭右开,下标从0开始,结束索引不包含。
2.3 切片的高级技巧
# 负数步长实现反转
text = "abcdef"
print(text[::-1]) # fedcba
# 提取偶数位置字符
print(text[::2]) # ace
# 提取奇数位置字符
print(text[1::2]) # bdf
# 切片不会索引越界(安全)
print(text[1:100]) # bcdef(超出部分忽略)
三、常用字符串方法大全
字符串方法不会修改原字符串(因为不可变),而是返回新的字符串。
3.1 大小写转换
| 方法 | 说明 | 示例 |
|---|---|---|
upper() | 全部转大写 | "Hello".upper() → "HELLO" |
lower() | 全部转小写 | "Hello".lower() → "hello" |
capitalize() | 首字母大写,其余小写 | "hello WORLD".capitalize() → "Hello world" |
title() | 每个单词首字母大写 | "hello world".title() → "Hello World" |
swapcase() | 大小写互换 | "Hello".swapcase() → "hELLO" |
s = " pYtHon "
print(s.upper()) # " PYTHON "
print(s.lower()) # " python "
print(s.capitalize()) # " python "(注意空格还在)
print(s.strip().capitalize()) # "Python"(先strip)
3.2 去除空白符
| 方法 | 说明 | 示例 |
|---|---|---|
strip() | 去除两端空白(空格、\t、\n) | " hi ".strip() → "hi" |
lstrip() | 去除左侧空白 | " hi".lstrip() → "hi" |
rstrip() | 去除右侧空白 | "hi ".rstrip() → "hi" |
user_input = " admin "
clean_input = user_input.strip() # "admin"
# 工作场景:用户登录时经常需要strip掉意外空格
3.3 查找与替换
| 方法 | 说明 | 返回值 |
|---|---|---|
find(sub) | 返回子串第一次出现的索引,找不到返回-1 | int |
rfind(sub) | 从右查找,返回索引 | int |
index(sub) | 同find,但找不到会抛出ValueError | int |
count(sub) | 返回子串出现的次数 | int |
replace(old, new[, count]) | 替换子串,可指定替换次数 | str |
s = "hello world, hello python"
print(s.find("hello")) # 0
print(s.rfind("hello")) # 13
print(s.index("world")) # 6
# print(s.index("xxx")) # ValueError
print(s.count("hello")) # 2
print(s.replace("hello", "hi", 1)) # "hi world, hello python"
3.4 分割与拼接
| 方法 | 说明 | 示例 |
|---|---|---|
split(sep=None, maxsplit=-1) | 按分隔符分割为列表 | "a,b,c".split(",") → ['a','b','c'] |
rsplit() | 从右开始分割 | |
splitlines() | 按行分割(处理\n) | "a\nb".splitlines() → ['a','b'] |
join(iterable) | 将可迭代对象的元素拼接为字符串 | ",".join(['a','b']) → "a,b" |
重要:join是高效拼接字符串的关键!
# split 基础
data = "apple,banana,orange"
fruits = data.split(",") # ['apple', 'banana', 'orange']
# 限制分割次数
log = "2024-01-01 10:30:00 INFO User login"
parts = log.split(" ", 2) # ['2024-01-01', '10:30:00', 'INFO User login']
# join 拼接
words = ["Python", "is", "awesome"]
sentence = " ".join(words) # "Python is awesome"
# 将列表中的字符串用逗号+空格连接
csv_line = ", ".join(["a", "b", "c"]) # "a, b, c"
3.5 判断开头/结尾与类型
| 方法 | 说明 | 示例 |
|---|---|---|
startswith(prefix) | 是否以某字符串开头 | "hello".startswith("he") → True |
endswith(suffix) | 是否以某字符串结尾 | "hello".endswith("lo") → True |
isalpha() | 是否全是字母 | "abc".isalpha() → True |
isdigit() | 是否全是数字 | "123".isdigit() → True |
isalnum() | 是否全是字母或数字 | "abc123".isalnum() → True |
isspace() | 是否全是空格 | " ".isspace() → True |
islower() / isupper() | 是否全小写/全大写 | "abc".islower() → True |
# 常见应用:验证用户输入是否为数字
age = input("请输入年龄: ")
if age.isdigit():
age = int(age)
else:
print("输入无效")
# 检查文件扩展名
filename = "report.pdf"
if filename.endswith(".pdf"):
print("这是一个PDF文件")
3.6 填充与对齐
| 方法 | 说明 |
|---|---|
center(width, fillchar=' ') | 居中,两侧填充 |
ljust(width, fillchar=' ') | 左对齐,右侧填充 |
rjust(width, fillchar=' ') | 右对齐,左侧填充 |
zfill(width) | 右侧对齐,左侧补零 |
print("Python".center(20, "*")) # "*******Python*******"
print("42".rjust(5, "0")) # "00042"
print("42".zfill(5)) # "00042"
四、转义字符与原始字符串
4.1 常见转义字符
| 转义序列 | 含义 |
|---|---|
\n | 换行 |
\t | 制表符(Tab) |
\\ | 反斜杠本身 |
\' | 单引号 |
\" | 双引号 |
\r | 回车 |
\b | 退格 |
print("第一行\n第二行")
# 第一行
# 第二行
print("姓名\t年龄\t城市")
print("张三\t25\t北京")
4.2 原始字符串(Raw String)
在字符串前面加r,让转义字符失效,所见即所得。常用于文件路径和正则表达式。
# 文件路径(Windows)
path = r"C:\Users\name\Desktop\file.txt"
print(path) # C:\Users\name\Desktop\file.txt
# 普通字符串需要两个反斜杠
path2 = "C:\\Users\\name\\Desktop\\file.txt"
# 正则表达式
import re
pattern = r"\d+" # 匹配数字,不需要双反斜杠转义
五、字符串格式化:三种方式深度对比
5.1 方法一:%格式化(旧式,来自C语言)
name = "Alice"
age = 25
score = 92.5
print("姓名:%s,年龄:%d,分数:%.1f" % (name, age, score))
常用格式化字符:
%s:字符串%d/%i:整数%f:浮点数(可指定精度:%.2f)%x:十六进制
缺点:当变量多时容易弄错顺序,不支持复杂格式。
5.2 方法二:str.format()(Python 2.6+)
使用位置索引或关键字参数。
# 按位置
print("{}的年龄是{}岁".format(name, age))
# 按索引
print("{0}的年龄是{1}岁,{0}的分数是{2}".format(name, age, score))
# 按关键字
print("{name}的年龄是{age}岁".format(name=name, age=age))
# 格式化数字
print("{:.2f}".format(3.14159))
5.3 方法三:f-string(格式化字符串字面量,Python 3.6+,最推荐)
在字符串前加f,直接在花括号里写变量和表达式。
# 基础用法
print(f"{name}的年龄是{age}岁")
# 执行表达式
print(f"3+5={3+5}")
# 调用方法
print(f"{name.upper()}")
# 格式化数字
pi = 3.1415926
print(f"π≈{pi:.2f}")
# 多行f-string
msg = f"""
姓名: {name}
年龄: {age}
"""
f-string的优势:
- 可读性最强:变量直接嵌入
- 性能最快:因为是在运行时直接求值,比
format()快 - 支持表达式:可以写任意Python表达式
🔥 工作应用:现代Python项目(Python 3.6+)几乎统一使用f-string。除非需要兼容旧版本或动态构造格式化模板,否则不要再用
%和format。
5.4 格式化细节对比表
| 需求 | % 风格 | format 风格 | f-string 风格 |
|---|---|---|---|
| 简单插入 | "%s" % name | "{}".format(name) | f"{name}" |
| 多个变量 | "%s %d" % (a,b) | "{} {}".format(a,b) | f"{a} {b}" |
| 指定宽度 | "%10s" % name | "{:>10}".format(name) | f"{name:>10}" |
| 浮点精度 | "%.2f" % pi | "{:.2f}".format(pi) | f"{pi:.2f}" |
| 千位分隔 | 不支持(需用locale) | "{:,}".format(10000) | f"{10000:,}" |
| 百分比 | "%.1f%%" % (0.25*100) | "{:.1%}".format(0.25) | f"{0.25:.1%}" |
六、字符串与其他类型互转
6.1 字符串转数字
# str -> int
num_str = "123"
num = int(num_str)
# str -> float
pi_str = "3.14"
pi_num = float(pi_str)
# 处理可能的异常
try:
val = int(input("请输入数字: "))
except ValueError:
print("不是有效的数字")
6.2 数字转字符串
n = 100
s = str(n) # "100"
s2 = repr(n) # "100"(repr通常用于调试)
6.3 字符串转列表/转回
s = "hello"
# 字符串 -> 字符列表
chars = list(s) # ['h','e','l','l','o']
# 字符列表 -> 字符串
s2 = "".join(chars) # "hello"
💻 代码案例实操
案例1:字符串基础操作——用户信息清洗
"""
user_data_clean.py
演示字符串常用方法处理用户输入的脏数据
"""
# 模拟从表单或文件读取的原始数据
raw_data = [
" 张三 ",
" lisi@example.com ",
" 25 ",
" Python,Java,GO "
]
# 数据清洗
cleaned = []
for item in raw_data:
# 去除两端空格
item = item.strip()
# 将所有字母转小写(邮箱统一格式)
if '@' in item:
item = item.lower()
cleaned.append(item)
print("清洗后:", cleaned)
# 输出: ['张三', 'lisi@example.com', '25', 'Python,Java,GO']
案例2:切片实战——文件名处理
"""
filename_processor.py
从文件路径中提取文件名、扩展名,并批量重命名
"""
# 文件路径示例
filepath = "/home/user/documents/report_2024.txt"
# 提取文件名(最后一个/之后的部分)
filename = filepath.split("/")[-1]
print(f"文件名: {filename}") # report_2024.txt
# 分离文件名和扩展名
dot_index = filename.rfind(".") # 从右边找最后一个点
if dot_index != -1:
name_part = filename[:dot_index] # "report_2024"
ext_part = filename[dot_index+1:] # "txt"
print(f"名称: {name_part}, 扩展名: {ext_part}")
# 批量重命名:给所有文件添加前缀
files = ["a.txt", "b.txt", "c.txt"]
prefix = "backup_"
new_files = [prefix + f for f in files]
print(new_files) # ['backup_a.txt', 'backup_b.txt', 'backup_c.txt']
案例3:字符串方法大合集——文本分析
"""
text_analysis.py
统计一段文本中的单词数、句子数、大写字母数等
"""
text = """Python is a powerful programming language.
It is widely used in data science, web development, and AI.
Python's syntax is simple and easy to learn!"""
# 1. 统计字符总数(包括空格和标点)
print(f"总字符数: {len(text)}")
# 2. 统计单词数(按空白分割)
words = text.split()
print(f"单词数: {len(words)}")
# 3. 统计句子数(按句号、感叹号、问号分割)
import re
sentences = re.split(r'[.!?]', text)
sentences = [s.strip() for s in sentences if s.strip()]
print(f"句子数: {len(sentences)}")
# 4. 统计大写字母个数
uppercase_count = sum(1 for ch in text if ch.isupper())
print(f"大写字母数: {uppercase_count}")
# 5. 统计每个单词的出现频率(忽略大小写)
from collections import Counter
word_list = text.lower().split()
word_counts = Counter(word_list)
print("高频词:", word_counts.most_common(3))
# 6. 替换敏感词
sensitive = "Python is powerful"
censored = sensitive.replace("powerful", "******")
print(censored)
案例4:拼接字符串的性能对比(面试重点)
"""
string_concat_performance.py
演示为什么用join而不是+拼接大量字符串
"""
import time
# 方法1:使用 + 拼接(效率低)
def concat_plus(n):
result = ""
for i in range(n):
result += str(i)
return result
# 方法2:使用列表 + join(效率高)
def concat_join(n):
parts = []
for i in range(n):
parts.append(str(i))
return "".join(parts)
# 方法3:列表推导式 + join(最简洁)
def concat_comprehension(n):
return "".join(str(i) for i in range(n))
# 测试性能
n = 100000
start = time.time()
concat_plus(n)
print(f"使用 + : {time.time() - start:.4f}秒")
start = time.time()
concat_join(n)
print(f"使用 join: {time.time() - start:.4f}秒")
start = time.time()
concat_comprehension(n)
print(f"使用推导+join: {time.time() - start:.4f}秒")
# 结论:+ 每次都会创建新字符串,O(n^2)复杂度;join一次性分配内存,O(n)
案例5:格式化输出实战——生成报告
"""
report_format.py
使用三种格式化方式生成美观的报告
"""
# 数据
title = "2024年度销售报告"
total_sales = 1234567.89
growth_rate = 0.1523
top_product = "智能手表"
top_sales = 345678.90
# 方法1:%格式化(旧式)
print("\n--- %% 格式化 ---")
print("《%s》" % title)
print("总销售额: %.2f 元" % total_sales)
print("同比增长: %.1f%%" % (growth_rate * 100))
print("明星产品: %s (%.2f 元)" % (top_product, top_sales))
# 方法2:format()方法
print("\n--- format() 格式化 ---")
print("《{}》".format(title))
print("总销售额: {:.2f} 元".format(total_sales))
print("同比增长: {:.1%}".format(growth_rate))
print("明星产品: {} ({:.2f} 元)".format(top_product, top_sales))
# 方法3:f-string(最推荐)
print("\n--- f-string 格式化 ---")
print(f"《{title}》")
print(f"总销售额: {total_sales:,.2f} 元") # 千分位分隔
print(f"同比增长: {growth_rate:.1%}")
print(f"明星产品: {top_product} ({top_sales:,.2f} 元)")
# 对齐输出表格
print("\n--- 对齐输出 ---")
items = [
("商品A", 23.5, 10),
("商品B", 100.0, 3),
("商品C", 8.99, 100)
]
print(f"{'商品':<10}{'单价':>8}{'数量':>6}{'小计':>10}")
for name, price, qty in items:
subtotal = price * qty
print(f"{name:<10}{price:>8.2f}{qty:>6}{subtotal:>10.2f}")
案例6:字符串在正则表达式中的简单应用
"""
regex_string_demo.py
演示字符串方法与正则的结合(实际正则后续课程详解)
"""
import re
# 常见场景:邮箱格式验证(简单版)
def validate_email(email):
# 使用字符串方法快速过滤
if '@' not in email or '.' not in email:
return False
# 简单正则
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
emails = ["test@example.com", "invalid", "user@domain", "a@b.c"]
for e in emails:
print(f"{e}: {'有效' if validate_email(e) else '无效'}")
# 提取文本中的所有数字
text = "Today is 2024-05-12, temperature 25.5°C"
numbers = re.findall(r'\d+\.?\d*', text)
print("提取的数字:", numbers) # ['2024', '05', '12', '25.5']
⚠️ 易错点避坑总结
| 序号 | 坑点描述 | 后果 | 解决方案 |
|---|---|---|---|
| 1 | 误以为字符串可变,直接修改索引 | TypeError | 用切片+拼接创建新字符串 |
| 2 | ==比较字符串时用成is | 结果可能不可预测(依赖驻留) | 永远用==比较内容 |
| 3 | 切片中结束索引位置理解错误 | 结果少一个字符或多截取 | 记住左闭右开s[0:2]取索引0和1 |
| 4 | 在循环中用+拼接大量字符串 | 性能极差,O(n^2) | 用列表收集,最后join |
| 5 | 忘记strip()导致用户输入有空格匹配失败 | 登录验证等逻辑错误 | 输入后调用strip() |
| 6 | split()默认按任意空白分割,但可能误解 | 处理CSV时出错 | CSV用csv模块或指定分隔符 |
| 7 | find返回-1时误当作索引使用 | s.find(sub)返回-1,s[-1]取最后一个字符 | 判断是否!= -1后再使用 |
| 8 | isalpha()/isdigit()与Unicode的处理 | 中文也是isalpha(),印度数字也是isdigit() | 明确需求,可能需要正则 |
| 9 | 混淆index()和find() | index找不到会抛异常 | 不确定时用find+条件 |
| 10 | f-string中花括号内的表达式过于复杂 | 可读性下降 | 复杂表达式提前赋值给变量 |
📝 课后实战练习题
第1题:字符串基础操作
给定字符串s = " Python programming is FUN! ",完成以下操作:
- 去除两端空格
- 全部转换为小写
- 将单词"fun"替换为"awesome"
- 统计字符’o’出现的次数
- 按空格分割成单词列表
第2题:切片练习
字符串email = "user@example.com",分别提取出用户名和域名。(提示:使用find和切片)
第3题:验证回文字符串
写一个函数is_palindrome(s),忽略大小写和非字母数字,判断字符串是否为回文(正读反读相同)。例如:“A man, a plan, a canal: Panama” 是回文。
第4题:统计文本中每个单词出现的频率
给定一段文本(自己写一段),输出单词及其出现次数,按出现次数降序排列。忽略大小写和标点符号。
第5题:格式化输出——商品价格表
有如下商品数据:
商品名,单价,库存
手机,2999,50
电脑,5999,30
耳机,199,200
要求格式化为表格输出,单价保留两位小数,库存右对齐,总价值(单价×库存)千分位分隔。
第6题:文件名批量重命名(模拟)
有一个文件列表:["report_2023_01.txt", "report_2023_02.txt", "data_backup.xlsx"]。要求:
- 将所有以
report_开头的文件名中的2023替换为2024 - 将所有
.txt文件扩展名改为.md - 输出修改后的文件名列表
第7题:字符串性能实验
创建一个大字符串:"a" * 100000,然后分别用+和join方法将每个字符用逗号分隔(如"a,a,a"),对比执行时间。编写代码并分析原因。
🔜 下节课预告
字符串是个体,而列表是容器。下一节课我们将进入Python最常用的数据结构——列表(List),它就像可以装任何东西的“购物车”,支持增删改查,灵活且强大。
第8课:列表List详解:增删改查、遍历与嵌套列表实战
内容包括:
- 列表的定义与特性(可变、有序、可重复)
- 增删改查:
append,insert,extend,remove,pop,index等 - 列表切片与遍历
- 列表推导式(高效生成列表)
- 嵌套列表与矩阵操作
- 列表与字符串的互转
- 实战案例:待办事项管理、学生成绩管理系统
列表是Python最核心的数据结构之一,掌握它将让你处理批量数据的能力大幅提升!
🌟 学习鼓励:字符串是编程中最早接触的数据类型,也是使用最频繁的。不要试图一次记住所有方法,而是通过实际练习建立“肌肉记忆”——遇到问题知道查文档、知道用什么方法。坚持下来,你会发现字符串处理像说话一样自然。
🔗《50节课 Python 从入门到精通》系列课程导航
🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

2万+

被折叠的 条评论
为什么被折叠?



