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脚本实现查找webshell的方法
Jul 31 Python
一个超级简单的python web程序
Sep 11 Python
Python装饰器的函数式编程详解
Feb 27 Python
总结python爬虫抓站的实用技巧
Aug 09 Python
新手如何快速入门Python(菜鸟必看篇)
Jun 10 Python
python 上下文管理器使用方法小结
Oct 10 Python
Python对列表去重的多种方法(四种方法)
Dec 05 Python
Python实现的井字棋(Tic Tac Toe)游戏示例
Jan 31 Python
python实现淘宝秒杀聚划算抢购自动提醒源码
Jun 23 Python
django+echart绘制曲线图的方法示例
Nov 26 Python
python粘包问题及socket套接字编程详解
Jun 29 Python
在django admin中配置搜索域是一个外键时的处理方法
May 20 Python
总结Python连接CS2000的详细步骤
python图片灰度化处理的几种方法
详解Python中的进程和线程
详解Go语言运用广度优先搜索走迷宫
常用的Python代码调试工具总结
Django+Celery实现定时任务的示例
Python django中如何使用restful框架
You might like
用php过滤危险html代码的函数
2008/07/22 PHP
PHP编程实现多维数组按照某个键值排序的方法小结【2种方法】
2017/04/27 PHP
jQuery对象和DOM对象的相互转化实现代码
2010/03/02 Javascript
根据出生日期自动取得星座的js代码
2010/07/20 Javascript
cnblogs中在闪存中屏蔽某人的实现代码
2010/11/14 Javascript
nodejs之请求路由概述
2014/07/05 NodeJs
node.js中的定时器nextTick()和setImmediate()区别分析
2014/11/26 Javascript
jquery实现textarea输入框限制字数的方法
2015/01/15 Javascript
ECMA5数组的新增方法有哪些及forEach()模仿实现
2015/11/03 Javascript
jQuery解决浏览器兼容性问题案例分析
2016/04/15 Javascript
JavaScript6 let 新语法优势介绍
2016/07/15 Javascript
angular学习之ngRoute路由机制
2017/04/12 Javascript
基于angular实现三级联动的生日插件
2017/05/12 Javascript
JavaScript事件委托原理与用法实例分析
2018/06/07 Javascript
详解如何webpack使用DllPlugin
2018/09/30 Javascript
jQuery实现当拉动滚动条到底部加载数据的方法分析
2019/01/24 jQuery
小程序自定义单页面、全局导航栏的实现代码
2019/03/15 Javascript
使用vue实现多规格选择实例(SKU)
2019/08/23 Javascript
es6中使用map简化复杂条件判断操作实例详解
2020/02/19 Javascript
微信小程序自定义弹出层效果
2020/05/26 Javascript
使用Vant完成DatetimePicker 日期的选择器操作
2020/11/12 Javascript
深入浅析python中的多进程、多线程、协程
2016/06/22 Python
python数据分析数据标准化及离散化详解
2018/02/26 Python
基于Python Numpy的数组array和矩阵matrix详解
2018/04/04 Python
python机器学习之KNN分类算法
2018/08/29 Python
Python常见的pandas用法demo示例
2019/03/16 Python
深度辨析Python的eval()与exec()的方法
2019/03/26 Python
详解python中index()、find()方法
2019/08/29 Python
Python实现RabbitMQ6种消息模型的示例代码
2020/03/30 Python
WiFi云数码相框:Nixplay
2018/07/05 全球购物
房屋继承公证书
2014/04/10 职场文书
2016春节慰问信范文
2015/03/25 职场文书
会计入职心得体会
2016/01/22 职场文书
全家福照片寄语怎么写?
2019/04/02 职场文书
从贫穷到富有,是知识技能和学习力的差别
2019/08/20 职场文书
Python Pytorch查询图像的特征从集合或数据库中查找图像
2022/04/09 Python