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 相关文章推荐
python入门之语句(if语句、while语句、for语句)
Jan 19 Python
Ruby使用eventmachine为HTTP服务器添加文件下载功能
Apr 20 Python
使用Python的Twisted框架构建非阻塞下载程序的实例教程
May 25 Python
python 判断网络连通的实现方法
Apr 22 Python
Pycharm在创建py文件时,自动添加文件头注释的实例
May 07 Python
Python 实现网页自动截图的示例讲解
May 17 Python
机器学习之KNN算法原理及Python实现方法详解
Jul 09 Python
在django中,关于session的通用设置方法
Aug 06 Python
Django如何实现上传图片功能
Aug 16 Python
对pytorch中的梯度更新方法详解
Aug 20 Python
Flask框架路由和视图用法实例分析
Nov 07 Python
对象析构函数__del__在Python中何时使用
Mar 22 Python
总结Python连接CS2000的详细步骤
python图片灰度化处理的几种方法
详解Python中的进程和线程
详解Go语言运用广度优先搜索走迷宫
常用的Python代码调试工具总结
Django+Celery实现定时任务的示例
Python django中如何使用restful框架
You might like
PHP关联链接常用代码
2012/11/05 PHP
PHP实现十进制、二进制、八进制和十六进制转换相关函数用法分析
2017/04/25 PHP
Laravel5.1 框架数据库操作DB运行原生SQL的方法分析
2020/01/07 PHP
Alliance vs AM BO3 第一场2.13
2021/03/10 DOTA
使用Modello编写JavaScript类
2006/12/22 Javascript
一份老外写的XMLHttpRequest代码多浏览器支持兼容性
2007/01/11 Javascript
How to Auto Include a Javascript File
2007/02/02 Javascript
索趣科技的答案
2007/02/07 Javascript
JQuery 学习笔记 选择器之六
2009/07/23 Javascript
js 巧妙去除数组中的重复项
2010/01/25 Javascript
Javascript 面向对象 继承
2010/05/13 Javascript
js常用代码段收集
2011/10/28 Javascript
javascript中关于&amp;&amp; 和 || 表达式的小技巧分享
2015/04/10 Javascript
js实现透明度渐变效果的方法
2015/04/10 Javascript
javascript深拷贝和浅拷贝详解
2017/02/14 Javascript
代码详解javascript模块加载器
2018/03/04 Javascript
5分钟学会Vue动画效果(小结)
2018/07/21 Javascript
react native 仿微信聊天室实例代码
2019/09/17 Javascript
微信小程序 textarea 层级过高问题简单解决方案
2019/10/14 Javascript
nest.js 使用express需要提供多个静态目录的操作方法
2019/10/24 Javascript
vue中使用带隐藏文本信息的图片、图片水印的方法
2020/04/24 Javascript
Python中使用select模块实现非阻塞的IO
2015/02/03 Python
NetworkX之Prim算法(实例讲解)
2017/12/22 Python
python实现给微信指定好友定时发送消息
2019/04/29 Python
PyQt5基本控件使用详解:单选按钮、复选框、下拉框
2019/08/05 Python
利用pandas向一个csv文件追加写入数据的实现示例
2020/04/23 Python
微软开源最强Python自动化神器Playwright(不用写一行代码)
2021/01/05 Python
找到您丢失的钥匙、钱包和手机:Tile
2017/05/19 全球购物
主管会计岗位职责
2014/03/13 职场文书
廉洁家庭事迹材料
2014/05/15 职场文书
土建施工员岗位职责
2014/07/16 职场文书
公安交警个人对照检查材料思想汇报
2014/10/01 职场文书
2014年党委工作总结
2014/11/22 职场文书
农村老人去世追悼词
2015/06/23 职场文书
学雷锋活动简报
2015/07/20 职场文书
Python实现聚类K-means算法详解
2022/07/15 Python