Python的文件操作

什么是IO

在计算机中 I/O 是指 Input/Output,即 Stream (流)的输入和输出,输入和输出是相对于内存来说的。程序运行时数据都驻留在在内存当中,由 CPU 这个超快的计算机核心来执行,涉及到数据交换的地方,通常是磁盘、网络操作就需要 IO 接口。

在 IO 编程中可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream(输入流)是指数据从外(磁盘、网络)流进内存,Output Stream 是数据从内存流出到外面(磁盘、网络)。

由于 CPU 和内存的速度远远高于外设的速度,所以在 IO 编程中,就存在速度严重不匹配的问题。举个例子来说,比如要把 100M 的数据写入磁盘,CPU 输出 100M 的数据只需要 0.01 秒,可是磁盘要接收这 100M 数据可能需要 10 秒,怎么办呢?有两种办法:

第一种是 CPU 处于等待状态,也就是程序暂停执行后续代码,等到 100M 的数据在 10 秒后写入磁盘,再接着往下执行,这种模式称为同步IO;

第二种是 CPU 不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是后续代码可以立刻接着执行,这种模式称为异步IO;

同步和异步的区别就在于是否等待 IO 执行的结果,举个去肯德基吃汉堡的例子:

同步IO:你说“来个汉堡”,服务员告诉你,对不起,汉堡要现做,需要等5分钟,于是你站在收银台前面等了5分钟,拿到汉堡再去逛商场,这是同步IO。

异步IO:你说“来个汉堡”,服务员告诉你,汉堡需要等5分钟,你可以先去逛商场,等做好了,我们再通知你,这样你可以立刻去干别的事情(逛商场)

很明显,使用异步IO来编写程序性能会远远高于同步IO,但是异步IO的缺点是编程模型复杂。想想看,你得知道什么时候通知你“汉堡做好了”,而通知你的方法也各不相同。如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步IO的复杂度远远高于同步IO。

操作IO的能力都是由操作系统提供的,每一种编程语言都会把操作系统提供的低级C接口封装起来方便使用,Python也不例外。

文件读写

读写文件是最常见的IO操作,Python 内置了读写文件的函数,用法和C是兼容的。

在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。

在 Python 中对文件操作的步骤:

打开文件,得到文件对象(文件描述符)赋值给一个变量。

通过文件描述符对文件进行操作。

关闭文件。

文件打开模式1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32'r' open for reading (default)

# 以只读模式打开文件(默认模式),并将文件指针指向文件头;如果文件不存在会报错

'w' open for writing, truncating the file first

# 以只写模式打开文件,并将文件指针指向文件头;如果文件存在则将其内容清空,如果文件不存在则创建

'x' create a new file and open it for writing

# 以只写模式新建文件,并将文件指针指向文件头;如果文件存在则抛出 FileExistsError

'a' open for writing, appending to the end of the file if it exists

# 以追加可写模式打开文件,并将文件指针指向文件尾部;如果文件不存在则创建

'b' binary mode

# 二进制模式,需要与上面几种模式搭配使用,如ab,wb, ab, ab+

't' text mode (default)

# 普通文本模式(默认模式)

'+' open a disk file for updating (reading and writing)

# 在原有操作基础上补充其他操作功能

'r+'

# 在 r 的基础上增加了可写功能

# 覆盖当前文件指针所在位置的字符,如原来文件内容是"Hello,World",打开文件后写入"hi"则文件内容会变成"hillo, World"

'w+'

# 在w的基础上增加了可读功能

# 'w+'与 r+ 的不同是,w+在打开文件时就会先将文件内容清空,和 w 基本相同

'a+'

# 在 a 的基础上增加了可读功能

# a+ 与 r+ 的不同是, a+ 不管指针在哪只能写到文件末尾(读指针和写指针不相同)

读取文件

例如现有文件 ‘poem’ 内容如下:

1

2

3

4

5关关雎鸠,在河之洲。窈窕淑女,君子好逑。

参差荇菜,左右流之。窈窕淑女,寤寐求之。

求之不得,寤寐思服。悠哉悠哉,辗转反侧。

参差荇菜,左右采之。窈窕淑女,琴瑟友之。

参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。

使用内置 open() 函数,传入文件名和标示符,指定文件编码并且以 read 模式打开文件:

1

2f = open('poem', 'rt', encoding='utf-8')

# 如果文件不存在,open() 函数会抛出一个 IOError 的异常,并且给出错误码和详细的信息告诉你文件不存在

调用 read() 方法一次读取文件全部内容到内存,用一个 str 对象表示,并使用 print() 将内容输出到屏幕

1print(f.read())

文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:

1f.close()

注意: 打开文件时 open() 函数是通过操作系统打开的文件,如果不指定文件编码方式则默认使用操作系统自身文件编码方式打开,并且在内存中是 Unicode 形式。在 windows 系统中,’poem’ 文件是 utf-8 保存的,而 Windows 的默认编码是 gbk 编码,所以直接打开会乱码,需要 f=open('poem',encoding='utf8'),’poem’ 文件如果是 gbk 保存的则直接打开即可。

由于文件读写时都有可能产生 IOError ,一旦出错,后面的 f.close() 就不会调用。所以为了保证无论是否出错都能正确地关闭文件,我们可以使用 try ... finally 来实现:

1

2

3

4

5

6try: # 执行代码,可能抛出异常

f = open('poem', 'r')

print(f.read())

finally: # 不管有无异常都执行

if f:

f.close()

但是每次都这么写实在太繁琐,所以 Python 引入了 with 语句来自动帮我们调用 close() 方法:

1

2with open('poem', 'r') as f:

print(f.read())

这和前面的 try ... finally 是一样的,但是代码更加简洁并且不必调用 f.close() 方法 ,而且无论在这段代码的任何地方,如果发生异常,此时文件仍会被关闭。

读取文件常用方法方法

描述

read()

一次读取文件所有内容,返回一个 str

read(size)

每次最多读取指定长度的内容,返回一个str;Python3 中 size 指定的是字符(非字节)长度

readlines()

一次读取文件所有内容,按行返回一个list

readline()

每次只读取一行内容

seek(n)

将文件指针移动到指定字节的位置

tell()

获取当前文件指针所在字节位置

我们已经知道,对文件的读取操作需要将文件中的数据加载到内存 ,调用 read() 会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以为了保险起见,可以反复调用 read(size) 方法,每次最多读取 size 个字符的内容。另外调用 readline() 可以每次读取一行内容,调用 readlines() 一次读取所有内容并按行返回 list。因此要根据需要决定怎么调用。

如果文件很小,read() 一次性读取最方便;如果不能确定文件大小,反复调用 read(size) 比较保险;如果是配置文件,调用 readlines() 最方便:

1

2

3

4with open('/etc/resolv.conf', 'rt', encoding='utf-8') as fr:

for line in fr.readlines(): # 一次读取所有内容并返回列表(list)

if line.startswith('nameserver'):

print(line.strip())

如果文件比较大,则可以使用迭代器的方法,它在你每次你需要下一个值的时候给你返回,没调用的时候就处于休眠状态等待下一次调用

1

2

3

4with open('/etc/resolv.conf', 'rt', encoding='utf-8') as fr:

for line in fr: # for 内部将文件对象 fr 做成一个迭代器,用一行取一行

if line.startswith('nameserver'):

print(line.strip())

需要注意的是,在 Python3 中使用 read() 时传入的参数是字符个数而不是字节,而 tell() 返回的是字节的位置,在 utf-8 中一个中文字符占三个字节:

1

2

3

4with open('poem','r',encoding='utf-8') as f:

print(f.tell())

print(f.read(5))

print(f.tell())

返回结果:

1

2

30

关关雎鸠,

15

写入文件

写文件和读文件都需要调用 open() ,当以 'w' 模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。以 'a' 模式写入文件时,会以追加(append)模式追加到文件末尾

1

2

3f = open('test.txt', 'w')

f.write('Hello world')

f.close()

可以反复调用 write() 来写入文件,但是务必要调用 f.close() 来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用 close() 方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用 close() 的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以还是用 with 语句来得保险:

1

2with open('test.txt','w') as f:

f.write('Hello World')

其他方法

使用 flush() 可以刷新缓冲区数据,立刻将缓冲区的数据写入硬盘中的文件:

1

2

3

4

5

6

7import sys,time

for i in range(30):

sys.stdout.write('*') # stdout 标准输出,也是一个文件

sys.stdout.flush()

# 实时将内存中的数据写入标准输出文件

# 如果不写此行则数据会保留着内存缓冲区,直到sleep完成并一次性显示所有符号

time.sleep(0.1)

内置的 print() 也有 flush 参数

1

2

3

4import sys,time

for i in range(30):

print('*',end='',flush=True)

time.sleep(0.1)

使用 truncate() 可以裁切数据,如果不加任何参数,则裁切(删除)掉开头到结尾的内容。如果指定一个数字 N,则将第 N 个字符后的内容全部截掉,保留前 N 个字符:

1

2

3with open('poem2','a',encoding='utf-8') as f:

# 文件打开模式应该是 append 而不是 write ,因为 write 会先清除所有内容

f.truncate(5)

isatty() 可以判断文件是否是终端文件

next() 将会返回文件下一行,这个方法也是 file 对象实例可以被当做迭代器使用的原因。

fileno() 返回一个整型的文件描述符,可以用于一些底层 IO 操作上(如 os 模块的 read 方法)。

操作练习

读取文件 ‘poem’ ,显示到屏幕同时为第四行添加注释 # 琴瑟友之:弹琴鼓瑟来亲近她 ,如果涉及到字符串拼接尽量使用 join() 方法代替 + 。

菜鸟版:一次性读入所有内容到内存然后循环打印,使用变量定位行号。

1

2

3

4

5

6

7

8f = open('poem', mode='r', encoding='utf8')

line_number = 0

for i in f.readlines():

line_number += 1

if line_number == 4:

i = ''.join([i.strip(), '# 琴瑟友之:弹琴鼓瑟来亲近她'])

print(i.strip())

f.close()

菜鸟加强版:读入所有内容赋值给变量后立即关闭文件,对变量的元素循环和修改,使用索引定位

1

2

3

4

5

6

7f = open('poem',mode='r',encoding='utf8')

data = f.readlines()

f.close()

data[3] = ''.join([data[3].strip(),'# 琴瑟友之:弹琴鼓瑟来亲近她'])

for line in data:

print(line.strip())

中级版:一次性读入所有内容,使用enumerate() 和 try ... finally

1

2

3

4

5

6

7

8

9try:

f = open('poem', 'r', encoding='utf-8')

for line_number, line in enumerate(f.readlines(), 1):

if line_number == 4:

line = ''.join([line.strip(), '# 琴瑟友之:弹琴鼓瑟来亲近她'])

print(line.strip())

finally:

if f:

f.close()

最佳实践:前三种方法中 read() 、readlines() 在文件比较大时都会消耗大量内存空间,容易造成内存溢出,因此结合 with 语句并使用迭代器用一行取一行,变量定位行号更合适:

1

2

3

4

5

6

7line_number = 0

with open('poem','r',encoding='utf-8') as f:

for line in f:

line_number += 1

if line_number == 4:

line = ''.join([line.strip(), '# 琴瑟友之:弹琴鼓瑟来亲近她'])

print(line.strip())

在 Python 中由于内存的机制无法像 word 那样实时修改并保存文件,因此只能先读取源文件数据到内存空间中进行修改,然后写入到另一个文件,再把新文件替换掉源文件。

例如,要在文件 ‘poem’ 的第四行之后添加注释 # 琴瑟友之:弹琴鼓瑟来亲近她

1

2

3

4

5

6

7

8

9

10

11

12

13# -*- coding:utf-8 -*-

f_read = open('poem','r',encoding='utf-8')

f_write = open('poem.bak','w',encoding='utf-8')

line_number = 0

for line in f_read:

line_number += 1

if line_number == 4:

line = line.replace('\n','# 琴瑟友之:弹琴鼓瑟来亲近她\n')

f_write.write(line)

f_read.close()

f_write.close()

使用 with 语句,可以同时管理多个文件对象

1

2

3

4

5

6

7

8

9# -*- coding:utf-8 -*-

line_number = 0

with open('poem', 'r', encoding='utf-8') as fr, \

open('poem.bak', 'w', encoding='utf-8') as fw:

for line in fr:

line_number += 1

if line_number == 4:

line = line.replace('\n', '# 琴瑟友之:弹琴鼓瑟来亲近她\n')

fw.write(line)

python不是内部文件别太任性_Python对文件的操作相关推荐

  1. python不是内部文件别太任性_Python一笑很倾城

    初次接触Python是因为安装虚拟机(使用VM安装macOS)的时候需要这个资源支持,后来才了解原来Python是一门语言,而且很好玩.很好Python已经成功引起了me的注意. 1.什么是Pytho ...

  2. python在文件中写入字典_python初学--文件操作、字典

    文件读写 1.先打开文件 2.读取/写入内容 3.保存文件 文件的open模式有三种 1.w 写模式,它是不能读的 只要用w打开文件,文件中的东西都会被清空 w+, 写读模式,只要沾上w 就会清空原来 ...

  3. python读取大文件的某行_python 大文件以行为单位读取方式比对

    先前需要做一个使用python读取大文件(大于1G),并逐条存入内存进行处理的工作.做了很多的尝试,最终看到了如下的文章. 该文章实际上提供了集中读取大文件的方式,先经过测试总结如下 1. for l ...

  4. python文件读取与输出_python基本文件操作(文件输入和输出)

    文件输入输出中常用的文件对象方法: open: 返回一个新的文件对象,调用该对象的上的方法可对文件进行任何操作 readline: 读取一行数据包括结尾的换行符在内 write: 将数据写入文件中 c ...

  5. python文件是怎么写_python头文件怎么写

    本文主要以python2为例.首先介绍一下Python头文件的编程风格,然后再给大家详细介绍import部分的基本用法.这两个部分就是Python中头文件的组成模块. 编程风格#!/usr/bin/e ...

  6. python文件保存在哪里_Python 的文件保存路径

    原博文 2019-06-02 12:12 − 1.保存在当前代码同级的目录下: 2.保存在代码文件夹外面一层的新文件夹(data文件夹与代码文件夹同级)里: 3.保存在下一级的子文件夹里 ... 相关 ...

  7. python文件拷贝并校验_Python札记 -- 文件校验

    好久没有写随笔了,正好这两天可以休整一下,借此机会总结下最近使用python的小体会. 个人体会文件校验在下载文件时使用较多,在linux下最简单的实现方式就是: 1 $ md5sum filenam ...

  8. python读取文件需要的异常处理_Python基础:文件的简单读取和操作以及异常处理...

    Python提供了直接获取文件对象的方法,方便我们直接操作文件. 下面我们就使用Python对文件的读取.写入以及数据存储进行整理说明,以及Python对异常的处理. 获取文件对象 打开一个txt文件 ...

  9. python中文件的write语句_Python之文件读写

    程序中的数据都存储在内存中,当程序执行完毕后,内存中的数据将丢失,而文件可以用来进行数据的长期保存. 一.文件的打开与关闭 1. open 函数 Python通过解释器内置的open()函数打开一个文 ...

最新文章

  1. linux操作系统教学,Linux操作系统教学视频
  2. c++ 一个头文件引用另一个头文件的类
  3. Jquery Datatable的使用样例(ssm+bootstrsp框架下)服务器端分页
  4. 三星显示、LG已开始为苹果iPhone 13生产OLED屏幕
  5. volatile和synchronized关键字
  6. 你在家看电视,家里的电视也在看你?
  7. android 实体 快捷键,as快捷键
  8. C盘AppData文件占用83.7G?
  9. 华东师范 2018年 研究生复试上机题解合集
  10. 中国IT产业未来在哪里
  11. CSDN博客图片服务器异常的艰辛排查与处理-上传文件时发生 HTTP 错误(错误代码:502)的解决办法
  12. 常用的前端还款计算器(包括按月等额本息、按四月等额本息、到期还本付息、到期还本按月付息四种还款方式)
  13. 熊猫之死,是腾讯对360的又一次胜利
  14. 哈啰电动车,未来可期
  15. opencv-python 改变图片尺寸
  16. 洛谷P1336 课题选择
  17. (AAAI-2019)用于行人重识别的水平金字塔匹配
  18. 如何通过python判断闰年?
  19. 大消息:虚拟现实已经可以让你互换性别了!
  20. 蓝桥杯 试题 基础练习 Sine之舞 c语言

热门文章

  1. Shell脚本读取mysql结果集各数据项的值
  2. spring boot+kafka+canal实现监听MySQL数据库
  3. http 升级https
  4. react-native结合react-navigation之TabNavigator
  5. Cypher制作数据-武汉地铁二号线
  6. Windows 10 64bit 安装dotnetfx 3.5出错的解决办法(备忘)
  7. 华为解锁密码忘了怎么办用计算机,不记得手机锁屏密码怎么办_华为手机密码忘了的解决方法-系统城...
  8. python:实现连接mysql数据库(附完整源码)
  9. vue收藏/取消收藏,点赞、取消点赞一个道理,切换图标
  10. 水晶报表各版本比较及相关释疑v2