打开文件
语法格式
打开文件使用函数open。语法格式如下:
open(filename[,access_mode][,buffering][,encoding])
参数说明:
- filename变量:是一个包含要访问的文件名称的字符串值。必须。
- access_mode变量:指打开文件的模式,包括:只读、写入、读写等。非必须。默认的文件访问模式为只读(r)
- buffering:缓存值大小,0代表不缓存,如果取负数,代表使用系统默认的缓存大小,如果取整数,将使用该值作为缓存大小。
- encoding:指定打开的字符编码
示例如下:
#! /usr/bin/python
# -*-coding:UTF-8-*-
path = 'h:/test.txt'
#需要确保该文件存在,否则会报异常
file = open(path)
print('文件名是:',file.name)
输出:
文件名是: h:/test.txt
绝对路径和相对路径
概念 | 说明 |
---|---|
绝对路径 | 从根目录开始,windows系统从盘符开始,linux系统从usr、home等根文件开始。指定是文件存在的真实路径,通过资源管理器中输入文件地址可以访问到的路径 |
相对路径 | 相对于当前程序文件所在目录的路径。比如当前程序目录的绝对路径是:d:\python\pyspace,如果使用相对路径,当和程序目录相同时,可以使用符号单个点.代替这个路径值。还可以使用符号两个点..代替上级目录。 |
实例如下:
#使用相对路径
#创建一个文件,如果存在就覆盖,不存在则新建
import os
path = './testfile'
if not os.path.exists(path):
os.mkdir(path)
path = './testfile/test2.txt'
file = open(path,'w')
print('文件名是:',file.name)
输出:
文件名是: ./testfile/test2.txt
文件模式
打开文件的模式:access_mode,详见以下表格
模式 | 描述 |
---|---|
t | 文本模式 (默认)。 |
x | 写模式,新建一个文件,如果该文件已存在则会报错。 |
b | 二进制模式。 |
+ | 打开一个文件进行更新(可读可写)。 |
U | 通用换行模式(不推荐)。 |
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
下面的表格很好的总结了这几种模式:
模式 | r | r+ | w | w+ | a | a+ |
---|---|---|---|---|---|---|
读 | ✓ | ✓ | ✓ | ✓ | ||
写 | ✓ | ✓ | ✓ | ✓ | ✓ | |
创建 | ✓ | ✓ | ✓ | ✓ | ||
覆盖 | ✓ | ✓ | ||||
指针在开始 | ✓ | ✓ | ✓ | ✓ | ||
指针在结尾 | ✓ | ✓ |
说明:
- 使用open函数时,不指定参数和指定参数为读模式的效果是一样的,因为access_mode的默认值就是读。
- +参数可以用在任意模式中,代表读和写都是允许的。
- 当参数带上字母b时,表示已二进制的方式操作文件。如果不指定,则只能处理文本文件。
IO编程
IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。
比如你打开浏览器,访问新浪首页,浏览器这个程序就需要通过网络IO获取新浪的网页。浏览器首先会发送数据给新浪服务器,告诉它我想要首页的HTML,这个动作是往外发数据,叫Output,随后新浪服务器把网页发过来,这个动作是从外面接收数据,叫Input。所以,通常,程序完成IO操作会有Input和Output两个数据流。当然也有只用一个的情况,比如,从磁盘读取文件到内存,就只有Input操作,反过来,把数据写到磁盘文件里,就只是一个Output操作。
IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和新浪服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。
由于CPU和内存的速度远远高于外设的速度,所以,在IO编程中,就存在速度严重不匹配的问题。举个例子来说,比如要把100M的数据写入磁盘,CPU输出100M的数据只需要0.01秒,可是磁盘要接收这100M数据可能需要10秒,怎么办呢?有两种办法:
第一种是CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行,这种模式称为同步IO;
另一种方法是CPU不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步IO。
同步和异步的区别就在于是否等待IO执行的结果。好比你去麦当劳点餐,你说“来个汉堡”,服务员告诉你,对不起,汉堡要现做,需要等5分钟,于是你站在收银台前面等了5分钟,拿到汉堡再去逛商场,这是同步IO。
你说“来个汉堡”,服务员告诉你,汉堡需要等5分钟,你可以先去逛商场,等做好了,我们再通知你,这样你可以立刻去干别的事情(逛商场),这是异步IO。
很明显,使用异步IO来编写程序性能会远远高于同步IO,但是异步IO的缺点是编程模型复杂。想想看,你得知道什么时候通知你“汉堡做好了”,而通知你的方法也各不相同。如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步IO的复杂度远远高于同步IO。
操作IO的能力都是由操作系统提供的,每一种编程语言都会把操作系统提供的低级C接口封装起来方便使用,Python也不例外。我们后面会详细讨论Python的IO编程接口。
基本文件方法
读和写
read()方法从一个打开的文件中读取字符串。需要注意的是,Python字符串可以是二进制数据,而不仅仅是文本。语法:
file.read([count])
其中count参数是可选的,它代表读取的文本内容的字节长度。如果没有传入count,就会尝试尽可能多的读取文件,直到文件结束。
write()方法不会在字符串结尾添加换行符“\n”,语法格式如下:
file.write(string)
string是要向文件中写入的内容,可以是文本,也可以是二进制数据,该方法返回写入文件字符串的长度。
示例:
#read and write
_path = './testfile/test.txt'
_file = open(_path,'w+')
#在文件中写入:“hello world!welcome”
_file.write("hello world!welcome")
#注意:对于新写入文件的内容,只有在重新打开后才可以读取到
_file = open(_path,'r')
print('file.read(12)=',_file.read(12))
#追加写入
_file = open(_path,'a')
_file.write('!I am coming!')
_file = open(_path)
print('_file.read()=',_file.read())
#添加换行符,增加换行
_file = open(_path,'a')
_file.write('\nyou am coming too!')
_file = open(_path)
print('_file.read()=',_file.read())
输出:
file.read(12)= hello world!
_file.read()= hello world!welcome!I am coming!
_file.read()= hello world!welcome!I am coming!
you am coming too!
使用读和写操作,实现copy文件的例子:
# copy文件
def copyFile(source, target):
file = open(source, 'rb')
file2 = open(target, 'wb')
# file2 = open(target, 'ab')
_len = file2.write(file.read())
print('写入大小:', _len)
path = "h:/1.mp4"
path2 = "h:/test.mp4"
opyFile(path,path2)
提示:如果要读和写特定的编码格式的文件,需要在打开文件时指定编码格式,例如:
file.open("./test.txt",'r',encoding='UTF-8')
读写行
python为我们提供了readline()、readlines()和writelines()等方法用于行操作。示例如下:
file = open("./testfile/test.txt","w")
file.write('hello world\n')
file = open("./testfile/test.txt","a")
file.write('welcome')
file = open('./testfile/test.txt','r')
print('file.readline()',file.readline())
输出:
file.readline()= hello world
从结果中可知,readline()方法会从文件中读取一行。readline()方法也可以像read方法一样传入数值,读取对应的字符数,传入小于0的数值表示整行都输出。
如果将最后一行的方法改为使用readlines()方法:
# print('file.readline()=',file.readline())
print('file.readlines()=',file.readlines())
输出:
file.readlines()= ['hello world\n', 'welcome']
writelines()方法的使用,示例如下:
file = open("./testfile/test.txt","w")
str_list = ['hello world\n','welcome!']
file.writelines(str_list)
file = open('./testfile/test.txt','r')
print(file.read())
file = open('./testfile/test.txt','r')
print(file.readlines())
输出:
hello world
welcome!
['hello world\n', 'welcome!']
writelines方法和readlines方法相反,传给它一个字符串列表(任何序列或者可迭代对象),它会把所有字符串写入文件。
关闭文件
一般来讲,一个文件对象在退出文件后会自动关闭,但是由于在打开文件对的过程中,可能会出现各种异常,问了安全起见,可以使用close()方法可以显示的关闭文件。一般的做法为把close语句放在try,catch的finnally中。实例如下:
try:
file = open("./testfile/test.txt","r")
except Exception as e:
print(e)
finally:
if file:
file.close()
在Python中,使用with语句可以自动调用close方法。将上面的示例改写为:
with open("./testfile/test.txt","w") as file_123:
file_123.writelines(['hello world\n', 'welcome'])
文件重命名
Python的os模块为我们提供了rename()方法,即文件重命名。使用这个方法需要导入os模块。rename()方法的语法格式如下:
os.rename(current_file_name,new_file_name)
示例如下:
# file = open("./test.txt","w")
# file.close()
# 上下两种方法都可以
open("./test.txt","w")
import os
os.rename('test.txt','test123.txt')
删除文件
Python的os模块为我们提供了remove()方法,即删除文件。语法格式如下:
os.remove(file_name)
如果文件不存在于当前目录下,则文件名需要使用绝对路径。该方法没有返回值。实例如下:
try:
os.remove('./test123.txt')
except Exception:
print('文件不存在!')
对文件内容进行迭代
按字节处理
#按字节处理
file = open('./testfile/test.txt')
while True:
c_str = file.read(1)
print(c_str)
if not c_str:
break
打开一个文件,然后每次只读取1个字节,一直到读取结束为止
按行处理
#按行处理
file = open('./test.txt',encoding='UTF-8')
while True:
c_str = file.readline()
print(c_str)
if not c_str:
break
打开一个文件,每次只读取一行,一直到读取结束为止
使用fileinput实现懒(延迟)加载
前面介绍的方法(read、readline),在不带参数时(不指定读取的长度)都将文件中的所有内容读取到了内存。这种方式在读取大文件上时,非常消耗内存,容易造成内存溢出。因此,考虑使用while循环和readline方法替代。
在Python中,按行读取文件时,若能使用for循环,则称之为懒(延迟)加载,因为在操作过程中只读取实际需要的部分。实例如下:
path = './test.txt'
import fileinput
for line in fileinput.input(path,openhook=fileinput.hook_encoded('UTF-8')):
print(line)
文件迭代器
文件对象是可迭代的,因此可以在for循环使用文件对象,从而进行迭代。示例如下:
file = open('./test.txt',encoding='UTF-8')
for line in file:
print(line)
file.close()
StringIO函数
数据的读取除了通过文件,还可以在内存中进行。Python中的io模块提供了对str操作的StringIO函数。要把str写入StringIO,我们需要创建一个StringIO,然后像文件一样写入。示例如下:
from io import StringIO
strio = StringIO()
strio.write('hello world')
print(strio.getvalue())
输出如下:
hello world
要读取StringIO,还可以用str初始化StringIO,然后像读文件一样读取。示例如下:
strio = StringIO('hello\nworld\nI\'m coming')
while True:
line = strio.readline()
if line:
print(line.strip())
else:
break
输出:
hello
world
I'm coming
序列化和反序列化
序列化——所有变量都在内存中,把变量从内存中变成可以存储或可传输的过程叫做序列化。序列化是数据结构或对象转换成二进制串的过程。
反序列化——反过来,把变量内容从序列化后的对象重新读取到内存中的过程叫做反序列化。反序列化是将二进制串转换成数据结构或对象的过程。
一般序列化和反序列化
Python的pickle模块实现了基本数据序列和反序列化。
pickle.dumps()方法把任意对象序列转化成一个bytes,然后把这个bytes写入文件。也可以使用另一种方法,pickle.dump(),直接把对象序列化后写入一个文件对象中。
既然已经将内容序列化到文件中了,使用文件时就需要把对象从磁盘读到内存中。可以先把内容读到一个bytes中,然后用pickle.loads方法反序列化对象;也可以直接使用pickle.load()方法从一个文件对象中直接反序列化对象。示例如下:
import pickle
str = 'hello\nworld\nwelcome'
print(pickle.dumps(str))
fileName = open('dump.txt','wb')
#以下两种写法都正确
#写法1
# fileName.write(pickle.dumps(str))
#写法2
pickle.dump(str,fileName)
fileName.close()
fileName = open('dump.txt','rb')
# 输出为byte
# print(fileName.read())
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
# print(fileName.read().decode('utf-8'))
# 正确写法
print(pickle.load(fileName))
输出:
b'\x80\x03X\x13\x00\x00\x00hello\nworld\nwelcomeq\x00.'
hello
world
welcome
JSON序列化与反序列化
Python3中可以使用json模块对json数据进行编码(序列化)和解码(反序列化)。包含以下两个函数。
- json.dumps():对数据进行编码(序列化)
- json.loads()对数据进行解码(反序列化)
Python编码与json类型映射关系:
Python | JSON |
---|---|
dict | {} |
list,tuple | [] |
str | string |
Int or Float | number |
True/False | True/False |
None | null |
示例代码:
#Json序列化实例
import json
data = {'name':'小明','age':23,'sex':'男'}
#如果不加ensure_ascii=False 返回的json数据中文显示有问题,
#变成\uXXX的形式。这是因为中文以unicode 编码了,
#而默认是以ASCII解析的,中文不在ASCII编码中,所以无法显示
str = json.dumps(data,ensure_ascii=False)
print('json str = ',str)
print('python dict = ',data)
data2 = json.loads(str)
print('covert python dict = ',data)
输出:
json str = {"name": "小明", "sex": "男", "age": 23}
python dict = {'name': '小明', 'sex': '男', 'age': 23}
covert python dict = {'name': '小明', 'sex': '男', 'age': 23}
如果要处理的是文件而不是字符串,就可以使用json.dump(比上面的方法少个s)和json.load编码、解码JSON数据。示例如下:
with open('dump.txt','w') as f:
json.dump(data,f)
with open('dump.txt') as f:
data = json.load(f)
print(data)
输出:
{'sex': '男', 'age': 23, 'name': '小明'}
调试
当我们读取和写入文件时,经常遇到和空白符相关的问题。如:
str = '1 2\t 3\n 4'
print(str)
输出:
1 2 3
4
在这种情况下,Python为我们提供了repr函数,该函数可接受任何对象作为参数,并返回对象的字符串表示形式。如:
str = repr('1 2\t 3\n 4')
print(str)
输出:
'1 2\t 3\n 4'
网友评论