Python知识学习19——IO操作

IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。

打开文件

先给出一个示范程序

1
2
3
f = open("test.py", "w")
f.read()
f.close()

补充:close()操作关闭文件是非常必要的,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的

这里介绍打开文件常用的几种访问模式

访问模式 说明
r 以只读的方式打开文件,文件的指针将会放在文件的开头,这是默认的模式(文件必须存在)
w 打开一个文件只用于写入,如果该文件已经存在则将其覆盖;如果该文件不存在,创建新的文件
a 打开一个文件用于追加,如果该文件已存在,文件指针将放在该文件的结尾;如果不存在,创建新文件以供写入
rb 以二进制格式打开一个文件用于读写,文件的指针放在文件的开头,这是默认模式(文件必须存在)
wb 以二进制格式打开一个文件用于写入,如果文件已存在则将其覆盖;如果文件不存在,创建新文件以供写入
ab 以二进制格式打开一个文件用于追加,如果文件已存在,文件指针将放在文件的结尾;如果文件不存在,创建新文件以供写入
r+ 打开一个文件用于读写,文件指针将放在文件开头
w+ 打开一个文件用于读写,如果文件已存在,则将其覆盖;如果不存在,创建新文件以供读写
a+ 打开一个文件用于读写,如果该文件已存在,文件指针将放在文件结尾;文件打开时会是追加模式;如果文件不存在,创建新文件以供读写
rb+ 以二进制格式打开一个文件用于读写,文件指针将放在开头
wb+ 以二进制格式打开一个文件用于读写,如果该文件已存在则将其覆盖;如果不存在,创建新文件以供读写
ab+ 以二进制格式打开一个文件用于追加,如果该文件已存在,文件指针将放在文件结尾,如果该文件不存在,创建新文件以供读写

读写(初见)

如果直接使用read(),将一次性读取文本中所有内容;

如果使用read(n),将会一次性读取n个字节,下一次读取将从此处继续

readline()方法可以一行一行地读取文件内容

readlines()方法依旧是一行一行读取,不过会一次性读取完,并将每一行存在列表当中作为一个元素

使用write()方法写入数据到文件

提示:当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入,只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了

小练习:复制文件

让我们用以上知识,先实现一个简单的文件复制功能吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1、获取源文件名
old_name = "/Users/sherlockgy/Desktop/1.txt"

# 2、计算新文件名
position = old_name.rfind(".")
new_name = old_name[:position] + "[复件]" + old_name[position:]


# 3、缓存读取的文件内容
file_read = open(old_name, "r")
file_content = file_read.read()

# 4、写入新文件
file_write = open(new_name, "w")
file_write.write(file_content)

# 5、关闭文件
file_read.close()
file_write.close()

print("复制完成!")

大文件读取的处理

如果我们需要读取的文件非常庞大,庞大到超过我们的内存大小,那么我们无论如何都不能使用以上的read()方法去读取文件内容

那么我们是否可以使用readline()方法去读取呢?我们这里是不建议的,原因如下:有些文件是经过压缩的,整个文件没有换行符和空白符,那么如果使用的是readline()依然会出现“挤爆内存”的情形

那么我们要怎么做呢?我们可以指定每一次读取的字节数

1
2
3
4
5
6
while True:
content = old_file.read(1024)

if len(content) == 0:
break
new_file.write(content)

文件的定位读写

现有一个txt文件,内容如下:

1
2
123456789
abcdefg

现在我们执行以下程序,该程序使用了seek()方法

1
2
3
4
5
f = open("/Users/sherlockgy/Desktop/1.txt", "r")
f.seek(2, 0)
print(f.read(1))
print(f.read(1))
print(f.read(1))

输出结果为:

1
2
3
3
4
5

seek(offset [,from])方法改变当前文件的位置。Offset变量表示要移动的字节数(偏移量)。From变量指定开始移动字节的参考位置。

如果from被设为0,这意味着将文件的开头作为移动字节的参考位置。如果设为1,则使用当前的位置作为参考位置。如果它被设为2,那么该文件的末尾将作为参考位置。

我们可以使用tell()方法查找当前文件指针的位置

1
2
3
4
f = open("/Users/sherlockgy/Desktop/1.txt", "rb")
print("初始指针:" + str(f.tell()))
f.seek(2, 0)
print("偏移后指针:" + str(f.tell()))
1
2
初始指针:0
偏移后指针:2

补充:在文本文件中,没有使用b模式选项打开的文件,只允许从文件头开始计算相对位置,从文件尾计算时就会引发异常【can’t do nonzero end-relative seeks】

文件的常见操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os

# 重命名
os.rename("xxx.txt", "yyy.txt")

# 删除文件
os.remove("yyy.txt")

# 新建文件夹
os.mkdir("test")

# 删除文件夹
os.rmdir("test")

# 获取当前文件操作路径
os.getcwd()

# 改变默认目录
os.chdir("../")

# 获取目录列表(重要)
# 该方法获取的不包括文件路径,仅文件名
os.listdir("./")

小练习:批量重命名

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
import os

# 输入需要重命名的文件所在路径
source = "/Users/sherlockgy/Desktop/123"

# 需要加上的前缀
before = "[测试]"


# 遍历每一个文件获得文件名
def iter_file(file_source):
"""迭代文件列表"""

# 得到目录下所有文件的名称
file_list = os.listdir(file_source)

for file_name in file_list:
# 判断是否是一个文件
if os.path.isfile(file_source + "/" + file_name):
old_name = file_source + "/" + file_name
new_name = file_source + "/" + before + file_name
os.rename(old_name, new_name)
else:
new_source = file_source + "/" + file_name
iter_file(new_source)


iter_file(source)