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

编辑: admin 分类: python 发布时间: 2021-12-23 来源:互联网

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

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

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文件读写内容请搜索hwidc以前的文章或继续浏览下面的相关文章希望大家以后多多支持hwidc!

【本文转自:韩国站群服务器 欢迎转载】