在Python文件处理中,文件指针的位置控制是核心技能。很多初学者在处理文件时会遇到"读取后无法再次读取"的问题,这通常是因为不了解文件指针的工作原理。
文件指针就像读书时的书签,记录着你当前阅读的位置。当你使用read()或readline()方法时,指针会不断向后移动。想要重新读取内容,必须使用seek()方法将指针移回起始位置。
seek()方法的两个参数需要特别注意:
python复制# 典型错误示例:读取后直接再次读取
f = open('data.txt', 'r')
content = f.read() # 指针已到文件末尾
print(content)
empty = f.read() # 这里会得到空字符串
f.close()
# 正确做法:使用seek重置指针
f = open('data.txt', 'r')
print(f.read())
f.seek(0) # 关键步骤:将指针移回文件开头
print(f.read()) # 现在可以再次读取
f.close()
重要提示:处理完文件后务必调用close()方法关闭文件,否则可能导致资源泄露。更推荐使用with语句自动管理文件关闭。
文件路径处理是另一个容易踩坑的地方。绝对路径和相对路径各有适用场景:
常见路径处理技巧:
__file__和os.path.dirname获取python复制import os
# 获取当前脚本所在目录
base_dir = os.path.dirname(os.path.abspath(__file__))
# 安全拼接路径
data_path = os.path.join(base_dir, 'data', 'example.csv')
# 使用with语句自动管理文件
with open(data_path, 'r', encoding='utf-8') as f:
content = f.read()
实际项目中,我建议建立一个专门的路径配置文件,集中管理所有文件路径,这样既保证了灵活性,又避免了路径硬编码的问题。
Python内置的csv模块远比直接使用字符串分割强大。很多开发者习惯自己解析CSV,这其实是在重复造轮子,而且容易出错。
csv模块的主要优势:
python复制import csv
# 标准读取方式
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
header = next(reader) # 读取标题行
for row in reader:
print(row)
# 更优雅的字典读取方式
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['name'], row['age'])
原始代码有几个可以改进的地方:
优化后的代码:
python复制import csv
from datetime import datetime
def parse_time(time_str):
"""将'HH:MM'格式时间转换为分钟数"""
try:
dt = datetime.strptime(time_str, '%H:%M')
return dt.hour * 60 + dt.minute
except ValueError:
return None
def load_schedule(filepath):
"""加载时辰数据"""
with open(filepath, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
return list(reader)
def find_time_period(schedule, minutes):
"""查找对应的时辰"""
# 处理子时特殊情况(23:00-1:00)
if minutes >= 23*60 or minutes < 1*60:
return schedule[0]
for period in schedule[1:]:
start = parse_time(period['start_time'])
end = parse_time(period['end_time'])
if start <= minutes <= end:
return period
return None
def main():
schedule = load_schedule('中国十二时辰.csv')
while True:
time_input = input("请输入时间(HH:MM),输入q退出:")
if time_input.lower() == 'q':
break
minutes = parse_time(time_input)
if minutes is None:
print("时间格式错误,请使用HH:MM格式")
continue
period = find_time_period(schedule, minutes)
if period:
print(f"时辰:{period['name']}")
print(f"属相:{period['zodiac']}")
print(f"描述:{period['description']}")
else:
print("未找到对应时辰")
if __name__ == '__main__':
main()
这个版本不仅更健壮,而且结构更清晰,每个函数只做一件事,方便后续维护和扩展。
直接从HTML中提取文本内容看似简单,但实际上有很多陷阱:
对于简单的静态HTML,可以使用正则表达式或字符串操作。但对于复杂情况,建议使用专业的HTML解析库如BeautifulSoup。
python复制from bs4 import BeautifulSoup
def extract_cities(html_file, output_csv):
with open(html_file, 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'html.parser')
# 查找所有class为para的div
paras = soup.find_all('div', class_='para')
cities = []
for para in paras:
text = para.get_text().strip()
if text: # 过滤空文本
cities.extend(text.split())
# 写入CSV
with open(output_csv, 'w', encoding='utf-8', newline='') as out_f:
writer = csv.writer(out_f)
writer.writerow(['城市名称']) # 标题行
for city in cities:
writer.writerow([city])
# 使用示例
extract_cities('中国城市名称大全.html', '城市大全.csv')
python复制# 更健壮的HTML处理方案
def safe_extract(html_file):
try:
with open(html_file, 'r', encoding='utf-8-sig') as f:
content = f.read()
if not content.strip():
raise ValueError("空文件")
soup = BeautifulSoup(content, 'lxml') # 使用lxml解析器
# ...其余处理逻辑
except Exception as e:
print(f"处理失败: {str(e)}")
return False
return True
当处理GB级别的大文件时,直接读取整个文件会消耗大量内存。这时应该采用流式处理:
python复制def process_large_file(filepath):
with open(filepath, 'r', encoding='utf-8') as f:
for line in f: # 逐行读取
process_line(line) # 处理单行
def process_line(line):
# 简单的行处理逻辑
if 'error' in line.lower():
write_to_log(line)
对于CSV大文件,csv模块本身就支持迭代式读取:
python复制def process_large_csv(filepath):
with open(filepath, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
if not row: # 跳过空行
continue
process_row(row)
使用watchdog库可以监控文件变化,实现自动化处理:
python复制from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class MyHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.src_path.endswith('.csv'):
print(f"检测到CSV文件变化: {event.src_path}")
process_file(event.src_path)
def start_monitoring(path):
event_handler = MyHandler()
observer = Observer()
observer.schedule(event_handler, path, recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
这个技巧特别适合需要实时处理新增数据的场景,比如日志分析、数据ETL等。
除了文本文件,Python也能很好地处理二进制文件,如图片、音频等:
python复制def copy_binary_file(src, dst):
with open(src, 'rb') as f_src, open(dst, 'wb') as f_dst:
while True:
chunk = f_src.read(4096) # 分块读取
if not chunk:
break
f_dst.write(chunk)
处理二进制文件时要注意:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 读取中文乱码 | 文件编码与打开编码不一致 | 尝试utf-8、gbk、gb2312等编码 |
| UnicodeDecodeError | 文件包含非法字符 | 使用errors='ignore'参数 |
| 写入后文件空白 | 文件未正确关闭 | 使用with语句或手动close() |
| 换行符混乱 | 跨平台差异 | 使用newline=''参数 |
缓冲区调整:处理大量小文件时,适当调整缓冲区大小
python复制with open('file.txt', 'rb', buffering=8192) as f:
# 8KB缓冲区
内存映射:超大文件处理使用mmap
python复制import mmap
with open('bigfile.bin', 'r+b') as f:
mm = mmap.mmap(f.fileno(), 0)
# 通过mm对象操作文件
并行处理:多线程/多进程处理独立文件
永远不要直接使用用户输入作为文件路径,防止路径遍历攻击
python复制# 不安全
user_input = input("输入文件名: ")
open(user_input, 'r') # 可能读取系统文件
# 安全做法
import os
base_dir = '/safe/directory'
filename = os.path.basename(user_input) # 去除路径
safe_path = os.path.join(base_dir, filename)
if not os.path.commonpath([base_dir, safe_path]) == base_dir:
raise ValueError("非法路径")
写入文件时检查磁盘空间
敏感文件设置适当权限
文件处理是Python编程中最基础也最常用的技能之一。从简单的文本处理到复杂的结构化数据解析,掌握这些技巧可以大幅提高开发效率。在实际项目中,我建议:
最后分享一个实用技巧:使用pathlib模块可以写出更优雅的路径操作代码,特别是在跨平台项目中:
python复制from pathlib import Path
# 创建目录(如果不存在)
data_dir = Path('data')
data_dir.mkdir(exist_ok=True)
# 路径拼接更直观
file_path = data_dir / 'example.csv'
# 链式调用
content = file_path.read_text(encoding='utf-8')