文件操作
一,文件的读写操作
1. I/O操作的概述
I/O在是指Input/Output,也就是Stream(流)的输入和输出。程序运行时,数据都是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方(通常是磁盘、网络操作)就需要IO接口。而操作系统屏蔽了底层硬件,向上提供通用接口。因此,操作I/O的能力是由操作系统的提供的,每一种编程语言都会把操作系统提供的低级C接口封装起来供开发者使用
2. 文件读写实现原理(文件描述符)
由于操作I/O的能力是由操作系统提供的,而操作系统不允许普通程序直接操作磁盘,所以读写文件时需要请求操作系统打开一个对象(通常被称为文件描述符–file descriptor, 简称fd),文件描述符在形式上是一个非负整数,实际上,它相当于一个指针,是一个用于指代被打开的文件索引值,所有执行i/o操作的系统调用都是通过文件描述符完成的,具体操作如下图:
文件操作时,在linux内核中通常会有个task_struct结构体来维护进程相关的表,叫进程控制块,这个块里面会有指针指向file_struct的结构体,称为文件描述表,文件描述符就是这个表的索引。
- 然后这个file_struct会指向一个file的结构体。
- file 有几个重要的结构体成员分别实现不同的功能。
file_operation: 这个指向的文件操作指针,file_operation里面包含了对文件 操作的内核函数指针,他指向内核操作函数,对文件进行操作。
dentry:目录项,它指向带有文件路径的dentry结构体指针 - dentry这个结构体会指向 inode,
- inode 保存着从磁盘inode读上来的信息,还有两个重要成员:
inode_opertions:它指向文件操作的内核函数,告诉内核文件能做那些操作
super_block: 保存着从磁盘分区的超级块读上来的信息,它还有个成员指向dentry,得到文件根目录被mount 到哪里的信息
简单总结:
-
每一个文件描述符会与一个打开文件相对应,同时,不同的文件描述符也会指向同一个文件。相同的文件可以被不同的进程打开也可以在同一个进程中被多次打开。系统为每一个进程维护了一个文件描述符表,该表的值都是从0开始的,所以在不同的进程中你会看到相同的文件描述符,这种情况下相同文件描述符有可能指向同一个文件,也有可能指向不同的文件
-
文件描述符默认1024个,所以最多只能同时打开1024个文件。
3 文件的打开与读取
3.1 文件的打开
单纯使用 open() 方式打开文件,如果在读写过程中出错会直接返回 IOError,从而没有关闭文件,造成资源占用。因此,无论读文件还是写文件,都需要再最后关闭文件流,为了避免麻烦,可以使用with方法,with语句会自动帮我们调用close方法.
with open('/path/to/file', 'r') as f:
print(f.read())
3.2 文件的打开模式
文件打开模式 | 描述 |
---|---|
r | 以只读模式打开文件,并将文件指针指向文件头;如果文件不存在会报错 |
w | 以只写模式打开文件,并将文件指针指向文件头;如果文件存在则将其内容清空,如果文件不存在则创建 |
a | 以只追加可写模式打开文件,并将文件指针指向文件尾部;如果文件不存在则创建 |
x | 创建一个新文件只写,如果文件存在则异常 |
+ | 补全读写功能;如r+ 是在r 的基础上增加了可写功能 |
b | 读写二进制文件(默认是t,表示文本),需要与上面几种模式搭配使用,如ab,wb, ab, ab+ |
注意:文件的操作一定要注意指针的位置,这点在下面代码中会体现
- 虽然r+, w+, a+ 都是读写功能,但是r+ 是覆盖写,w+ 是清空后再写, a+ 是追加写
- open()以a+模式开启了一个附加读写模式的文件,由于是a,所以指针在文件末尾。此时如果做read(),则Python发现指针位置就是EOF,读取到空字符串。
解决方法:读取之前将指针重置为文件头
3.3 文件的读取方式
-
read(): 使用read 方法一次性读取所有的数据使用,包括换行符。只能在文件不大的情况下使用。
-
readline(): 使用readline 方法每次只读取一行数据,包括换行符
-
readlines(): 使用readlines 方法一次性读完所有数据,包括换行符,并以列表的形式返回,列表中的每个元素就是原文件的一行。
4. 几个使用文件读取的代码
- 将程序参数和结果直接缓存,写入文件,下次调用直接打开文件调取缓存
import json
import time
def cacheRead(): #打开文件将文件中缓存读出
try:
with open("text.txt", "r") as f:
if not f.read():
d = {
}
else:
f.seek(0)
d = json.load(f)
except FileNotFoundError: #文件不存在会报错
d = {
}
return d
d =cacheRead()
def cache(fn):
def inner(*args):
if str(args) in d.keys(): #如果缓存中存在,直接使用
return d[str(args)]
else:
a = fn(*args) #如果不存在调用函数,再把这次的调用写入缓存
d[str(args)]=a
with open("tpt.txt", "w") as f: #将缓存写入文件
json.dump(d, f)
return a
return inner
@cache
def add(x,y,z=3):
time.sleep(z)
return x+y
print(add(5,5))
print(add(4.0,5))
print(add(4,6))
上面代码打开文件的方式是 r,所以如果是第一次打开文件,而文件不存在就会报错,所以要使用一个 try 保证程序不会中断。 我们也可以使用 a+ 方式打开,就可以省去外层的 try,打开文件函数如下,注意a 方式打开文件的指针位置。
def cacheRead()