Python文件操作——基础操作

文件操作


冯诺依曼体系架构

  • CPU 由运算器和控制器组成
    • 运算器,完成各种算数运算、逻辑运算、数据传输等数据加工处理
    • 控制器,控制计算机各部件协调运行
    • 存储器,用于记忆程序和数据,例如内存
    • 输入设备,将数据或者程序输入到计算机中,例如键盘,鼠标
    • 输出设备,将数据或程序的处理结果展现给用户,例如显示器、打印机等
      一般说IO操作,指的是文件IO,如果指的是网络IO,都会直接说网络IO

文件IO常用操作

columncolumn
open打开
read读取
write写入
close关闭
readline行读取
readlines多行读取
seek文件指针操作
tell指针位置

打开操作

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

打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常
基本使用:
创建一个文件test,然后打开它,用完 关闭

f = open('test') #file对象
# windows <_io.TextIOWrapper name='test'mode='r' encoding='cp936'>
# linux <_io.TextIOWrapper name='test' mode='r' encoding='UTF-8'>
print(f.read()) #读取文件
f.close() #关闭文件

文件操作中,最常用的操作就是读和写。
文件访问的模式有两种:文本模式二进制模式。不同模式下,操作函数不尽相同,表现的结果也不一样。
注:
windows 中使用 codepage代码页,可以认为每一个代码页就是一张编码表。cp936 等同于 GBK。


open的参数
file

打开或者要创建的文件名。如果不指定路径,默认是当前路径

mode模式
描述字符意义
r缺省的,表示只读打开
w只写打开
x创建并写入一个新文件
a写入打开,如果文件存在,则追加
b二进制模式
t缺省的,文本模式
+读写打开一个文件。给原来只读、只写方式打开提供缺失大读或者写能力

在上面的例子中,可以看到默认是文本打开模式,且是只读的。

# r模式
f = open('test') #只读还是只写?
f.read()
f.write('abc')
f.close()

f = open('test','r') #只读
f.write('abc')
f.close()

f = open('test','r') #只读,文件不存在

# w 模式
f = open('test','w') #只写打开
f.write('abc')
f.close()

>>>cat test #看看内容

f = open('test',mode = 'w')
f.close()

>>>cat text #看看内容

f = open('test1',mode = 'w')
f.write('123')
f.close()

>>>cat test1 #看看内容

open 默认是只读模式 rt 打开已经存在的文件。
r 模式

  • 只读打开文件,如果使用write方法,会抛异常
  • 如果文件不存在,抛出FileNotFoundError异常

w 模式

  • 表示只写方式打开,如果读取则抛出异常
  • 如果文件不存在,则直接创建文件
  • 如果文件存在,则清空文件内容

f = open('test2','x')
f.read()
f.write('abc')
f.close()

f = open('test2','x')

x模式

  • 文件不存在,创建文件,并只写方式打开
  • 文件存在,抛出FileExistsError异常

f = open('test2','a')
f.write('abcdef')
f.close()

>>>cat test2

f = open('test2','a')
f.write('\n hello')
f.close()

>>>cat test2
f = open('test3','a')
f.write('test3')
f.close()

a 模式

  • 文件存在,只写打开,追加内容

  • 文件不存在,则创建后,只写打开,追加内容

  • r 是只读,wxa都是只写

  • wxa模式都可以产生新文件

    • w 不管文件存在与否,都会生成全新内容的文件
    • a 不管文件时候存在,都能在打开的文件尾部追加
    • x 必须要求文件事先不存在,自己创建一个新文件

文本模式:
字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode就是 rt
二进制模式 b
字节流,将文件就按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用 bytes 类型

f = open('test3','rb')
s = f.read()
print(type(s))
print(s)
f.close()

f = open('test3','wb')
s = f.write('hello hero'.encode())
print(s)
f.close()

在windows下,执行下面的代码

f = open('test3','rw')
f = open('test3','r+')
f.write('hello hero')
print(f.read())
f.close()

f = open('test3','r+')
s = f.write('hero school')
print(f.read())
f.close

>>>cat test3

f = open('test3','w+')
f.read()
f.close()

>>>cat test3

f = open('test3','a+')
f.write('hero hero')
f.read()
f.close()

>>>cat test3

f = open('test3','a+')
f.write('justice')
f.close()

>>>cat test3

f = open('test3','x+')
f = open('test4','x+')
f.write('python')
f.read()
f.close()
>>>cat test4

+模式

  • 为r、w、a、x提供缺失的读或写功能,但是,获取文件对象依旧按照r、w、a、x自己的特征。
  • +模式不能单独使用,可以认为它是为前面的模式字符做增强功能的。

文件指针

上面的例子中,已经说明了有一个指针。
文件指针,指向当前字节位置
mode = r ,指针起始在0
mode = a ,指针起始在EOF
tell() 显示指针当前位置
seek(offest[,whence])
移动文件指针位置。offest偏移多少字节,whence从哪里开始。

文本模式下
whence 0 缺省值,表示从头开始,offest只能正整数
whence 1 表示从当前位置,offest只接收0
whence 2 表示从EOF开始,offest只接受0

# 文本模式
f = open('test4','r+')
f.tell()  #起始
f.read()
f.tell()  #EOF
f.seek(0)  #起始
f.read()
f.seek(2,0)
f.seel(2,1)  # offset必须为0
f.seek(2,2)  # offset必须为0
f.close()

#中文
f = open('test4','w+')
f.write('hello hero')
f.tell()
f.close()
f = open('test4','r+')
f.read(2)
f.seel(1)
f.tell()
f.read()
f.seel(2)  #f.seek(3)
f.close()

文本模式从开头向后偏移的方式。
whence为1表示从当前位置开始偏移,但是只支持偏移0,相当于原地不动,所以没什么用。
whence为2表示从EOF开始,只支持偏移0,相当于移动文件指针到EOF。
seek是按照字节偏移的。
read在文本模式是按照字符读取的。

二进制模式下
whence 0 缺省值,表示从头开始,offest只能正整数
whence 1 表示从当前位置,offest 可正可负
whence 2 表示从EOF开始,offest 可正可负

# 二进制模式
f = open('test4','rb+')
f.tell()  #起始
f.read()
f.tell()  #EOF
f.seek(0)  #起始
f.seel(2,1)  # 从当前指针开始,向后2
f.read()
f.seek(-2,1)  # 从当前指针开始,向前2
f.close()


f.seek(2,2)  #从EOF开始,向后2
f.seek(0)
f.seek(-2,2) #从EOF开始,向前2
f.read()

f.seek(-20,2)  # OSError
f.close()

二进制模式支持任意起点的偏移,从头、从尾、从中间位置开始。
向后seek可以超界,但是向前seek的时候,不能超界,否则抛异常。


buffering : 缓冲区

-1 表示使用缺省大小的 buffer。如果是二进制模式,使用 io.DEFAULT_BUFFER_SIZE 值,默认是4096 或者8192。如果是文本模式,如果是终端设备,是行缓存方式。如果不是,则使用二进制模式的策略。

  • 0,只在二进制模式使用,表示管 buffer
  • 1,只在文本模式使用,表示使用行缓冲。意思就是见到换行符就 flush
  • 大于1,用于指定 buffer 的大小

buffer 缓冲区
缓冲一个内存空间,一般来说是一个 FIFO 队列,到缓冲区满了或者达到阈值,数据才会 flush 到磁盘。
flush() 将缓存数据写入磁盘
close() 关闭前会调用 flush()

io.DEFAULT_BUFFER_SIZE 缺省缓冲区大小,字节
先来看看二进制模式

import io

f = open('test4','w+b')
print(io.DEFAULT_BUFFER_SIZE)
f.write('hello.hero'.encode())
f.seek(0)
f.write('www.heroschool.com'.encode())
f.flush()
f.close()

f = open('test','wb+',4) # 缓冲区大小
f.write(b'hero')
f.write(b'school')
f.close()

文本模式

# buffering=1 ,使用行缓冲
f = open('test4','w+',1)
f.write('my')
f.write('hero')
f.write('school'*4)
f.write('\n')
f.write('Hello\nPython')
f.close()

# buffering > 1 ,使用指定大小的缓冲区
f = open('test4','w+',15)
f.write('hero')
f.write('my')
f.write('duke\n')
f.write('\none punch')
f.write('a'*(io.DEFAULT_BUFFER_SIZE - 20)) # 设置为大于1没什么用
f.write('\nwww.heroschool.com')
f.close()

buffering = 0
这是一种特殊的二进制模式,不需要内存的 buffer,可以看做是一个 FIFO的文件。

f = open('test4','wb+',0)
f.write(b'my')
f.write(b'hero')
f.write(b'school')
f.write(b'\n')
f.write(b'Hello\nPython')
f.close()
bufferign说明
buffering = -1t 和 b ,都是 io.DEFAULT_BURRER_SIZE
buffering = 0b 关闭缓冲区 ;t 不支持
buffering = 1t 行缓冲,遇到换行符才 flush
buffering > 1b 模式表示缓冲大小。缓冲区的值可以超过 io.DEFAULT_BUFFE_SIZE,直到设定的值超出后才把缓冲区 flush;t 模式,是 io.DEFAULT_BUFFER_SIZE 字节,flush 完后把当前字符串也写入磁盘

似乎看起来很麻烦,一般来说,只需要记得:

  1. 文本模式,一般都用默认缓冲区大小
  2. 二进制模式,是一个个字节的操作,可以指定 buffer 的大小
  3. 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它
  4. 一般编程中,明确知道需要写磁盘了,都会手动调用一次 flush,而不是等到自动 flush 或者 close 的时候
encoding:编码,仅文本模式使用

None 表示使用缺省编码,依赖操作系统。window 、linux 下测试如下代码:

f = open('test1','w')
f.write('啊')
f.close()

windows 下缺省 GBK(0xBOA1),Linux 下缺省UTF-8(0xE5 95 8A)

其他参数

errors
什么样的编码错误将被捕获
None 和 Strict 表示有编码错误将抛出 ValueError 异常;ignore 表示忽略

newline
文本模式中,换行的转换,可以为 None、‘ ’ 空串、‘\r’、‘\n’、‘\r\n’
读时,None 表示 ‘\r’、‘\n‘、’\r\n’ 都被转换为 ‘\n’;‘ ’ 表示不会自动转换通用换行符,其他合法字符表示 ‘\n’ 会被替换为指定的字符

f = open('f:/test/test123','w')
# newline 缺省为 None,windows下会把\n替换为\r\n
f.write('Hello\rPython\nhero\r\nschool')
# 真正写入的是
# 'Hello\rPthon\r\nhero\r\r\nschool'
f.close()

newlines = [None,'','\n','\r\n']
for n1 in newlines:
    f = open('f:/test/test123','r+',newline=n1) # 缺省替换所有换行符
    print(f.readlines())
    f.close()

运行结果如下:

# 常见换行符都替换为 \n
['Hello\n', 'Python\n', 'hero\n', '\n', 'school']
# ‘’ 表示什么都不做
['Hello\r', 'Python\r\n', 'hero\r', '\r\n', 'school']
# \n 做为换行符
['Hello\rPython\r\n', 'hero\r\r\n', 'school']
# \r\n 作为换行符
['Hello\rPython\r\n', 'hero\r\r\n', 'school']

closed
关闭文件描述符,True 表示关闭它。False会在文件关闭后保持这个描述符。Fileobj.fileno() 查看

read


read(size = -1)
size 表示读取到的多少个字符或字节;负数或者 None 表示读取到 EOF

f = open('f:/test/tis.txt','r+')
f.write('Python')
f.write('\n')
f.write('英雄学院')
f.seek(0)
print(f.read(8))
f.close()
# 二进制
f = open('f:/test/tis.txt','rb+')
print(f.read(8))
print(f.read(1))
f.close()

运行结果如下:

Python
英
b'Python\r\n'
b'\xd3'

行读取


readine(size = -1)
一行行读取文件内容。size 设置一次能读取行内几个字符或字节。
readine(hint = -1)
读取所有行的列表。指定 hint 则返回指定的行数。

# 按行迭代
f = open('test') # 返回可迭代对象

for line in f:
    print(line.encode())
    
f.close()

write


wirte(s) ,把字符串 s 写入到文件中并返回字符的个数
writelines(lines) ,将字符串列表写入文件。

f = open('tis','w+')

lines = ['abc','123\n','xyz'] # 提供换行符
f.writelines(lines)

f.seek(0)
print(f.read())
f.close()

运行结果如下:

abc123
xyz

close


flush 并关闭文件对象
文件已经关闭,再次关闭没有任何效果。

其他


名称说明
seekable()是否可 seek
readable()是否可读
writable()是否可写
closd是否已经关闭

上下文管理


问题的引出
在 Linux 中,执行

# 下面必须这么写
lst = []
for _ in range(2000):
    lst.append(open('test'))
# OSError:[Error 24] Too many open files: 'test'
print(len(lst))

lsof 列出打开的文件。

$ ps aux | grep python
$ lsof -p 9255 | grep test | wc -l
$ ulimit -a

ps 命令返回进程,grep 出 python 进程 ID
lsof -p 进程号,列出该进程的所有文件描述符,grep出test文件描述符,wc统计
ulimit -a 查看所有限制。其中open files 就是打开文件数的限制,默认1024

for x in lst:
    x.close()

将文件一次关闭,然后就可以继续打开了。再看一次 lsof。
如何解决?

1、异常处理
当出现异常的时候,拦截异常。但是,因为很多代码都可能出现 OSError 异常,还不好判断异常就是因为资源限制产生的。

f = open('test')
try:
    f.write('abc123') # 文件只读模式打开,写入失败
finally:
    f.close() # 这样才会将文件关闭

使用 finally 可以保证打开的文件可以被关闭。

2、上下文管理
一种特殊的语法,交给解释器去释放文件对象


上下文管理

del f 
with open('test') as f:
    f.write('abc') # 文件只读,写入失败
    
# 测试 f 是否关闭
f.closed # f 的作用域

上下文管理

  1. 使用 with…as 关键字
  2. 上下文管理的语句块并不会开启新的作用域
  3. with 语句块执行完的时候,会自动关闭文件对象

另一种写法:

f1 = open('test')
with f1: 
    f1.write('xyz123') # 文件只读模式打开,写入失败

# 测试 f1 是否关闭
f1.closed # f1 的作用域

对于类似文件对象的 IO 对象,一般来说都需要在不使用的时候关闭、注销,以及释放资源。

IO 被打开的时候,会获得一个文件描述符。计算机资源是有限的,所以操作系统会做限制。就是为了保护计算机的资源不要为完全耗尽,计算机资源是共享的,不是独占的。

一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值