微信公众号:码农充电站pro

个人主页:https://codeshellme.github.io

过去的代码都是未经测试的代码。

目录

无论是哪种编程语言,IO 操作都是非常重要的部分。IInput(输入),OOutput(输出)。

IO 操作一般分为以下两种:

  • 磁盘IO: 即在磁盘上读写文件。读文件是指将文件内容从磁盘读入内存,写文件是指将内存中的内容写到磁盘。
  • 网络IO: 即文件在网络上传输。网络传输一般会有两种角色,分别是服务端(如HTTP Server)和客户端(如浏览器)。

本节我们主要介绍磁盘IO,即文件读写

1,open 函数介绍

要想读写文件,首先要打开一个文件。

Python 中的内建函数open 用来打开一个文件,我们可以使用help(open),来查看open 函数的原型,如下:

open(file, mode='r',
buffering=-1, encoding=None,
errors=None, newline=None,
closefd=True, opener=None)

该函数成功调用时会返回一个流stream,用于读写文件等操作;发生错误时会抛出IOError 异常。

被打开的文件占用了系统资源,使用完后要记得close,否则会浪费系统资源。

不管以读模式打开文件,还是以写模式打开文件,成功打开一个文件后,这个可操作文件的的内部都有一个隐含的指针,一般这个指针会指向文件开头或者文件末尾的位置,表示从文件的哪个位置读写文件。

可以看到,该函数支持8 个参数,但最重要的是前两个参数:

  • file:是指要打开的文件的路径
  • mode:是指以什么模式打开文件,要用引号引住

mode 参数支持的模式(默认为读文本模式,即rt)如下:

  • r:以读模式打开文件(默认方式),指针在文件开头
  • w:以写模式打开文件,如果件已存在,则内容会被清空(指针在文件开头);如果文件不存在,则会创建新文件
  • x:创建一个新文件,并以写模式打开,指针在文件开头,如果文件已存在,则抛出FileExistsError异常
  • a:以写模式打开文件,如果文件已有内容,在写入内容时,会追加到文件末尾(指针在文件末尾)
  • b:以二进制模式打开文件,一般用于读写二进制文件,如图片,视频等
  • t:以文本模式打开文件(默认方式),一般用于读写文本文件
  • +:以读写模式打开文件,指针在文件开头

这些模式还可以组合使用,常见的组合如下:

  • rb:以二进制模式打开一个文件,用于只读
  • r+:打开一个文件,用于读写
  • rb+:以二进制模式打开一个文件,用于读写
  • wb:以二进制模式打开一个文件,用于
  • w+:打开一个文件,用于读写
  • wb+: 以二进制模式打开一个文件,用于读写
  • ab: 以二进制模式打开一个文件,用于追加
  • a+:打开一个文件用于读写,指针在文件末尾
  • ab+:以二进制模式打开一个文件,用于读写,指针在文件末尾

2,open 函数示例

如下代码,成功打开文件./1.txt

f = open('./1.txt')

通过type(f)查看open 函数的返回值的类型:

>>> type(file)
<class '_io.TextIOWrapper'>

可看到,其返回值类型为_io.TextIOWrapper

我们用dir(f) 来查看对象 f 支持的属性和方法:

>>> dir(file)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__',
'__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__lt__',
'__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable',
'_finalizing',
'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno',
'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read',
'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell',
'truncate', 'writable', 'write', 'writelines']

可以通过help(f.方法名) 来查看每个方法的帮助手册,也可以使用help(f) 来查看该对象的所有属性和方法,及其简介。

我们来看一下常用方法的作用:

  • mode:打开文件时的模式
  • name:被打开的文件名
  • close:关闭文件流,并刷新缓冲区中的内容,之后不能再操作文件
  • closed:文件流是否已关闭
  • flush:刷新写缓冲区,只写流非阻塞流不适用
  • read:读入文件内容
  • readable:是否可读
  • readline:读入一行内容
  • readlines:读入文件所有的行,直至文件末尾
  • seek:移动文件指针的位置
  • seekable:文件指针是否可被移动
  • tell:返回文件指针当前位置
  • truncate:截断文件内容
  • writable:是否可写
  • write:向文件中写入内容
  • writelines:向文件中写入多行

3,关闭系统资源

正确的调用close() 函数是关键的。

在成功打开一个文件后,对该文件进行操作(读写)时,有可能发生异常。

比如我们打开的文件只能用来,如果用来,则会发生异常:

>>> f = open('1.txt', 'w')  # 用只读模式打开文件
>>> f.readable() # 查看文件是否可读
False # 返回 False,表示不可读
>>> f.read() # 读文件,发生异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
io.UnsupportedOperation: not readable

如果,我们将这段代码写在文件中:

#! /usr/bin/env python3

f = open('1.txt', 'w')
f.read()
f.close()

python3 来执行,结果如下:

$ python3 Test.py
Traceback (most recent call last):
File "Test.py", line 4, in <module>
f.read()
io.UnsupportedOperation: not readable

可以看到,在执行到f.read() 这句代码的时候,程序异常退出,那么后边的f.close() 就没有执行到,这就导致程序执行不够完整,系统资源没有关闭。

这时,我们可以用try...finally来处理,如下:

#! /usr/bin/env python3

f = open('1.txt', 'w')

try:
f.read() except Exception as e:
print('read file err:%s' % e) finally:
f.close()
print('file closed')

上面代码的执行结果如下:

$ python3 Test.py
read file err:not readable
file closed

我们将f.close() 这句代码放在了finally 代码块中,这样,不管遇到什么情况,f.close() 这句话总会被执行,就不会导致系统资源泄漏的问题。

4,with 语句使用

为了确保系统资源能够关闭,Python 中提供了with 语句,能够让我们更加安全方面的使用open 函数,而不用关心资源关闭的问题。

with 语句也叫上下文管理器,有了with 语句,我们可以这样使用open 函数:

with open('./1.txt') as f:
print(f.read())

这样的代码,不管在with 语句块内出现怎样的异常,close 函数都会被调用,而我们也不需要自己调用。

使用with 语句,就不再需要使用try...finally 语句,也使得代码更加简洁。

需要特别注意的是,这里的f只能在with 语句块中使用,一旦离开with 语句块,f 就被关闭了。如果在with 语句块之外使用f 进行读写等操作,将出现异常。

如下代码中,f.closed 将返回True

with open('./1.txt') as f:
pass
f.closed # True

5,with 语句原理

为什么open 函数能够使用with 语句?

实际上open 函数能够使用with 语句的原因取决于open 的返回值的类型。我们知道,open 的返回值的类型为_io.TextIOWrapper,而这个类中有两个方法,__enter__ 方法和__exit__ 方法。

我们再来看下with 语句的格式:

with ... as ... :
pass

with 关键字的后边是一个表达式as 后边是一个变量名,表达式的计算结果会赋值给as 后边的变量。

Python 规定,只要一个类中有__enter____exit__ 方法,就可以使用with 语句。with 语句后边的表达式执行完毕后,就会执行__enter__ 方法,在退出with 语句块时,会执行__exit__ 方法。

我们自己编写一个测试类,使其能够使用with 语句:

#! /usr/bin/env python3

class TestWith:

    def __init__(self):
print('执行__init__') def __enter__(self):
print('执行__enter__') def __exit__(self, exc_type, exc_val, exc_tb):
print('执行__exit__') print('exc_type is %s' % exc_type)
print('exc_val is %s' % exc_val)
print('exc_tb is %s' % exc_tb)

再该类中有三个函数:

  • __init__:构造函数,创建类的对象时调用
  • __enter__:进入with 语句块时会调用
  • __exit__:离开with 语句块时会调用

其中__exit__ 方法有三个参数:

  • exc_typewith 语句块中的代码发生异常时的异常类型
  • exc_val:发生异常时的异常值
  • exc_tb:发生异常时的traceback 类的对象

我们这样使用这个类:

with TestWith() as t:
print('test with')

python3 来执行,结果如下:

$ python3 Test.py
执行__init__
执行__enter__
test with
执行__exit__
exc_type is None
exc_val is None
exc_tb is None

可以看到执行步骤是这样的:

  1. 生成该类的对象,执行__init__ 方法
  2. 进入with 语句块,执行__enter__ 方法
  3. 执行with 语句块中的代码
  4. 退出with 语句块,执行__exit__ 方法

因为with 语句块中没有发生异常,所以__exit__ 方法中的 exc_typeexc_valexc_tb 三个参数均为None

下面再示范一个with 语句块中出现异常的代码:

with TestWith() as t:
print('test with1...')
1 / 0 # 除数为 0,抛出异常
print('test with2...')

该代码的执行结果如下:

$ python3 Test.py
执行__init__
执行__enter__
test with1...
执行__exit__
exc_type is <class 'ZeroDivisionError'>
exc_val is division by zero
exc_tb is <traceback object at 0x7fe8b7c98888>
Traceback (most recent call last):
File "Test.py", line 27, in <module>
1 / 0
ZeroDivisionError: division by zero

通过上面的执行结果可以看到,在执行1 / 0 之前,我们不用多说。在执行到1 / 0 时,出现异常,然后会执行__exit__ 方法。

在执行结果中,我们能看到 exc_typeexc_valexc_tb 三个参数的值,最后抛出Traceback 异常。

with 语句中,抛出异常的语句1 / 0 之后的代码不会再执行。

(完。)


推荐阅读:

Python 简明教程 --- 19,Python 类与对象

Python 简明教程 --- 20,Python 类中的属性与方法

Python 简明教程 --- 21,Python 继承与多态

Python 简明教程 --- 22,Python 闭包与装饰器

Python 简明教程 --- 23,Python 异常处理


欢迎关注作者公众号,获取更多技术干货。

Python 简明教程 --- 24,Python 文件读写的更多相关文章

  1. Python 简明教程 --- 25,Python 目录操作

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 做技术一定要一颗恒心,这样才不会半途而废. 目录 上一节我们介绍了文件相关的操作,本节我们来介绍目录 ...

  2. Python 简明教程 --- 26,Python 多进程编程

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 学编程最有效的方法是动手敲代码. 目录 1,什么是多进程 我们所写的Python 代码就是一个程序, ...

  3. python简明教程

    Python简明教程 MachinePlay关注 0.7072018.09.26 01:49:43字数 2,805阅读 9,287 Python一小时快速入门 1.Python简介   pylogo. ...

  4. 【笔记】Python简明教程

    Python简明教程,此资源位于http://woodpecker.org.cn/abyteofpython_cn/chinese/ s=u'中文字符' #u表示unicode,使用u之后能正常显示中 ...

  5. Python 简明教程 --- 3,Python 基础概念

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 控制复杂性是计算机编程的本质. -- Brian Kernighan 了解了如何编写第一个Pytho ...

  6. Python 简明教程 --- 2,第一个Python 程序

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 如果你发现特殊情况太多,那你肯定是用错方法了. -- Carig Zerouni 当你在自己的电脑上 ...

  7. Python 简明教程 --- 18,Python 面向对象

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 代码能借用就借用. -- Tom Duff 目录 编程可分为面向过程编程和面向对象编程,它们是两种不 ...

  8. Python 简明教程 --- 17,Python 模块与包

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 正确的判断来源于经验,然而经验来源于错误的判断. -- Fred Brooks 目录 我们已经知道函 ...

  9. Python 简明教程 --- 8,Python 字符串函数

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 好代码本身就是最好的文档.当你需要添加一个注释时,你应该考虑如何修改代码才能不需要注释. -- St ...

随机推荐

  1. 多语言工作者の十日冲刺<3/10>

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 团队进行Alpha冲刺--第三天(05.02) 作业正文 ...

  2. linux网络编程-posix信号量与互斥锁(39)

    -posix信号量信号量 是打开一个有名的信号量 sem_init是打开一个无名的信号量,无名信号量的销毁用sem_destroy sem_wait和sem_post是对信号量进行pv操作,既可以使用 ...

  3. 使用spring-test时报错

    java.lang.NoClassDefFoundError: org/springframework/core/annotation/MergedAnnotations$SearchStrategy ...

  4. JavaScript图形实例:窗花图案

    1.窗花基本框线 设定曲线的坐标方程为: n=25; r=100; x=r/n*cos(5*θ)+r*cos(θ); y=r/n*sin(5*θ)+r*sin(θ);          (0≤θ≤2π ...

  5. 前端基础:”天龙八步“细说浏览器输入URL后发生了什么

    参考:https://www.xuecaijie.com/it/157.html#1Q64p5DeC8dKFF 本文摘要: 1.DNS域名解析: 2.建立TCP连接: 3.发送HTTP请求: 4.服务 ...

  6. dart快速入门教程 (3)

    3.运算符 运算符本质上就是代表某运算规则的符号,例如: + ,这个符号,代表着数学运算里面的加法,按照加法法则进行运算即可,同理,学习运算符就是掌握这些规则而已 3.1.算术运算符 算术运算符主要包 ...

  7. JQuery UI - draggable参数中文详细说明

    概述 在任何DOM元素启用拖动功能.通过单击鼠标并拖动对象在窗口内的任何地方移动. 官方示例地址:http://jqueryui.com/demos/draggable/ 所有的事件回调函数都有两个参 ...

  8. JavaScript基础对象创建模式之沙盘模式(026)

    沙盘模式可以弥补命名空间模式中的两项不足之处: 使用唯一全局对象作为程序的全局变量入口,使得无法在同一程序中使用两个不同版本的API,因此它们使用的是同一个唯一的全局对象名,如MYAPP: 较长的嵌套 ...

  9. 自定义PHPstorm快捷键

    这篇随笔介绍一下PHPstorm自定义快捷键的步骤: 1.点击主菜单 File 下的 setting : 2.在弹出框中点击 Keymap : 3.之后会出现如下图所示的界面(图中所有的菜单都折叠了) ...

  10. 阿里云Linux CentOS8.1 64位服务器安装LNMP(Linux+Nginx+MySQL+PHP)

    LNMP环境和软件版本: 名称 版本号 查询命令 Linux系统 CentOS Linux release 8.1.1911 (Core) cat /etc/redhat-release Nginx ...