Python 从零开始 · 第 7 课:字符串处理与文件读写
到这一课为止,程序处理的数据都是我们手写的。但现实里,数据大多存在文件里:日志、配置、表格、文章……程序得能读进来、处理、再写出去。
这一课我们搞定三件事:字符串的十八般武艺、正则表达式,以及文件的读写。
一、字符串的十八般武艺
字符串是 Python 里最常用的类型,方法多到数不清,但常用的就这些:
s = " Hello, Python World "
s.strip() # 去掉首尾空白
s.lower() # 全小写
s.upper() # 全大写
s.replace("Python", "Java") # 替换
s.split(",") # 按逗号切成列表
",".join(["a", "b"]) # 用逗号把列表拼成字符串
s.startswith("Hello")# 是否以某串开头
s.endswith("World") # 是否以某串结尾
"py" in s # 子串是否在里面
len(s) # 长度最容易搞混的一对:split 是"拆",join 是"拼",方向相反:
words = "a,b,c".split(",") # ['a', 'b', 'c']
s = "-".join(words) # 'a-b-c'二、正则表达式:文本匹配的"万能钥匙"
前面那些字符串方法,适合处理你明确知道长什么样的字符串。但现实里,很多文本是"模式":比如"找出文章里所有的手机号""验证用户填的邮箱对不对""把日志里的日期抠出来"。这时用 split、replace 会写得又长又脆。
正则表达式(regex)就是专门干这个的:用一个"模式字符串",去匹配目标文本里符合规律的部分。Python 里用内置的 re 模块。
最常用的几个元字符
| 元字符 | 含义 | 例子 |
|---|---|---|
\d | 一个数字 | \d\d 匹配 "42" |
\w | 一个字母/数字/下划线 | \w+ 匹配 "hello" |
. | 任意一个字符 | a.c 匹配 "abc"、"a1c" |
* | 前面的出现 0 次或多次 | \d* |
+ | 前面的出现 1 次或多次 | \d+ 匹配 "123" |
? | 前面的出现 0 次或 1 次 | colou?r 匹配 "color"/"colour" |
[...] | 方括号里的任意一个 | [aeiou] 匹配一个元音 |
^ / $ | 行的开头 / 结尾 | ^你好 匹配以"你好"开头 |
() | 分组,把匹配结果圈出来 | 见下文 |
三个最常用的 re 函数
import re
# 1. findall:找出所有匹配,返回列表(最常用)
text = "我的号码:13812345678,备用:13987654321"
phones = re.findall(r"1\d{10}", text) # 1 开头 + 10 位数字
print(phones) # ['13812345678', '13987654321']
# 2. search:找第一个匹配,返回一个 Match 对象
m = re.search(r"\d+", "订单号:A2026")
print(m.group()) # 2026(.group() 取出匹配到的内容)
# 3. sub:替换所有匹配(比 str.replace 强在能按"模式"换)
clean = re.sub(r"\d+", "#", "电话 123 和 456")
print(clean) # 电话 # 和 #分组:把想要的"抠"出来
用 () 圈住感兴趣的部分,findall 就会只返回括号里的内容:
import re
text = "张三 138-1234-5678,李四 139-8765-4321"
# 名字 + 电话,用 () 分组
pairs = re.findall(r"(\w+)\s(\d{3}-\d{4}-\d{4})", text)
print(pairs) # [('张三', '138-1234-5678'), ('李四', '139-8765-4321')]验证输入对不对:fullmatch
比如检查用户填的邮箱格式(不必背这个表达式,理解思路即可):
import re
def 是不是邮箱(s):
# 一个或多个字母数字,@ 一个或多个字母数字,. 字母
模式 = r"[\w.+-]+@[\w-]+\.[\w.-]+"
return re.fullmatch(模式, s) is not None
print(是不是邮箱("alice@example.com")) # True
print(是不是邮箱("这不是邮箱")) # False两个新手提醒:
- 正则前面加
r(如r"\d+"),表示"原始字符串",别让\被转义吃掉。- 正则不需要背,用到再查,但
\d、\w、+、*、()这几个得眼熟——它们覆盖了 90% 的日常需求。
三、字符串格式化:把变量塞进文本
老方法是 %,新方法(推荐)是 f-string:
name = "张三"
age = 20
# f-string(Python 3.6+,最推荐)
print(f"我叫 {name},今年 {age} 岁")
# format 方法
print("我叫 {},今年 {} 岁".format(name, age))
# 百分号(老式)
print("我叫 %s,今年 %d 岁" % (name, age))f-string 还能做运算、格式化数字:
price = 3.14159
print(f"价格是 {price:.2f} 元") # 保留两位小数:3.14
print(f"占宽 10 格右对齐:{price:>10}")四、字符串是不可变的
第 4 课说过元组不可变。字符串也是不可变的——你不能改它的某个字符:
s = "hello"
# s[0] = "H" # 报错!字符串不能改
s = "H" + s[1:] # 正确做法:拼一个新的
print(s) # Hello要"改"字符串,本质都是造一个新字符串。
🧊 冷笑话:A:"我想把"Hello"的第一个字母改成小写。" B:"改不了,字符串不可变。" A:"那怎么办?" B:"新建一个"hello",把旧的扔掉。"
五、读文件:把文件内容拿进来
用 open() 打开文件,用 with 保证用完自动关闭:
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read() # 一次性全读进来(字符串)
# lines = f.readlines() # 读成列表,每个元素是一行
# line = f.readline() # 读一行
print(content)"r":读模式"w":写模式(会清空原文件!)"a":追加模式(在末尾接着写)encoding="utf-8":处理中文必须加,否则乱码
with的好处:文件用完自动关闭,不用手动f.close(),也不会忘记关。
六、写文件:把数据存下来
with open("out.txt", "w", encoding="utf-8") as f:
f.write("第一行\n") # 写入字符串,\n 是换行
f.write("第二行\n")追加模式不会清空:
with open("out.txt", "a", encoding="utf-8") as f:
f.write("这是追加的一行\n")七、逐行读文件:处理大文件的正确姿势
如果文件很大,read() 全读进来可能撑爆内存。正确做法是逐行读:
with open("data.txt", "r", encoding="utf-8") as f:
for line in f: # 逐行遍历,内存友好
line = line.strip() # 去掉行尾的换行符
print(line)八、一个完整例子:读文件 → 处理 → 写文件
假设 scores.txt 每行一个数字,统计平均分并写到 result.txt:
# scores.txt 内容:
# 90
# 85
# 78
total = 0
count = 0
with open("scores.txt", "r", encoding="utf-8") as f:
for line in f:
total += int(line.strip())
count += 1
avg = total / count
with open("result.txt", "w", encoding="utf-8") as f:
f.write(f"总人数:{count}\n")
f.write(f"平均分:{avg:.2f}\n")这就是"数据流水线"的最小雏形:读 → 算 → 写。
九、动手时间 🎯
实验 1:统计文件字数
with open("data.txt", "r", encoding="utf-8") as f:
text = f.read()
words = text.split()
print("字符数:", len(text))
print("单词数:", len(words))实验 2:找出所有邮箱
import re
text = "联系我:alice@example.com 或 bob@test.org,谢谢"
emails = re.findall(r"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+", text)
print(emails) # ['alice@example.com', 'bob@test.org']实验 3:写一个简易日志
from datetime import datetime
def log(msg):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open("log.txt", "a", encoding="utf-8") as f:
f.write(f"[{now}] {msg}\n")
log("程序启动")
log("处理了 100 条数据")
log("程序结束")十、小结
- 字符串方法 + 正则双管齐下:strip/split/join/replace 处理明确文本;
re.findall/re.search/re.sub按"模式"匹配(\d、\w、+、()),模式前记得加r。 - 格式化用 f-string:
f"{变量:.2f}"又简单又好用。 - 文件读写用
with open(..., encoding="utf-8"),r读、w覆盖写、a追加写,大文件逐行读。
下一课,我们学 模块与第三方库——学会 import 和 pip,站在千万开发者的肩膀上,不再重复造轮子。
先把「统计字数」和「简易日志」敲熟,字符串和文件的手感就长在你脑子里了。