暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

面试官:python文件IO的基本操作你都不知道?

Python都知道 2022-09-02
245


前言

哈喽大家好,我是知道,在计算机的世界里,文件操作是很常见的,无论是在操作系统里直接操作文件还是使用编程语言操作文件。大多数情况下文件都需要进行文件的持久化,也叫落地,由于内存有掉电易失特性,所以一般是放在磁盘上,既然是操作文件就需要磁盘的IO操作,相比于放内存里,访问速度就会慢很多,所以遇到IO操作时需要考虑操作是否频繁,是否会影响性能等。接下来我们对python里的文件操作进行一个知识梳理。

1. 操作文件常用方法

python中操作文件有以下方法:

方法说明
open打开文件
read读取文件内容(文本读字符和二进制读字节)
write写入内容
close关闭文件
readline按行读取内容
readlines多行读取内容
seek操作指针
tell显示指针位置

其中最常用的方法有打开读取写入关闭。文件只有在打开之后才可以进行其他操作,比如读、写(读写过程是可以循环多次的);一系列文件操作之后别忘记关闭,这里留一个问题,为什么操作之后都需要进行关闭呢?后文中会给出解释。

2. 打开

在python中,通过open方法打开文件,返回文件流对象,可迭代,打开失败会抛出OSError异常。 open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True)

2.1 基本使用

创建文件夹test,并以此为项目根目录使用pycharm打开,在test目录下创建文件hello.txt并写入内容"This is hello.txt"

# file对象,<class '_io.TextIOWrapper'>
f = open("hello.txt")
print(f)
# windows下输出
>>> <_io.TextIOWrapper name='hello.txt' mode='r' encoding='cp936'>
# linux下输出
>>> <_io.TextIOWrapper name='hello.txt' mode='r' encoding='UTF-8'>

content = f.read()
print(type(content), content)
>>> <class 'str'> This is hello.txt

f.close()

2.2 open方法常用参数

2.2.1 file

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

2.2.2 mode

以什么模式打开文件,用来限定文件对象的操作及文本访问,比如只读,只写等。有以下模式:

描述字符说明
r只读打开,默认模式
w只写打开
x创建并写入一个新文件
a写入打开,如果文件存在,则尾部追加写
b二进制模式
t文本模式,默认模式
+给只读只写方式提供读写能力
  • 模式r, w, a, x
# r模式,默认模式
# f = open("hello.txt")
f = open("hello.txt""r")
content = f.read()
print(content)
>>> This is hello.txt
f.write("QQQ")
f.close()
>>> 会出现什么结果?


# w模式,打开已有文件并写入
f = open("hello.txt""w")
f.read()
f.close()
>>> 会出现什么结果?

f = open("hello.txt""w")
f.write("write success!")
f.close()
>>> 查看一下hello.txt文件会有什么结果?

# 创建新文件hello1.txt
f = open("hello1.txt""w")
f.write("This is hello1.txt")
f.close()
>>> 查看一下当前目录,是否多出一个新文件?文件内容是什么?


# x模式,打开已有文件
f = open("hello.txt""x")
>>> 会出现什么现象?

f = open("hello2.txt""x")
f.write("This is hello2.txt")
f.read() 
>>> 会出现什么现象?

f = open("hello2.txt""x")
f.write("This is hello2.txt")
f.close()
>>> 查看当前目录是否有新文件产生,如果有,看下内容


# a模式,打开已有文件
f = open("hello.txt""a")
print(f.read())
>>> 会有什么现象?

f = open("hello.txt""a")
f.write("append success")
f.close()
>>> 查看已有文件内容,发现了什么?

f = open("hello3.txt""a")
f.write("This is hello3.txt with mode 'a' opening")
f.close()
>>> 查看当前目录有什么变化,查看文件内容

小结: open方法默认只读模式r打开已存在文件;r模式下:只读模式打开文件,使用write方法会抛异常,io.UnsupportedOperation;如果文件路径错误,会抛异常FileNotFoundError。w模式下:只写模式打开文件,如果使用read方法会抛异常,io.UnsupportedOperation;打开已有文件,会清空文件内容;如果文件不存在,则会新建文件。x模式下:文件存在,会抛FileExistsError异常;文件不存在则创建新文件,只写模式,不可读取,读取则io.UnsupportedOperation异常。a模式下:只写打开,文件已存在,则追加写入;文件不存在,则新建文件并追加写入。

  • 文本模式t与二进制模式b

文本模式t,字符流,将文件按照指定字符编码,按照字符进行操作。open默认mode是rt, 二进制模式b,字节流,将文件按照字节打开,按照bytes类型进行操作,与字符编码无关。

# 二进制只读
f = open("hello.txt""rb")
content = f.read()
print(type(content))
print(content)
f.close()

# 二进制只写
f = open("hello.txt""wb")
f.write("This is mode 'wb' test")
>>> 会出现什么情况?

f = open("hello.txt""wb")
# encode()默认utf-8编码
res = f.write("This is mode 'wb' test".encode())
print(res) # 字节数
f.close()

  • +模式
f = open("hello.txt""rw")
>>> 会出现什么?

f = open("hello.txt""r+")
# 与文件原先的内容作比较
print("1-------", f.read())
>>> 显示什么内容?
f.write("mode 'r+' test")
print("2-------", f.read())
f.close()
>>> 会出现什么现象?

f = open("hello.txt""r+", encoding="utf-8")
f.write("mode 'r+' test".)
print(f.read())
>>> 显示什么?想一想为什么?
f.close()

f = open("hello.txt""w+")
print(f.read())
>>> 显示什么?为什么?
f.close()

f = open("hello.txt""a+")
f.write("a+ test")
print(f.read())
>>> 显示什么?为什么?
f.close()

f = open("hello4.txt""x+")
f.write("x+ test")
print(f.read())
>>> 显示什么?为什么?
f.close()

小结: +模式为r, w, a, x提供确实的读写功能,但获取文件对象时,仍按照原有的模式特征;+模式不能单独使用。既然+模式是为其他基础功能的一次增强,但是实际使用的时候,所增强的功能虽然没有报错,但好像也没有生效,这是为什么呢?此时不得不提的就是文件指针。 文件指针,指当前字节位置。有如下特点及操作:r模式下,指针起始位置为0;a模式下,指针起始位置为末尾EOF(End Of File);tell()显示当前指针位置;seek(offset[, whence]),移动文件指针位置。其中offset为偏移字节量,whence指从哪里开始。 文本模式下: whence 0 为默认值,表示从头开始,offset只能正整数;whence 1 ,表示从当前位置开始,offset只能为0;whence 2 ,表示从EOF开始,offset只能为0。

f = open("tell_test.txt""w+", encoding="utf-8")
f.write("这是tell的测试")
print("1----------", f.tell())
print("2----------", f.read())
print("3----------", f.tell())
>>> 会有输出吗?输出是什么?

f.seek(0)
print("4----------", f.tell())
print("5----------", f.read())
>>> 会有输出吗?输出是什么?
print("6----------", f.tell())

f.seek(1)
print("7----------", f.read(1))
>>> 与想象中的输出一致吗?
f.close()

小结: 文本模式支持从头开始向后偏移方式;whence为1表示,从当前位置开始偏移,但只支持偏移为0;whence为2表示从EOF开始偏移,只支持偏移0;seek是按照字节偏移的;read在文本模式下按照字符读取。 二进制模式下: whence 0 为默认值,表示从头开始,offset只能正整数;whence 1 ,表示从当前位置开始,offset可正可负;whence 2 ,表示从EOF开始,offset可正可负。

f = open("tell_test.txt""rb+")
print("1----------", f.tell())
print("2----------", f.read())
print("3----------", f.tell())

f.write(b"qwer")
f.seek(0)
f.seek(11)  # 从当前指针开始,向后偏移1
print("4----------", f.read())
f.seek(-11)  # 从当前指针开始,向前偏移1
print("5----------", f.read())

f.seek(12)
f.seek(0)
f.seek(-12)
print("6----------", f.read())

f.seek(0)
f.seek(1001)

f.seek(0)
f.seek(-1002)
f.close()

>>> 输出结果与预期是否一致?

小结: 二进制模式支持任意起点位置的偏移,头尾中间均可;正向seek可以超界,反向seek不可超界,会抛异常。 注: 在文本模式下最好不要使用seek中间某个位置,因为seek是按字节来的,有可能将中文拆出一部分从而报错UnicodeDecodeError,一个中文三个(utf-8)或者两个字节(GBK);文本及bytes模式,seek时左边界不能超,右边界超的时候会在中间补ascii码0;文件指针大多数情况下用在二进制模式。

2.2.3 buffering

在操作系统中,与磁盘打交道是很耗时的操作,因为写入磁盘相对来说太慢了,所以一般IO设备都会有缓冲区,在写入的时候,先写入缓冲区,在达到一定条件(比如缓冲区满了,或者达到设定的阈值,又或者强制flush写入等)的时候再写入磁盘,一批一批地写入相对于不断地与磁盘进行交互写入,效率高很多。缓冲区是由操作系统分配地,一般都在内存中,所以操作会很快。 注:以下测试用例最好在Linux系统或者交互式python界面测试,IDE可能会影响结果

import io
# 查看系统默认缓冲区大小,单位是字节
print(io.DEFAULT_BUFFER_SIZE)

f = open("buffer_test.txt", buffering=0)
=> 输出什么?说明什么?

# 二进制模式,一般情况下用的很少,因为要和磁盘交互,性能会有很大问题
f = open("buffer_test.txt""rb+", buffering=0)
f.write(b"1")
=> 查看文件内容,内容是否立即落地?查看之后再关闭文件
f.close()


f = open("buffer_test.txt""w+", buffering=1)
f.write("123")
=> 查看下文件内容
f.close()
=> 再次查看下文件内容


f = open("buffer_test.txt""w+", buffering=1)
f.write("abcdefg" * 200)
=> 再次查看下内容,如果还没有内容,继续写入,直到有内容写入为止
f.close()


f = open("buffer_test.txt""w+", buffering=1)
f.write("123")
f.write("\n")
=> 查看下文件内容,发现什么?
f.close()


f = open("buffer_test.txt""w+", buffering=1)
f.write("123")
f.write("q\nw")
=> 查看下文件内容,发现什么?
f.close()


f = open("buffer_test.txt""w+", buffering=1)
f.write("123")
f.flush()
=> 查看下文件内容,发现什么?
f.close()

* 将buffering=1的模式换成wb+试试看
----------------------------------------------------

# 设置缓冲区8个字节
f = open("buffer_test.txt""wb+", buffering=8)
f.write(b"12345678")
=> 查看文件里是否有内容
f.write(b"9")
=> 再次查看文件内容

* 将buffering>1的模式换成文本模式试试看

小结: python在写文件时,当缓冲区满达到临界值时,再次执行写入操作,会将内容落地到磁盘,同时提供了强制写入磁盘方法flush,在对文件进行关闭时也会调用flush并关闭文件对象。

buffering值说明
-1默认值,适用于t和b模式,默认系统大小io.DEFAULT_BUFFER_SIZE。
0支持b模式,关闭缓冲区,写一次与磁盘交互一次;
不支持t模式。
1t模式下,行缓冲,遇到换行符之后才将内容落地到磁盘;
b模式下不受换行符影响。
>1b模式表示自定义缓冲区大小,直到超出设定的值之后才落地到磁盘,可以超过默认大小;
t模式下设置大小无效,只有超出默认大小或者flush后才会将内容写入。

注: 文本模式下,一般使用默认缓冲区大小;二进制模式,如果需要调缓冲区大小,一般是512的倍数;一般情况下,默认缓冲区大小即可;在编程中,明确需要写磁盘时,都会手动调用一次flush。

2.2.4 encoding

在不指定编码的情况下,open函数会采用当前系统的默认编码。None表示使用默认编码,windows默认GBK编码,linux默认UTF-8。需要注意的是写文件和读文件最好采用一样的编码,避免出现乱码,一般推荐utf-8

2.2.5 其他不常用参数

  • errors:获编码错误,None或者strict表示有编码错误就抛出ValueError异常,ignore表示不抛异常,一般默认即可;
  • newline:文本模式中,转换换行符;
f = open("newline_test.txt""w")
f.write("a\nb\rc\r\nd")
f.close()

# 只支持这些,默认None
newlines = (None"""\n""\r""\r\n")
for nl in newlines:
    f = open("newline_test.txt", newline=nl)
    # 将每行内容以列表形式返回
    print(f.readlines())
    f.close()
=> 查看下区别

 小结:  默认None情况下将所有常见换行符替换为\n;newline=""时识别换行符,什么都不做; \n、\r、 \r\n会以符号本身为界限进行切割并保留符号进行输出。

  • closefd:关闭文件描述符,表示是否在文件关闭后关闭文件描述符,True表示关闭,False表示不关闭。文件对象.fileno()可以查看。

3. 读操作

  • read(n=-1),读取文件内容,参数n表示读取多少个字节或字符,负数或None表示读取到EOF;
  • readline(limit=-1),一行行读取文件内容,limit表示一次能读取行内几个字节或字符;
  • readlines(hint=-1),返回读取所有行的列表,hint指定行数。

一般在开发过程中,对文件的每行做处理时,很少用到这两个函数,一般直接遍历文件对象。

f = open("test.txt""w+")
for i in f:
    # 处理i逻辑
    print(i)
    
f.close()

4. 写操作

  • write(s),将字符串s写入文件并返回字符的个数;
  • writelines(lines),将字符串列表写入文件,列表内元素只能为字符串;如果元素需要换行,需要加换行符。
s = ["a\n""b""c"]
f = open("write_test.txt""w+")
f.writelines(s)
f.close()

s = ["a""b""c"]
f = open("write_test.txt""w+")
f.writelines(s)
f.close()

s = ["a""b""c"""]
f = open("write_test.txt""w+")
# 不如这样操作
f.write("\n".join(s))
f.close()

5. 关闭

close(),调用flush并关闭文件对象,可多次关闭,如果已经关闭,再次关闭则没有什么效果及现象。

6. 其他操作

函数名说明
closed文件对象是否已关闭,返回True或False
readable()文件对象是否可读,返回True或False
writable()文件对象是否可写,返回True或False
seekable()文件对象是否可移动指针,返回True或False

7. 总结

通过本篇文章,我们对基本的文件IO操作知识进行了梳理,包括文件的打开,读写等方法;文件打开的七种模式及各模式有什么特点;为什么+模式明明是补充功能却好像也没生效似的;为什么一个文件打开之后要及时关闭;为什么要设置缓冲区,缓冲区有什么作用等。希望上述例子大家能手动都敲一遍,然后参照文章总结为自己的知识,这样才能更好地吸收。同时,也欢迎大家能够随时指正文章中总结不恰当的地方,共同进步。

我是知道, 感谢各位人才的:点赞、收藏和评论,我们下期更精彩!


文章转载自Python都知道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论