给Python初学者的文件读写指南(含基础与进阶,建议收藏)
toyiye 2024-09-09 09:00 3 浏览 0 评论
对于初学者来说,一份详尽又清晰明白的指南很重要。今天,猫猫跟大家一起,好好学习Python文件读写的内容,这部分内容特别常用,掌握后对工作和实战都大有益处。学习是循序渐进的过程,欲速则不达。文章较长,建议大家收藏,以备复习查阅哦。
1、如何将列表数据写入文件?
2、如何从文件中读取内容?
3、多样需求的读写任务
4、从with语句到上下文管理器
如何将列表数据写入文件?
首先,我们来看看下面这段代码,并思考:这段代码有没有问题,如果有问题的话,要怎么改?
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()方法写到文件,具体操作方法以及注意事项,请看喵喵之前发的《假期玩得开心也不忘充电,学习Python操作JSON,网络数据交换不用愁》
总结一下,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(),会读取下一行。
喵喵,是否感觉跟《超强汇总:学习Python列表,只需这篇文章就够了》学习过的生成器很像,需要不停调用next()获取下一行。
这么看来,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 position 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): self.filename=filename self.mode=mode def __enter__(self): self.f=open(self.filename,self.mode) self.f.write("enter now\n") return self.f #作为as说明符指定的变量的值 def __exit__(self,type,value,tb): self.f.write("exit now") self.f.close() return False #异常会被传递出上下文 with OpenFile('test.txt','w') 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猫】,后台回复“爱学习”,免费获得20本精选电子书。
相关推荐
- 为何越来越多的编程语言使用JSON(为什么编程)
-
JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...
- 何时在数据库中使用 JSON(数据库用json格式存储)
-
在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...
- MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)
-
前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...
- JSON对象花样进阶(json格式对象)
-
一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...
- 深入理解 JSON 和 Form-data(json和formdata提交区别)
-
在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...
- JSON 语法(json 语法 priority)
-
JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...
- JSON语法详解(json的语法规则)
-
JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...
- MySQL JSON数据类型操作(mysql的json)
-
概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...
- JSON的数据模式(json数据格式示例)
-
像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...
- 前端学习——JSON格式详解(后端json格式)
-
JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...
- 什么是 JSON:详解 JSON 及其优势(什么叫json)
-
现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...
- PostgreSQL JSON 类型:处理结构化数据
-
PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...
- JavaScript:JSON、三种包装类(javascript 包)
-
JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...
- Python数据分析 只要1分钟 教你玩转JSON 全程干货
-
Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...
- 比较一下JSON与XML两种数据格式?(json和xml哪个好)
-
JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- r语言矩阵 (127)
- browsererror (114)
- exportexcel (119)
- cv2.bitwise_not (137)
- dump命令 (128)
- es6concat (126)
- heapify (127)
- java.security.egd (130)
- javax.annotation (117)
- jsstringsplit (117)
- js数字 (115)
- maven编译 (132)
- mysqlleft (128)
- nodejsbuffer (149)
- org.apache.commons.httpclient (126)
- org.jsoup (141)
- org.springframework.web (128)
- robotframework-ride (115)
- setnocounton (141)
- socket.gethostbyname (122)
- sqlmid (121)
- time.strptime (133)
- vscode格式化 (125)
- win32con (129)
- window.localstorage (126)