Python初学者必备的文件读写指南


Posted in Python onJune 23, 2021

一、如何将列表数据写入文件

 ⾸先,我们来看看下⾯这段代码,并思考:这段代码有没有问题,如果有问题的话,要怎么改?

li = ['python',' is',' a',' cat']
with open('test.txt','w') as f:
    f.write(li)

现在公布答案,这段代码会报错:

TypeError Traceback (most recent call last)
<ipython-input-6-57e0c2f5a453> in <module>()
        1 with open('test.txt','w') as f:
----> 2  f.write(li)
TypeError: write() argument must be str, not list

以上代码的想法是将list列表内容写⼊txt⽂件中,但是报错 TypeError: write() argument must be str。  

就是说,write()⽅法必须接受字符串(str)类型的参数。 Python中内置了str()⽅法,可以返回字符串版本的对象(Return a string version of object)。所以,上⾯的例⼦中,我们试试把 f.write(li) 改为 f.write(str(li)) ,先做⼀下字符串类型的转化看看。代码略。 这次没有报错了,但是打开⽂件就傻眼了吧,写⼊的内容是“['python',' is',' a',' cat']”。怎么才能写 成“python is a cat”呢? ⽂件写操作还有⼀个writelines()⽅法,它接收的参数是由字符串组成的序列(sequence),实际写⼊的效果是将全部字符串拼接在⼀起。字符串本身也是⼀种序列,所以当参数是字符串的时候,writelines()⽅法等价于write()。

# 以下3种写法等价,都是写⼊字符串“python is a cat” 
In [20]: with open('test.txt','w') as f: 
    ...: f.writelines(['python',' is',' a',' cat']) 
    ...: f.writelines('python is a cat') 
    ...: f.write('python is a cat') 
# 以下2种写法等价,都是写⼊列表的字符串版本“['python',' is',' a',' cat']” 
In [21]: with open('test.txt','w') as f: 
    ...: f.write(str(['python',' is',' a',' cat'])) 
    ...: f.writelines(str(['python',' is',' a',' cat'])) 
# 作为反例,以下写法都是错误的: 
In [22]: with open('test.txt','w') as f: 
    ...: f.writelines([2018,'is','a','cat']) # 含⾮字符串 
    ...: f.write(['python','is','a','cat']) # ⾮字符串

由上可知,当多段分散的字符串存在于列表中的时候,要⽤writelines()⽅法,如果字符串是⼀整段,那直 接使⽤write()⽅法。如果要以整个列表的形式写⼊⽂件,就使⽤str()⽅法做下转化。 这个问题还没结束,如果列表中就是有元素不是字符串,⽽且要把全部元素取出来,怎么办呢? 那就不能直接使⽤write()和writelines()了,需要先⽤for循环,把每个元素取出来,逐⼀str()处理。  

In [37]: content=[1,' is',' everything'] 
In [38]: with open('test.txt','w') as f: 
...: for i in content: 
...: f.write(str(i))

需要注意的是,writelines()不会⾃动换⾏。如果要实现列表元素间的换⾏,⼀个办法是在每个元素后⾯加 上换⾏符“\n”,如果不想改变元素,最好是⽤for循环,在写⼊的时候加在末尾:for i in content: f.writelines(str(i)+“\n”)   引申⼀下,经过实验,数字及元祖类型也可以作为write()的参数,不需转化。但是dict字典类型不可以, 需要先⽤str()处理⼀下。字典类型⽐较特殊,最好是⽤json.dump()⽅法写到⽂件。   总结⼀下,write()接收字符串参数,适⽤于⼀次性将全部内容写⼊⽂件;writelines()接收参数是由字符串 组成的序列,适⽤于将列表内容逐⾏写⼊⽂件。str()返回Python对象的字符串版本,使⽤需注意。

二、如何从文件中读取内容?

从⽂件中读取内容有如下⽅法:

file.read([size]) 
从⽂件读取指定的字节数,如果未给定或为负则读取所有。 
file.readline([size]) 
读取整⾏,包括 "\n" 字符。 
file.readlines([sizeint]) 
读取所有⾏并返回列表,若给定sizeint>0,则是设置⼀次读多少字节,这是为了减轻读取压⼒。

简⽽⾔之,在不传参数的情况下,read()对应write(),读取全部内容;readlines()对应writelines(),读取 全部内容(含换⾏符)并以列表形式返回,每个换⾏的内容作为列表的⼀个元素。

In [47]: with open('test.txt','r') as f: 
...: print(f.read()) 
1 is everything. 
python is a cat. 
this is the end. 
In [48]: with open('test.txt','r') as f: 
...: print(f.readlines()) 
['1 is everything.\n', 'python is a cat.\n', 'this is the end.']

但是,以上两个⽅法有个缺点,当⽂件过⼤的时候,⼀次性读取太多内容,会对内存造成极⼤压⼒。读操作还有⼀个readline()⽅法,可以逐⾏读取。

In [49]: with open('test.txt','r') as f: ...: print(f.readline()) 1 is everything.

readline()读取第⼀⾏就返回,再次调⽤f.readline(),会读取下⼀⾏。 这么看来,readline()太笨拙了。那么,有什么办法可以优雅地读取⽂件内容呢? 回过头来看readlines()⽅法,它返回的是⼀个列表。这不奇怪么,好端端的内容为啥要返回成列表呢? 再想想writelines()⽅法,把字符串列表写⼊⽂件正是这家伙⼲的事,readlines()⽅法恰恰是它的逆操作! ⽽writelines()⽅法要配合for循环,所以我们把readlines()与for循环结合,看看会怎样。

In [61]: with open('test.txt','r') as f: 
    ...: for line in f.readlines(): 
    ...: print(line) 
1 is everything. 
python is a cat. 
this is the end. 
# 读取内容包含换⾏符,所以要strip()去掉换⾏符 
In [62]: with open('test.txt','r') as f: 
    ...: for line in f.readlines(): 
    ...: print(line.strip()) 
1 is everything. 
python is a cat. 
this is the end.

总结⼀下,readline()⽐较鸡肋,不咋⽤;read()适合读取内容较少的情况,或者是需要⼀次性处理全部内容的情况;⽽readlines()⽤的较多,⽐较灵活,因为for循环是⼀种迭代器,每次加载部分内容,既减少内 存压⼒,⼜⽅便逐⾏对数据处理。

三、多样需求的读写任务

前两部分讲了⽂件读写的⼏⼤核⼼⽅法,它们能够起作⽤的前提就是,需要先打开⼀个⽂件对象,因为只有在⽂件操作符的基础上才可以进⾏读或者写的操作。   打开⽂件⽤的是open()⽅法,所以我们再继续讲讲这个⽅法。open() ⽅法⽤于打开⼀个⽂件,并返回⽂件对象,在对⽂件进⾏处理过程都需要使⽤到这个函数,如果该⽂件⽆法被打开,会抛出 OSError。

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

open()⽅法的参数⾥file(⽂件)是必需的,其它参数最常⽤的是mode(模式)和encoding(编码)。 先说说encoding,⼀般来说,打开⽂件的编码⽅式以操作系统的默认编码为准,中⽂可能会出现乱码,需要加encoding='utf-8'。

In [63]: with open('test.txt','r') as f: 
    ...: for line in f.readlines(): 
    ...: print(line.strip()) 
----------------------- 
UnicodeDecodeError Traceback (most recent call last) 
<ipython-input-63-731a4f9cf707> in <module>() 
    1 with open('test.txt','r') as f: 
----> 2 for line in f.readlines(): 
    3 print(line.strip()) 
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in positio n 26: illegal multibyte sequence 
In [65]: with open('test.txt','r',encoding='utf-8') as f: 
    ...: for line in f.readlines(): 
    ...: print(line.strip()) 
python is a cat.

再说mode,它指定⽂件打开的模式。

r': 以只读模式打开(缺省模式)(必须保证⽂件存在) 
'w':以只写模式打开。若⽂件存在,则清空⽂件,然后重新创建;若不存在,则新建⽂件。 
'a':以追加模式打开。若⽂件存在,则会追加到⽂件的末尾;若⽂件不存在,则新建⽂件。 
常⻅的mode组合 
'r'或'rt': 默认模式,⽂本读模式 
'w'或'wt': 以⽂本写模式打开(打开前⽂件会被清空) 
'rb': 以⼆进制读模式打开 'ab': 以⼆进制追加模式打开 
'wb': 以⼆进制写模式打开(打开前⽂件会被清空) 
'r+': 以⽂本读写模式打开,默认写的指针开始指在⽂件开头, 因此会覆写⽂件 
'w+': 以⽂本读写模式打开(打开前⽂件会被清空) 
'a+': 以⽂本读写模式打开(写只能写在⽂件末尾) 
'rb+': 以⼆进制读写模式打开 
'wb+': 以⼆进制读写模式打开(打开前⽂件会被清空) 
'ab+': 以⼆进制读写模式打开

初看起来,模式很多,但是,它们只是相互组合罢了。建议记住最基本的w、r、a,遇到特殊场景,再翻看⼀下就好了。

四、从with语句到上下文管理器

基础部分讲完了,下⾯是进阶部分。知其然,更要知其所以然。

1、with语句是初学者必会常识

⾸先,要解释⼀下为啥前⽂直接就⽤了with语句。with语句是读写⽂件时的优雅写法,这已经默认是Python初学者必会的常识了。如果你还不会,先看看⽤和不⽤with语句的对⽐:

# 不⽤with语句的正确写法 
try: 
    f = open('test.txt','w') 
    f.writelines(['python',' is',' a',' cat']) 
finally: 
    if f: 
        f.close()
# 使⽤with语句的正确写法 
with open('test.txt','w') as f:
    f.writelines(['python',' is',' a',' cat'])

因为⽂件对象会占⽤操作系统的资源,并且操作系统同⼀时间能打开的⽂件数量是有限的,所以open()⽅法之后⼀定要调⽤close()⽅法。另外,读写操作可能出现IO异常的情况,所以要加try...finally,保证⽆论如何,都会调⽤到close()⽅法。   这样写万⽆⼀失,但是实在繁琐,⼀不⼩⼼还可能漏写或者写错。⽽with语句会保证调⽤close(),只需⼀⾏代码,简直不要太优雅!所以,with语句是Python初学者必会技能。

2、什么是上下⽂管理器?

下⾯,重头戏来了,什么是上下⽂管理器(context manager)?

上下⽂管理器是这样⼀个对象:它定义程序运⾏时需要建⽴的上下⽂,处理程序的进⼊和退出,实现了上下⽂管理协议,即在对象中定义了 __enter__() 和 __exit__() ⽅法。 __enter__():进⼊运⾏时的上下⽂,返回运⾏时上下⽂相关的对象,with 语句中会将这个返回值绑定到⽬标对象。 __exit__(exception_type, exception_value, traceback):退出运⾏时的上下⽂,定义在块执⾏(或终⽌)之后上下⽂管理器应该做什么。它可以处理异常、清理现场或者处理 with 块中语句执⾏完成之后需要处理的动作。

注意 enter 和 exit 的前后有两个下划线,Python 中⾃带了很多类似的⽅法,它们是很神秘⼜很强⼤的存在,江湖⼈常常称其为“⿊魔法”。例如,迭代器协议就实现了__iter__⽅法。   在Python的内置类型中,很多类型都是⽀持上下⽂管理协议的,例如 file、thread.LockType、 threading.Lock 等等。上下⽂管理器⽆法独⽴使⽤,它们要与 with 相结合,with 语句可以在代码块运⾏前进⼊⼀个运⾏时上下⽂(执⾏__enter__⽅法),并在代码块结束后退出该上下⽂(执⾏__exit__⽅法)。   with 语句适⽤于对资源进⾏访问的场合,确保不管使⽤过程中是否发⽣异常都会执⾏必要的“清理”操作,释放资源,⽐如⽂件使⽤后⾃动关闭、线程中锁的⾃动获取和释放等。  

3、⾃定义上下⽂管理器

除了Python的内置类型,任何⼈都可以定义⾃⼰的上下⽂管理器。下⾯是⼀个示例:

class OpenFile(object):
    def __init__(self,filename,mode):
def open_file(name):
    ff = open(name, 'w')
    ff.write("enter now\n")
    try:
        yield ff
    except RuntimeError:
        pass
        ff.write("exit now")
        ff.close()
    with open_file('test.txt') as f:
        f.write('Hello World!\n')

最终写⼊⽂件的结果是:

enter now Hello World! exit now

上下⽂管理器必须同时提供 enter() 和 exit() ⽅法的定义,缺少任何⼀个都会导致 AttributeError。   上下⽂管理器在执⾏过程中可能会出现异常,exit() 的返回值会决定异常的处理⽅式:返回值等于 False,那么这个异常将被重新抛出到上层;返回值等于 True,那么这个异常就被忽略,继续执⾏后⾯的代码。exit() 有三个参数(exception_type, exception_value, traceback),即是异常的相关信息。

4、contextlib实现上下⽂管理器

上例中,⾃定义上下⽂管理器的写法还是挺繁琐的,⽽且只能⽤于类级别。为了更好地辅助上下⽂管理,Python 内置提供了 contextlib 模块,进⽽可以很⽅便地实现函数级别的上下⽂管理器。   该模块本质上是通过装饰器(decorators)和⽣成器(generators)来实现上下⽂管理器,可以直接作⽤于函数/对象,⽽不⽤去关⼼ enter() 和 exit() ⽅法的具体实现。   先把上⾯的例⼦改造⼀下,然后我们再对照着解释:

from contextlib import contextmanager 
 
@contextmanager
def open_file(name): 
    ff = open(name, 'w') 
    ff.write("enter now\n") 
    try: 
        yield ff 
    except RuntimeError: 
        pass 
    ff.write("exit now") 
    ff.close() 
with open_file('test.txt') as f: 
    f.write('Hello World!\n')

contextmanager 是要使⽤的装饰器,yield 关键字将普通的函数变成了⽣成器。yield 的返回值(ff)等于上例__enter__()的返回值,也就是 as 语句的值(f),⽽ yield 前后的内容,分别是__enter__() 和__exit__() ⽅法⾥的内容。   使⽤ contextlib,可以避免类定义、__enter__() 和 __exit__() ⽅法,但是需要我们捕捉可能的异常(例如,yield 只能返回⼀个值,否则会导致异常 RuntimeError),所以 try...except 语句不能忽略。  

到此这篇关于Python初学者必备的文件读写指南的文章就介绍到这了,更多相关Python文件读写内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
探索Python3.4中新引入的asyncio模块
Apr 08 Python
详解python实现识别手写MNIST数字集的程序
Aug 03 Python
python基础梳理(一)(推荐)
Apr 06 Python
python redis连接 有序集合去重的代码
Aug 04 Python
python多线程案例之多任务copy文件完整实例
Oct 29 Python
使用IDLE的Python shell窗口实例详解
Nov 19 Python
python3 BeautifulSoup模块使用字典的方法抓取a标签内的数据示例
Nov 28 Python
python3中pip3安装出错,找不到SSL的解决方式
Dec 12 Python
python dataframe NaN处理方式
Dec 26 Python
python实现文法左递归的消除方法
May 22 Python
浅谈Python __init__.py的作用
Oct 28 Python
利用python实时刷新基金估值(摸鱼小工具)
Sep 15 Python
总结Python连接CS2000的详细步骤
python图片灰度化处理的几种方法
详解Python中的进程和线程
详解Go语言运用广度优先搜索走迷宫
常用的Python代码调试工具总结
Django+Celery实现定时任务的示例
Python django中如何使用restful框架
You might like
PHP实现的curl批量请求操作示例
2018/06/06 PHP
javascript静态的url如何传递
2007/05/03 Javascript
setAttribute 与 class冲突解决
2008/02/17 Javascript
ExtJS 2.2.1的grid控件在ie6中的显示问题
2009/05/04 Javascript
JQUERY实现左侧TIPS滑进滑出效果示例
2013/06/27 Javascript
JS两种定义方式的区别、内部原理
2013/11/21 Javascript
jQuery仿360导航页图标拖动排序效果代码分享
2015/08/24 Javascript
基于javascript实现checkbox复选框实例代码
2016/01/28 Javascript
Javascript基础_标记文字的实现方法
2016/06/14 Javascript
D3.js实现直方图的方法详解
2016/09/25 Javascript
原生js实现放大镜效果
2017/01/11 Javascript
vue双向数据绑定知识点总结
2018/04/18 Javascript
详解一次Vue低版本安卓白屏问题的解决过程
2019/05/30 Javascript
Node 代理访问的实现
2019/09/19 Javascript
JavaScript大数相加相乘的实现方法实例
2020/10/18 Javascript
[06:21]完美世界亚洲区首席发行官竺琦TI3采访
2013/08/26 DOTA
[50:22]完美盛典-2018年度红毯走秀
2018/12/16 DOTA
Python使用xlrd模块操作Excel数据导入的方法
2015/05/26 Python
通过数据库向Django模型添加字段的示例
2015/07/21 Python
图文详解WinPE下安装Python
2016/05/17 Python
解决python3中自定义wsgi函数,make_server函数报错的问题
2017/11/21 Python
Python实现PS图像调整黑白效果示例
2018/01/25 Python
Flask之请求钩子的实现
2018/12/23 Python
对Python 语音识别框架详解
2018/12/24 Python
Python静态类型检查新工具之pyright 使用指南
2019/04/26 Python
对django layer弹窗组件的使用详解
2019/08/31 Python
VS2019+python3.7+opencv4.1+tensorflow1.13配置详解
2020/04/16 Python
深入了解python列表(LIST)
2020/06/08 Python
CSS3属性background-size使用指南
2014/12/09 HTML / CSS
匡威比利时官网:Converse Belgium
2017/04/13 全球购物
硕士研究生求职自荐信范文
2014/03/11 职场文书
见习报告的格式
2014/10/31 职场文书
和领导吃饭祝酒词
2015/08/11 职场文书
Redis高可用集群redis-cluster详解
2022/03/20 Redis
微前端qiankun改造日渐庞大的项目教程
2022/06/21 Javascript
Mysql如何查看是否使用到索引
2022/12/24 MySQL