到这一课为止,程序处理的数据都是我们手写的。但现实里,数据大多存在文件里:日志、配置、表格、文章……程序得能读进来处理、再写出去

这一课我们搞定三件事:字符串的十八般武艺正则表达式,以及文件的读写


一、字符串的十八般武艺

字符串是 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'

二、正则表达式:文本匹配的"万能钥匙"

前面那些字符串方法,适合处理你明确知道长什么样的字符串。但现实里,很多文本是"模式":比如"找出文章里所有的手机号""验证用户填的邮箱对不对""把日志里的日期抠出来"。这时用 splitreplace 会写得又长又脆。

正则表达式(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

两个新手提醒

  1. 正则前面加 r(如 r"\d+"),表示"原始字符串",别让 \ 被转义吃掉。
  2. 正则不需要背,用到再查,但 \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("程序结束")

十、小结

  1. 字符串方法 + 正则双管齐下:strip/split/join/replace 处理明确文本;re.findall/re.search/re.sub 按"模式"匹配(\d\w+()),模式前记得加 r
  2. 格式化用 f-stringf"{变量:.2f}" 又简单又好用。
  3. 文件读写用 with open(..., encoding="utf-8")r 读、w 覆盖写、a 追加写,大文件逐行读。

下一课,我们学 模块与第三方库——学会 importpip,站在千万开发者的肩膀上,不再重复造轮子。

先把「统计字数」和「简易日志」敲熟,字符串和文件的手感就长在你脑子里了。

标签: none

添加新评论