文件相关操作及编码

文件操作

文件IO常用操作

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

    open(file, mode=‘r’, buffering=-1, encoding=None, errors=None, newline=None, closed=True, opener=None)

    打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常

    基本使用:

    创建一个文件test,然后打开它,用完及时关闭

    f = open('test')  # file对象
    # windows <_io.TextIOWrapper name='test' mode='r' encoding='cp936'
    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('test1', 'r')  # 只读,文件不存在
    
    # w模式
    f = open('test', 'w')  # 只写打开
    f.write('abc')
    f.close()
    
    >>> cat test  # 查看内容
    
    f = open('test', mode='w')
    f.close()
    
    >>> cat test  
    
    f = open('test', mode='w')
    f.write('123')
    f.close()
    
    >>> cat test1
    

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

    r模式

    ​ 只读打开文件,如果使用write方法,会抛异常

    ​ 如果文件不存在,抛出FIleNotFoundError异常

    w模式

    ​ 表示只写方式打开,如果读取则抛出异常

    ​ 如果文件不存在,则直接创建文件

    ​ 如果文件存在,则清空文件内容

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

    x模式

    ​ 文件不存在,创建文件,并只写方式打开

    ​ 文件存在,抛出FileExistError异常

    f = open('test2', 'a')
    f.read()
    
    f.write('abced')
    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必须要求文件事先不存在,自己要早一个新文件

  • 文本模式t

    字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认是mode就是rt

  • 二进制模式b

    字节流,将文件就按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型

    f = open('test3', 'rb')  # 二进制只读
    s = f.read()
    print(type(s))  # bytes
    print(s)
    f.close()  # 关闭文件
    
    f = open('test3', 'wb')  # io对象
    s = f.write('马哥教育'.encode())
    peint(s)
    f.close()
    
  • +模式

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

文件指针

文件指针,指向当前字节位置

mode=r,指针起始在0

mode=a,指针起始在EOF

tell()显示指针当前位置

seek(offset[,whence])

移动文件指针位置。offset偏移多少字节,whence从哪里开始。

文本模式下

whence 0 缺省值,表示从头开始,offset只能正整数

whence 1表示从当前位置,offset只接受0

whence 2 表示从EOF开始,offset只接受0

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

# 中文
f = open('test4', 'w+')
f.write('马哥教育')
f.tell()
f.close()
f = open('test4', 'r+')
f.read(2)
f.seek(1)
f.tell()
f.read()
f.seek(2)  # f.seek(3)
f.close()

文本模式支持从开头向后偏移的方式

whence为1表示从当前位置开始偏移,但是只支持偏移0,相当于原地不动,所以没什么用。

whence为2表示从EOF开始,只支持偏移0,相当于移动文件指针到EOF

seek是按照字节偏移的。

read在文本模式是按照字符读取的

二进制模式下

whence 0缺省值,表示从头开始,offset只能正整数

whence 1表示从当前位置,offset可正可负

whence 2表示从EOF开始,offset可正可负

# 二进制模式
f = open('test4', 'rb+')
f.tell()
f.read()
f.tell()
f.write(b'abc')
f.seek(0)
f.seek(2, 1)
f.read()
f.seek(-2, 1)

f.seek(2, 2)
f.seek(0)
f.seek(-2, 2)
f.read()

f.seek(-20, 2)
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)  # ipython中为8192
f.write('magedu.com'.encode())
# cat test4
f.seek(0)
# cat test4
f.write('www.magedu.com'.encode())
f.flush()
f.close()

# 缓冲区大小为0
f = open('test4', 'w+b', 0)
f.write(b'a')
# cat test4
f.write(b'a')
# cat test4
f.close()

# 缓冲区大小为1
f = open('test4', 'w+b', 1)
f.write(b'a')
# cat test4
f.write(b'a' * 200)
# cat test4
f.write(b'a' * 8000)
# cat test4
f.close()

# 设置缓冲区大小
f = open('test4', 'W+b', 4)
f.write(b'mag')
# cat test4
f.write(b'edu')
# cat test4
f.close()

二进制模式:

  • -1是缺省值,使用缺省缓冲区大小
  • 0关闭缓冲区,不需要内存的buffer,可以看作是一个FIFO的文件
  • 1不支持,直接使用缺省缓存区大小
  • 大于1,使用指定缓冲区大小

文本模式

# buffering=0, 行吗?

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

f = open('test4', 'w+', 1)
f.write('a' * 3000)
f.write('a' * 3000)
# cat test4
f.write('a' * 3000)
# cat test4
f.close()

# buffering>1,使用指定大小的缓冲区
f = open('test4', 'w+', 15)
f.write('mag')  # cat test4
f.write('edu')
f.write('Hello\n')
f.write('\nPython')
f.write('a' * (io.DEFAULT_BUFFER_SIZE - 20))  # 设置为大于1没什么用
f.write('\nwww.magedu.com/python')
f.close()
buffering说明
buffering=-1t和b,都是io.DEFAULT_BUFFER_SIZE
buffering=0b 关闭缓冲区
t 不支持
buffering=1t 行缓冲,遇到换行符才flush
buffering>1b模式表示缓冲大小,缓冲区的值可以超过io.DEFAULT_BUFFER_SIZE,直到设定的值超出后才把缓冲区flush
t模式,是io.DEFAULT_BUFFER_SIZE字节,flush完后把当前字符串也写入磁盘

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

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

encoding:编码,仅文本模式使用

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

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

windows下缺省GBK (0xB0A1),linux下缺省UTF-8(0xE5 95 8A)

其他参数

errors

什么样的编码错误将被捕获

None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略

newline

文本模式中,换行的转换。可以为None,’’, ‘\r’,’\n’, ‘\r\n’

读时

  • None表示’\r’,’\n’, ‘\r\n’都被替换为’\n’;
  • ''表示不会自动转换通用换行符,常见换行符都可以认为是换行;
  • 其他合法换行字符表示按照该字符分行

写时

  • None表示’\n’都会被替换为系统缺省行分隔符os.linesep;
  • ‘\n’或’‘表示’\n’不替换
  • 其他合法换行字符表示文本中的’\n’换行符会被替换为指定的换行字符
f = open('o:/test', 'w')
# newline缺省为None,windows下会把\n替换为\r\n
f.write('python\rwww.python.org\r\nwww.magedu.com\r\npython3')
# 真正写入的是
# 'python\rwww.python.org\r\nwww.magedu.com\r\r\npython3'
f.close()

newline = ['', None, '\n', '\r\n']
for nl in newlines:
	f = open('o:/test', newline=nl)  # 缺省替换所有换行符
	print(f.readlines())
	f.close()
# 运行结果如下
['python\r', 'www.python.org\r\n', 'www.magedu.com\r', '\r\n', 'python3']''表示什么都不做,常见换行符都用来分行
['python\n', 'www.python.org\n', 'www.magedu.com\n', '\n', 'python3']常见换行符都替换为\n,\n来分行
['python\rwww.python.org\r\n', 'www.magedu.com\r\r\n', 'python3']\n作为换行符
['python\rwww.python.org\r\n', 'www.magedu.com\r\r\n', 'python3']\r\n作为换行符

closed

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

read

read(size=-1)

size表示读取的多少个字符或者字节;负数或者None表示读取到EOF

f = open('o/test4', 'r+')
f.write('magedu')
f.write('\n')
d.write('马哥教育')
f.seek(0)
f.read(7)
f.close()
# 二进制
f = open('test4','rb+')
f.read(7)
f.read(1)
f.close()

行读取

readline(size=-1)

一行行读取文件内容。size设置一次能读取行内几个字符或字节

readlines()

读取所有行的列表

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

for line in f:
	print(line.encode())  # 带换行符,有必要清strip
	
f.close()

write

write(s),把字符串s写入到文件中并返回字符的个数

writelines(linse),将字符串列表写入文件。

 f = open('test', 'w+')
 
 lines = ['abc', '123\n', 'magedu']  # 需提供换行符
 f.writelines(lines)
 
 f.seek(0)
 print(f.read())
 f.close()

close

flush并关闭文件对象

文件已经关闭,再次关闭没有任何效果

其他

名称说明
seek()是否可seek
readable()是否可读
writeable()是否可写
closed是否已经关闭

上下文管理

问题的引出

在linux中,执行

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

print(len(lst))

lsof列出打开的文件,没有该命令就安装 # yum install 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')
print(f.closed)
try:
	f.write('abc')  # 文件只读,写入失败
finally:
	f.close()  # 这样才行

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

2、上下文管理

一种特殊的语法,交给解释器去释放文件对象

上下文管理

del f  # 删除前面定义的f,如果没有则不用删除
with open('test') as f:
	print(f, f.closed)
	f.write('abc')  # 文件只读,写入失败
	
# 测试f是否关闭
f.closed  # f的作用域

上下文管理

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

另一种写法

f1 = open('test')
with f1:
	f1.write('abc')  # 文件只读,写入失败
	
# 测试f是否关闭
f1.closed  # f1的作用域
  • 对于类似文件对象的IO对象,一般来说都需要在不使用的时候关闭,注销,以释放资源
  • IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所以操作系统都会做限制。就是为了保护计算机的资源不要别完全耗尽,计算机资源hi是很多程序共享的,不是独占的。
  • 一般情况下,除非特别明确的直到当前资源情况,否则不要盲目提高资源的限制值来解决问题。

StringIO和BytesIO

StringIO

  • io模块中的类
    • from io import StringIO
  • 内存中,开辟一个文本模式的buffer,可以像文件对象一样操作它
  • 当close方法被调用的时候,这个buffer会被释放

getvalue()获取全部内容。根文件指针没有关系

from io import StringIO

# 内存中构建
sio = StringIO()  # 像文件对象一样操作
print(sio.readable, sio.writeable(), sio.seekable())
sio.write('magedu\nPython')
sio.seek(0)
print(sio.readable())
print(sio.getvalue())  # 无视指针,输出全部内容
sio.close()

好处

一般来说,磁盘的操作比内存的操作慢得多,内存足够的情况下,一般的优化思路是少落地,减少磁盘IO的过程,可以大大提高程序的运行效率。

BytesIO

  • io模块中的类
    • from io import BytesIO
  • 内存中,开辟的一个二进制模式的buffer,可以像文件对象一样操作它
  • 当close方法被调用的时候,这个buffer会被释放
from io import BytesIO  # 内存中构建
bio = BytesIO()
print(bio.readable(), bio.writeable(), bio.seekable())
bio.write(b'magedu\nPython')
bio.seek(0)
print(bio.readline())
print(bio.getvalue())  # 无视指针,输出全部内容
bio.close()

file-like对象

  • 类文件对象,可以像文件对象一样操作
  • socket对象,输入输出对象(stdin,stdout)都是类文件对象
from sys import stdout, stderr
f = stdout
print(type(f))
print(f.seekable(), f.readable(), f.writeable())
f.write('magedu.com')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值