Python 文件操作魔法手册:open函数的终极艺术
Python 文件操作魔法手册:open 函数的终极艺术
对话实录
小白:(崩溃)我写了open("data.txt"),为什么报错?
专家:(掏出魔法书)文件操作要小心,模式、编码都要注意!
open 函数基础三连击
1. 基本用法
# 读取文件
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read()
# 写入文件
with open("output.txt", "w") as f:
f.write("Hello World!")
专家提醒:一定要用with语句,它能在代码块结束时自动关闭文件,防止资源泄露。想象一下,你打开一扇门进入房间,离开时却忘记关门,这可能会带来安全隐患。在文件操作中,忘记关闭文件就如同忘记关门,可能导致数据丢失或系统资源被占用。使用with语句就像是有一个贴心的助手,在你离开房间时自动帮你关门。
2 open函数参数介绍
open函数的参数
open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True)
1)file:传入不带路径的字符串(文本文件/二进制文件)或者带有绝对路径或者相对路径的文件。如果传入的文件不存在,默认会创建文件。
如果传入的文件不合要求,会报错如下:
2)mode:指明文件打开模式的可选字符串。默认为‘r’表示以文本模式读取。
字符 | 含意 |
'r' | 只读取(默认);文件必须存在,否则会报错。 |
'w' | 只写入,是覆盖式写入(比如第一次写入后,再次打开以w方式写入会覆盖原内容。) |
'x' | 排它性创建,如果文件已存在则失败。如果文件不存在则创建,模式为写入,与'w'类似。 |
'a' | 只写入,再次打开文件并不会覆盖,而是在末尾追加写入。 |
'b' | 表示二进制模式读取和写入,不能单独使用,须与'r','w','a','x'配合使用。 比如打开一张图片文件,如果用文本模式打开,看到的将是一堆乱码,因为图片数据是二进制格式,需要用二进制模式正确读取。 |
't' | 文本模式(默认),与'r','w','a','x'配合使用。 比如'r'与'rt'同义,一般直接使用'r'。 |
'+' | 打开用于更新(读取与写入),不能单独使用,须与'r','w','a','x'配合使用。 |
以上模式结合使用:
a. 只读取:'r',’rb’
b. 只写入:'w',’wb’,’a’,’ab’,'x','xb'
c. 读取和写入:'r+',’r+b’,'w+',’w+b’,’a+’,’a+b’,’x+’,’x+b’
- 'r+':以读写模式打开文件,文件指针会放在文件的开头。
- 'w+':以读写模式打开文件,原文件内容会被删除。(慎用)
- 'a+':以读写模式打开文件用于追加,文件指针会放在文件的末尾(表现为以a+模式打开文件直接读取内容,读取的内容为空)。
在读取和写入模式下我们可以使用seek()方法来移动文件指针到指定位置,以便在文件的任意位置进行读写操作。
3)buffering:参数可选,表示设置缓冲策略,默认为None。0表示无缓冲(仅适用于二进制模式),大于0表示缓冲区的大小(以字节为单位)。
4)encoding:参数可选,默认为系统默认编码(在Python 3中通常是UTF-8);在文本模式下可以指定编码,在二进制模式下不需要指定编码。
5)errors:参数可选,指定编码和解码错误的处理方式。默认为None,跟值'strict'效果一样,表示严格处理错误,其他值如'ignore'用于忽略错误、'replace'替换一些错误标记等。
5)newline:参数可选,用于控制文件读取和写入时行的结束符。默认为None,表示使用系统默认的行结束符,比如'\r\n','\n'。在文本模式下读取文件时,默认把平台特定的行结束符(Unix 上为 \n, Windows 上为 \r\n)转换为 \n。在文本模式下写入数据时,默认把 \n 转换回平台特定结束符。
6)closefd:参数可选,closefd默认为True表示当文件对象被关闭时,同时关闭文件描述符,防止资源泄漏。当closefd为False指当文件对象被关闭时,不关闭文件描述符。当file参数传入的是文本文件时,该参数必须为Ture,否则会报错。
3 常用方法介绍
1)读取方法
- read(size=-1):从文件中读取并返回指定数量的字符或者字节,如果不指定size则读取并返回文件的全部内容。
- readline(size=-1):从文件中读取并返回一行(直到换行符 \n),或返回指定数量的字节。
- readlines(hint=-1):读取所有行并返回列表,其中每一行都是一个字符串。如果指定了 hint,则读取指定数量的字符或者字节。
2)写入方法
- write(str):将字符串或字节写入文件,只写入一行,并且不会在末尾添加换行符,需要自己添加。
- writelines(lines):向文件写入一个字符串或者字节列表,可写入多行,并且不会在末尾添加换行符,需要自己添加。
3)移动文件对象指针位置
- seek(offset, whence=0):移动文件读取指针到指定位置。offset 是指从 whence 指定的位置开始计算的字符或者字节数。whence 的值为 0(文件开头,默认值)、1(当前位置)或 2(文件末尾)。
- tell():返回文件当前的指针位置。
4)关闭文件对象
close():关闭文件对象。
其他方法
flush():将缓冲区的内容写入文件,但不关闭文件。
fileno():返回文件的描述符(一个小的非负整数)。
isatty():如果文件是一个与终端设备(tty)相关联的,则返回 True,否则返回 False.
readable()、writable()、seekable():分别用于检查文件是否可读、可写、以及是否支持 seek() 操作。
truncate(size=None):截断文件到指定大小。如果 size 未指定,则截断文件到当前位置。如果当前位置大于文件大小,则文件将被扩展,并且扩展部分的内容将用零字节填充。
4 常用属性介绍
- name 属性:返回打开文件的名称。
- closed 属性:返回一个布尔值,指示文件是否已关闭。当文件被成功打开时,closed 属性为 False。一旦文件被关闭,closed属性为True。
- mode属性:返回open函数传入的mode值。
- encoding属性:返回open函数使用的编码。
- buffer属性:返回open函数的buffer配置。
- errors属性:返回open函数的errors配置。
六大实战案例
案例 1:逐行读取大文件
# 错误示范
with open("big.txt") as f:
lines = f.readlines() # 内存爆炸!
这种方式会一次性将整个文件读入内存,如果文件非常大,可能会耗尽系统内存,导致程序崩溃。
# 正确做法
with open("big.txt") as f:
for line in f: # 逐行读取
process(line)
逐行读取大文件,每次只在内存中处理一行数据,大大降低了内存压力。
案例 2:CSV 文件处理
import csv
with open("data.csv", newline="") as f:
reader = csv.reader(f)
for row in reader:
print(row)
CSV(Comma - Separated Values)文件常用于存储表格数据,如数据库导出的数据。使用csv模块可以方便地读取和写入 CSV 文件。在这个例子中,newline=""参数用于避免在读取 CSV 文件时出现额外的空行,确保数据读取的准确性。
案例 3:JSON 文件读写
import json
# 读取json文件内容为python对象
with open("data.json") as f:
data = json.load(f)
# 将python对象写入json文件
with open("output.json", "w") as f:
json.dump(data, f, indent=2)
JSON(JavaScript Object Notation)文件常用于存储结构化数据,在 Web开发和数据交换中广泛应用。json.load(f)用于将文件中的JSON 数据解析为Python 对象,json.dump(data, f, indent = 2)则将 Python 对象转换为JSON 格式并写入文件,indent = 2参数使生成的JSON文件更加美观易读,数据层次结构一目了然。
案例 4:XML 文件解析
import xml.etree.ElementTree as ET
tree = ET.parse("data.xml")
root = tree.getroot()
for child in root:
print(child.tag, child.attrib)
XML(eXtensible Markup Language)也是一种常用的结构化数据格式,常用于配置文件和数据交换。通过xml.etree.ElementTree模块可以轻松解析 XML 文件。在这个例子中,我们使用ET.parse方法读取 XML 文件,然后获取根元素,并遍历根元素的子元素,打印出每个子元素的标签和属性。
案例 5:日志文件处理
import logging
logging.basicConfig(filename='app.log', level=logging.INFO)
logging.info('程序开始运行')
try:
result = 1 / 0
except ZeroDivisionError as e:
logging.error(f'发生错误: {e}')
logging.info('程序结束运行')
在开发应用程序时,记录日志是非常重要的。它可以帮助我们追踪程序的运行状态,排查错误。通过logging模块,我们可以方便地将日志信息写入文件。在这个例子中,我们设置日志级别为INFO,记录程序的开始和结束运行信息,并在发生ZeroDivisionError错误时记录错误信息到app.log文件中。
案例 6:图片文件处理(简单的图片格式转换)
from PIL import Image
try:
img = Image.open('input.jpg')
img.save('output.png', 'PNG')
except Exception as e:
print(f'处理图片时发生错误: {e}')
使用 Python 的PIL(Python Imaging Library)库可以对图片进行各种处理,如格式转换、裁剪、调整大小等。在这个例子中,我们将一张 JPEG 格式的图片转换为 PNG 格式。Image.open方法用于打开图片文件,img.save方法用于将图片保存为指定格式。
四大血泪陷阱
忘记关闭文件
# 错误示范
f = open("data.txt")
content = f.read()
# 忘记f.close()
在这种情况下,文件在读取操作完成后没有被关闭,可能会导致资源泄露。如果程序中多次出现这种情况,可能会耗尽系统的文件描述符资源,导致程序无法再打开新的文件。
# 正确做法
with open("data.txt") as f:
content = f.read()
with语句会自动管理文件的生命周期,在代码块结束时关闭文件,确保资源得到正确释放。
编码问题
# 错误示范
with open("data.txt") as f: # 默认编码可能出错
content = f.read()
不同的操作系统和文本编辑器可能使用不同的默认编码。如果在打开文件时不指定编码,可能会导致读取文件时出现乱码或UnicodeDecodeError错误。例如,在 Windows 系统中,默认编码可能是cp936,而在处理包含非 ASCII 字符的文件时,可能会出现编码不匹配的问题。
# 正确做法
with open("data.txt", encoding="utf-8") as f:
content = f.read()
显式指定编码为utf - 8,这是一种通用的编码格式,能够处理各种语言的字符,避免编码问题。
路径问题
# 错误示范
open("data.txt") # 相对路径可能出错
使用相对路径时,文件的实际位置取决于当前工作目录。如果在不同的环境中运行程序,或者当前工作目录被改变,可能会导致找不到文件。比如,在一个脚本中使用相对路径open("data.txt"),在脚本所在目录运行程序时可以正常找到文件,但如果在其他目录运行,就可能找不到该文件。
# 正确做法
from pathlib import Path
file = Path(__file__).parent / "data.txt"
with open(file) as f:
content = f.read()
使用Pathlib模块来处理路径,它提供了一种跨平台的路径操作方式。Path(__file__).parent获取当前脚本所在的目录,然后通过/运算符拼接文件名,确保无论在何种环境下都能正确定位文件。
文件模式误用
# 错误示范
with open("data.txt", "w") as f:
data = f.read() # 写入模式下不能读取
在这种情况下,以w(写入)模式打开文件后,尝试读取文件内容,会导致io.UnsupportedOperation错误,因为w模式只允许写入操作,不允许读取。
# 正确做法
with open("data.txt", "r+") as f:
data = f.read()
f.write("追加的内容")
如果需要对文件进行读写操作,应使用合适的模式,如r+模式,它允许在读取文件的同时进行写入操作。
专家工具箱
1. 二进制文件操作
# 读取图片
with open("image.png", "rb") as f:
data = f.read()
# 写入二进制
with open("output.bin", "wb") as f:
f.write(b"\x00\x01\x02")
二进制文件操作在处理图片、音频、视频等非文本文件时非常重要。在读取图片时,使用rb(二进制读取)模式,确保文件内容被正确读取。写入二进制数据时,使用wb(二进制写入)模式,数据以字节形式写入文件。例如,b"\x00\x01\x02"表示写入三个字节的数据,每个字节的值分别为 0、1、2。
2. 文件指针操作
with open("data.txt") as f:
f.seek(10) # 移动到第10字节
print(f.read(5)) # 读取5个字符
文件指针用于指示当前文件读取或写入的位置。f.seek(offset, whence)方法用于移动文件指针,offset表示偏移量,whence表示参考位置,0 表示文件开头,1 表示当前位置,2 表示文件末尾。在这个例子中,f.seek(10)将文件指针移动到第 10 个字节的位置,然后f.read(5)从该位置读取 5 个字符。通过灵活操作文件指针,可以实现对文件内容的随机访问,比如读取文件中间某一段特定的数据。
3. 临时文件
from tempfile import TemporaryFile
with TemporaryFile("w+") as f:
f.write("临时数据")
f.seek(0)
print(f.read())
临时文件在程序运行过程中用于临时存储数据,当程序结束时,临时文件会自动被删除。TemporaryFile函数创建一个临时文件,w+模式表示可读写。在这个例子中,我们先写入一些临时数据,然后将文件指针移回开头,读取并打印这些数据。使用临时文件可以避免在磁盘上留下不必要的文件,同时保证数据的安全性,因为临时文件在程序结束后会自动消失,不用担心数据残留问题。
小白:(献上膝盖)原来文件操作这么讲究!
专家:(扶起小白)记住:文件操作要小心,资源泄露是大忌!