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生成随机mac地址的方法
Mar 16 Python
Python使用dis模块把Python反编译为字节码的用法详解
Jun 14 Python
Python中如何获取类属性的列表
Dec 26 Python
python实现字典(dict)和字符串(string)的相互转换方法
Mar 01 Python
详解python selenium 爬取网易云音乐歌单名
Mar 28 Python
pandas计算最大连续间隔的方法
Jul 04 Python
python sorted方法和列表使用解析
Nov 18 Python
django 中使用DateTime常用的时间查询方式
Dec 03 Python
python图片剪裁代码(图片按四个点坐标剪裁)
Mar 10 Python
python“静态”变量、实例变量与本地变量的声明示例
Nov 13 Python
python程序的组织结构详解
Dec 06 Python
python中mongodb包操作数据库
Apr 19 Python
总结Python连接CS2000的详细步骤
python图片灰度化处理的几种方法
详解Python中的进程和线程
详解Go语言运用广度优先搜索走迷宫
常用的Python代码调试工具总结
Django+Celery实现定时任务的示例
Python django中如何使用restful框架
You might like
PHP4实际应用经验篇(7)
2006/10/09 PHP
php下使用无限生命期Session的方法
2007/03/16 PHP
PHP header函数分析详解
2011/08/06 PHP
destoon切换城市后实现logo旁边显示地区名称的方法
2014/08/21 PHP
thinkPHP框架实现多表查询的方法
2018/06/14 PHP
从零开始学习jQuery (十一) 实战表单验证与自动完成提示插件
2011/02/23 Javascript
Jquery动态更改一张位图的src与Attr的使用
2013/07/31 Javascript
jQuery 属性选择器element[herf*='value']使用示例
2013/10/20 Javascript
JS脚本defer的作用示例介绍
2014/01/02 Javascript
javascript遍历控件实例详细解析
2014/01/10 Javascript
JavaScript动态创建link标签到head里的方法
2014/12/22 Javascript
jQuery中:text选择器用法实例
2015/01/03 Javascript
浅谈JavaScript函数的四种存在形态
2016/06/08 Javascript
js省市区级联查询(插件版&amp;无插件版)
2017/03/21 Javascript
带你快速理解javascript中的事件模型
2017/08/14 Javascript
js限制input只能输入有效的数字(第一个不能是小数点)
2018/09/28 Javascript
关于JS解构的5种有趣用法
2019/09/05 Javascript
Electron实现应用打包、自动升级过程解析
2020/07/07 Javascript
python中定义结构体的方法
2013/03/04 Python
编写Python的web框架中的Model的教程
2015/04/29 Python
Python解析json时提示“string indices must be integers”问题解决方法
2019/07/31 Python
python集成开发环境配置(pycharm)
2020/02/14 Python
Css3圆角边框制作代码
2015/11/18 HTML / CSS
悬挂训练绳:TRX
2017/12/14 全球购物
NIHAOMARKET官方海外旗舰店:意大利你好华人超市
2018/01/27 全球购物
介绍一下except的用法和作用
2015/01/22 面试题
会计毕业自我鉴定
2014/02/05 职场文书
洗发水广告词
2014/03/13 职场文书
人事行政经理岗位职责
2014/06/18 职场文书
2015年暑期社会实践活动总结
2015/03/27 职场文书
4S店客服专员岗位职责
2015/04/07 职场文书
初中军训感言
2015/08/01 职场文书
七年级作文之秋游
2019/10/21 职场文书
Python使用Kubernetes API访问集群
2021/05/30 Python
Golang解析JSON对象
2022/04/30 Golang
Go中使用gjson来操作JSON数据的实现
2022/08/14 Golang