文件操作介绍

python中的都是用 open() 函数,调用 open() 函数,应用程序会发起系统调用 open(...) ,进而对文件完成操作~。以下示例均在 python3 环境中完成~

读取文件

简单读取

读取文件示例:

f = open(file='/Users/luyi/tmp/abc', mode='r', encoding='utf-8')
data = f.read()       # 读取文件中所有数据
print(data)             # 打印文件内容
f.close()                 # 关闭操作系统级打开的文件

open() 函数参数说明:

file:指定文件路径
mode:指定打开文件的模式,r 表示只读模式,还有很多其他模式,下面会依次介绍
encoding:指定从文件中读取的数据使用什么解码标准(decode),解码后以unicode的形式存放到内存中~。注意:若不指定encoding,打开文件的默认编码就是操作系统的默认编码,简体中文版的windows是gbk,linux下是utf-8。以什么编码存放,就应该以什么编码读取~

读取文件操作的注意点: 1)读取动作完成之后,需要关闭操作系统级打开的文件(f.close()) 2)回收应用程序级的变量(del f) Tip:f 变量没有引用之后,python垃圾回收机制会自动回收 f 变量,无需手动进行。但是f.close()必须手动完成,否则会持续占用系统资源,直至程序运行结束~

也有一种方式 不需要手动关闭系统级资源

with open() as f:
    pass
		
# 将上述代码进行调整
with open(file = '/Users/luyi/tmp/abc', mode = 'r', encoding = 'utf-8') as f:
    data = f.read()
    print(data)

Tip:with段代码运行结束,系统级打开的文件会自动被关闭~

with 还可以同时打开多个文件,如下示例实现了文件的文件的逐行复制,其中涉及到的方法下面会介绍:

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'r', encoding='utf-8') as read_file, open(file = '/Users/luyi/tmp/cde.txt', mode = 'w', encoding='utf-8') as write_file:
    for line in read_file:
        write_file.write(line)

在读取文件时,若不清楚正在打开的文件是什么编码,可以使用chardet模块探测文件的编码方式

import chardet
result = chardet.detect(open('/Users/luyi/tmp/abc.txt', mode='rb').read())
print(result)

结果输出:
{'encoding': 'utf-8', 'confidence': 0.87625, 'language': ''}

上述示例中,使用 'rb' 模式打开文件,即以只读,且以字节为单位打开文件。非文本文件只能使用 b 模式打开,因为文件本身就是以字节的形式存放在存储设备上的。文本文件可以使用字节模式打开,也可以 以本文模式打开(默认),图片格式的文件(jpg,png...)、视频音频格式文件(mp3、mp4、avi...)则只能以字节模式打开。

当文本文件使用字节模式打开时,open函数中不能使用 encoding 参数,若要转成unicode,只能现将数据读到内存,然后手动decode~

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'rb', encoding = 'utf-8') as f:
    print(f.read())

# open中使用 encoding参数,会直接抛出错误信息
ValueError: binary mode doesn't take an encoding argument

#############

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'rb') as f:
    data = f.read()
    print(data)                           # 原样输出内容
    print(data.decode('utf-8'))  # 解码后输出文本内容
		
输出结果:
b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8ckitty\n'
你好,kitty

循环文件

f = open("abc.txt",'r',encoding="utf-8")
data = f.read()

使用这种方式读取文件,会一次性将文件中所有的数据读到内存中,如果文件很大,这会给内存造成很大的压力,这并不是一种合理的读取文件方式。这时候可以逐行获取数据~

# 使用 readline() 仅读取文本的一行内容,光标从文件的开头移动到一行的末尾(只读一行,遇到\r 或者 \n 为止),然后读物第二行
with open(file = '/Users/luyi/tmp/def.txt', mode = 'r', encoding='utf-8') as f:
    data = f.readline()         # 仅读取一行
    print(data)
		
# 使用 while 循环遍历文件
with open(file = '/Users/luyi/tmp/def.txt', mode = 'r', encoding='utf-8') as f:
    line = f.readline()
    while line:                   # 当 line 不为None,继续读取文件
        print(line, end='')    # print() 自带换行功能,line中已有'\n',print不用再换行,所以加上参数end=''
        line = f.readline()
				
# 使用 for 循环遍历 f 变量也是逐行读取文件内容,与 readline() 类似。这里其实内部调用了 f 变量的next()方法,逐行迭代文件对象,这种方式的遍历貌似比 readline() 要来的简单明了。
with open(file = '/Users/luyi/tmp/def.txt', mode = 'r', encoding='utf-8') as f:
    for line in f:
        print(line, end='')

Tip:在调用next()方法时,不能同时调用 tell() 方法获取文件光标位置

和 readline() 功能类似的还有个 readlines() ,readlines()会将文件的内容一次性获取,并存储为列表,文件每一行内容为列表的一个元素~

with open(file = '/Users/luyi/tmp/def.txt', mode = 'r', encoding='utf-8') as f:
    line = f.readlines()
    print(type(line))
    print(line)
		
结果输出:
<class 'list'>
['1.aaaaaaaaa\n', '2.bbbbbbbbb\n', '3.ccccccccc\n', '4.ddddddddd\n', '5.eeeeeeeee\n', '6.fffffffff\n']

写文件

python写文件,同样使用open(),模式使用 'w'(只写),'wb'(二进制写),'w+'(可读可写)~

直接写

f = open(file = '/Users/luyi/tmp/abc.txt', mode = 'w', encoding = 'utf-8')
f.write('你好~~,hello')
f.close()       # 同样需要注意,关闭系统级别的资源

open() 参数说明:

file:指定文件路径,若该目录下文件不存在,会创建,若文件存在,直接覆盖;若目录不存在,抛出错误
mode='w':指定为只写模式
encoding:指定写入到文件时,使用的编码标准,即将 内存中 unicode 格式的数据 按照指定编码标准编码后进行存储(这里是 encoding 过程,和read相反)

同样可以使用 with open() 方式代替:

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'w', encoding = 'utf-8') as f:
    f.write('你好!!,hello')

二进制写

二进制写 的模式为 'wb',当指定 mode='wb' ,写入数据时必须传入字节类型的数据(bytes类型),且需要手动添加换行符(\n)

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'wb') as f:
    f.write('你好~~,hello'.encode('utf-8'))

Tip:这里由于是'wb'模式,write()函数中需要传入bytes类型的数据,直接使单引号或双引号包含的数据是 str 类型,str 类型的数据 encode 之后,返回的是 bytes类型,或者也可以使用 b'' ~

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'wb') as f:
    f.write(b'abc')          # bytes 类型只能包含 ASCII 码字符

同时写入多行使用writelines()函数

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'w', encoding='utf-8') as f:
    f.writelines(['111111\n', '222222\n', '333333\n'])    # 需要自己添加换行符
		
# wb模式
with open(file = '/Users/luyi/tmp/abc.txt', mode = 'wb') as f:
    f.writelines([bytes('你好\n'.encode('utf-8')), 'kitty\n'.encode('utf-8')])

Tip:f.flush() 可立刻将文件内容从内存刷到磁盘 注意点:当以 'w' 、'wb'、'w+'(写读模式,很少用) 模式打开文件时,在打开文件的同时会将文件内容清空~

读写模式

打开文件模式还有读写模式,打开后可进行读写操作,即 mode='r+';

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'r+', encoding='utf-8') as f:
    print(f.read())
    f.write('你好~~,hello')

追加文件

打开文件时,若使用模式 “a” 或 “ab” ,则只能对文件进行追加,即:在原来内容的尾部追加内容~

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'a', encoding='utf-8') as f:
    f.write('aaaaaaa\n')
    f.write('vvvvvvv\n')
		
文件内容:
你好
kitty
aaaaaaa
vvvvvvv

文件操作的各种模式说明

操作文件的模式列举

模式 说明
'r' 读模式
'w' 写模式
'a' 追加模式
'b' 二进制模式
't' text 模式(默认)
'+' 同时读写某个文件
'x' 只写模式,文件不存在 自动创建,文件原先就存在则报错

除了上述几个模式,还有以上模式的各种组合~ 常用的模式还有: rb','wb','ab' # 非文本文件,只能使用 'b' 模式打开 'r+','w+','a+' # 都表示可读写,但是注意了 'w+' 模式打开文件,会首先清空文件,所以一般模式 'w+' 很少用,使用模式 'a+' 时,光标会直接跳到文件的最后,所以之前的内容读不到~

针对文件的其他操作

其他文件操作的常用函数:

def writable(self, *args, **kwargs): # real signature unknown
		判断文件是否可写
		
def readable(self, *args, **kwargs): # real signature unknown
		判断文件是否可读

def fileno(self, *args, **kwargs): # real signature unknown
		返回文件句柄在内核中的索引值,做IO多路复用时可以用到

def seek(self, *args, **kwargs): # real signature unknown
		移动光标移到指定位置,注意这里移动光标的单位是字节

def seekable(self, *args, **kwargs): # real signature unknown
		判断文件是否可进行 seek 操作

def tell(self, *args, **kwargs): # real signature unknown
		返回当前文件操作光标位置 

def truncate(self, *args, **kwargs): # real signature unknown
		按指定长度截断文件,经个人测试 python3中的 truncate 方法貌似已经失效

在对文件操作的过程中,光标会不断移动,例如readline()方法读取一行信息后,光标会移动至当前行的末尾,writeline()光标也会移动至当前行末尾(写过程貌似光标一直在末尾)~

光标移动的单位和文件被打开的模式相关,若文件以文本模式打开时,read(5) 会使光标向后移动5个字符;若文件以非文本模式打开('b'模式),则 read() 会向后移动5个字节。seek函数不管模式是什么,光标移动的单位都是字节~

例如文本内容如下:

123456789
abcdefghi

code:

with open(file = '/Users/luyi/tmp/abc.txt', mode = 'r+', encoding='utf-8') as f:
    f.seek(5)
    print(f.readline())
    print(f.tell())        # 输出当前光标位置
		
结果输出:
6789

10

seek 方法可接受2个参数:

fileObject.seek(offset[, whence])

offset:光标的偏移量,以字节为单位 whence:可选参数,默认值为 0。表示要从哪个位置开始偏移;0 代表从文件开头开始偏移,1 代表从当前位置开始偏移,2 代表从文件末尾开始偏移。

.................^_^