文件操作是编程语言的一项重要能力。包括了文件的读写操作,文件以及目录的操作等。下面就逐一介绍。
10.1 文件的打开
在python中,使用open语句打开一个文件。演示的代码如下。
f=open('myfile.txt','r')
上述的语句当中,'myfile.txt'是文件的名称,f是获取到的文件句柄,在以后的文件读写操作都需要通过这个句柄进行。
如果这个文件不是绝对路径的话,那么python会在sys.path里面列出的路径集合中寻找这个文件;如果是绝对路径,例如'/Users/chenosanabin/Documents/myfile.txt'或者‘D:\\myfile.txt’这样的绝对路径,python会直接用这个绝对路径中访问文件。
文件路径也是乱码频发的一个源头。linux、windows、mac系统对文件路径的中文编码均不相同。windows系统使用原生的unicode编码对文件路径中的中文编码;linux系统的文件路径编码可以自行设置;mac系统则使用utf8对文件路径进行编码。
在不同的系统中,一个相同的文件路径需要进行不同的编码才能够访问到。很多人为了避免麻烦,会自觉地避免把文件放在中文目录,避免文件名中含有中文。
实际上如果知道了背后的原理,是可以很妥善地处理中文路径的问题的。一个系统的文件路径的编码,可以通过sys.getfilesystemencoding()这个函数获取到。
调用这个函数后,windows系统会返回'mbcs',这个是原生unicode编码;unix系统是用户自行设置,可能返回不同编码;mac系统会返回'utf8'。
一个文件路径,在程序里面最好使用unicode存储,然后根据 sys.getfilesystemencoding()这个函数返回的值,通过encode把unicode转化成相应的编码,就可以正确访问到文件了。演示的代码如下:
#coding=utf8
import sys
filepathencoding=sys.getfilesystemencoding()
filename=u'中文文件.txt'
if filepathencoding!='mbcs':
filename=filename.encode(filepathencoding)
f=open(filename,'r')
使用open()来打开一个文件,需要考虑文件的读写模式问题。如果是只读模式,那么就不能够修改文件;如果是写模式,那么就可写可读了。这是为了防止对文件的误操作加入的功能。
open()的第二个参数就是设置读写模式的。其中'r'是只读;'w'是可写的;'a'是增量写入;'r+'是可读可写。如果选择了'w'模式,那么写入的时候会从文件头开始写入;而选择了'a'模式,写入的时候会从文件的末尾开始写入。
在windows系统里面,文本文件和非文本文件是有区别的。对一个文本文件进行写入的时候,换行符会被转成系统特定符号。这个事情是windows系统做的,会破坏文件的结构。对于图片文件或者音视频文件,这种修改会导致文件无法打开。因此在读写图片和音视频文件的时候,需要特别声明这是非文本文件。此时需要设置文件使用'b'模式打开,也就是二进制文件。'b'模式和之前提到的读写模式组合,可以变成'rb','wb','ab' 等等。'b'模式是为了windows系统特别存在,对于unix体系的系统来说,'b'模式是透明的。
在文件的读写完毕后,一个好的习惯是,把文件的句柄关闭,释放文件资源。文件的关闭使用close()方法。
f.close()
在python里有一个with语句,离开with语句的时候,会自动就调用了close()。
一个简单的使用with语句操作文件的代码如下:
with open(filename,'r') as f:
print(f.read())
with语句把open()语句返回的文件句柄赋值给f,接着所有的文件操作都可以在with语句下面进行。当离开with语句,f的close()操作会被自动调用,不再需要自行处理了。
10.2 文件的读写
要读取一个文件,有三种方式。一行一行读,一块一块读,全部读。
如果文件大小合适,不超过python默认的缓存,操作系统的内存也装得下,全部读进来是可以的。这个时候的操作就很简单。使用f.read()或者f.readlines()就可以了。
f.read()把读进来的内容看作是一个字符串。而f.readlines()把读进来的内容看作是一个list,list里面的每个item都是一行。
比如一个文件,包含了下面几行
1
2
3
4
使用f.read()的话,结果是
'1\n2\n3\n4\n'
使用f.readlines()的话,结果是
['1\n', '2\n', '3\n', '4\n']
无论是f.read()还是f.readlines(),换行符都不会被处理。使用readlines()的时候,要记得自行处理换行符。
如果文件很大,无法一下子读进来,可以采用一块一块读,或者一行一行读。
使用f.read(readsiez)每次读取readsize大小的内容。循环读取文件直到读入的内容是空的。演示代码如下:
r=f.read(1)
while(r!=''):
r=f.read(1)
上述代码每次读取1个字节,一直读到空字符,说明到了文件末尾了。
使用f.readline()一行一行读取文件,每次读一行,循环读取直到读入的内容为空。演示代码如下:
r=f.readline()
while(r!=''):
r=f.readline()
无论f.read()、f.readline()、f.readlines(),默认都是从文件开头开始读起的。如果需要直接读取文件的某个位置的内容,那么就使用文件指针,从指针那里开始读起。使用f.seek()函数可以修改文件指针。
f.seek()有两个参数,第一个是位移,而第二个开始地点。开始地点有三种选择:
文件开头(os.SEEK_SET)
当前位置(os.SEEK_CUR)
文件结尾(os.SEEK_END)。
举几个例子:
f.seek(5,os.SEEK_SET)把指针指向文件开头顺数第五个字节;f.seek(-3,os.SEEK_END)把指针指向文件结尾倒数第三个字符。
修改了文件指针之后,文件的读写就会从当前的指针开始。
使用f.write()可以往文件里面添加内容。下面的例子展示了在文件末尾倒数一个字符的位置写入'5\n\'
with open(filename,'w') as f:
f.seek(-1,os.SEEK_END)
f.write('5\n')
文件内容的读写也涉及到了字符编码的问题,和之前提到的字符编码处理一样,需要按照编码类型decode成unicode字符,再进行处理;而写入的时候,把unicode字符encode为编码类型,再写入。
10.3 文件和目录的操作
python在os模块和os.path模块中提供了很多文件和目录的常用操作。
>>> import os.path
>>>os.path.exists('/Users/chenosanabin/Documents/dir0/dir1/file11.txt')
True
>>>os.path.isdir('/Users/chenosanabin/Documents/dir0/dir1/file11.txt')
False
>>>os.path.isfile('/Users/chenosanabin/Documents/dir0/dir1/file11.txt')
True
使用os.mkdir()或者os.makedirs()来创建目录
>>> os.path.exists('/Users/chenosanabin/Documents/d0')
False
>>>os.mkdir('/Users/chenosanabin/Documents/d0')
>>>os.path.exists('/Users/chenosanabin/Documents/d0')
True
当路径中有未创建的目录时,使用os.mkdir()进行创建会失败,这个时候应该使用os.makedirs()来循环创建目录
>>>os.mkdir('/Users/chenosanabin/Documents/d1/d11')
Traceback (most recent calllast):
File "", line 1, in
OSError: [Errno 2] No suchfile or directory: '/Users/chenosanabin/Documents/d1/d11'
>>>os.makedirs('/Users/chenosanabin/Documents/d1/d11')
>>> os.path.exists('/Users/chenosanabin/Documents/d1/d11')
True
使用os.rmdir()或者os.removedirs()来删除目录,此时需要删除的目录应该都是空的,如果不是空目录就不会被删除。
>>>os.rmdir('/Users/chenosanabin/Documents/d0')
>>>os.path.exists('/Users/chenosanabin/Documents/d0')
False
如果需要循环删除,可以使用os.removedirs(),只要文件路径的每个目录节点是空的,就进行删除。
>>>os.removedirs('/Users/chenosanabin/Documents/d1/d11')
>>>os.path.exists('/Users/chenosanabin/Documents/d1')
False
可以看到,在循环删除目录'/Users/chenosanabin/Documents/d1/d11'的时候,最终'/Users/chenosanabin/Documents/d1'也被删除了。
使用os.remove()来删除文件
>>>os.remove('/Users/chenosanabin/Documents/f1.txt')
>>>os.path.exists('/Users/chenosanabin/Documents/f1.txt')
False
使用os.walk()来遍历一个目录。
filelist=os.walk('/Users/chenosanabin/Documents/dir0')
for fl in filelist:
print(fl)
在这里'/Users/chenosanabin/Documents/dir0'是根目录,os.walk()从根目录开始遍历所有的子目录和子文件,并且把所有的目录下面的目录列表和文件列表列出来,结果用(root,dirlist,filelist)的形态展现。上述代码的输出类似于以下:
('/Users/chenosanabin/Documents/dir0',['dir1', 'dir2'], ['file1.txt'])
('/Users/chenosanabin/Documents/dir0/dir1',['dir11'], ['file11.txt'])
('/Users/chenosanabin/Documents/dir0/dir1/dir11',[], ['file111.txt'])
('/Users/chenosanabin/Documents/dir0/dir2',[], ['file21.txt'])
从结果可以看出,dir0目录下面有两个目录dir1和dir2,有一个文件file1.txt。而dir1目录下面有dir11目录和file11.txt文件。等等。
使用os.walk()可以很快地遍历一个根目录,访问所有的文件和目录,非常方便。
领取专属 10元无门槛券
私享最新 技术干货