python文件与输入输出
注:本文档是学习《Python核心编程(第二版)》时的整理。
1.文件对象
文件对象不仅可以用来访问普通的磁盘文件,也可以访问任何其他类型抽象层面上的"文件"。一旦设置了合适的"钩子",就可以访问具有文件类型接口的其它对象,就好像访问的是普通的文件。
2.文件内建函数(open()和file())
open()和file()提供了初始化输入/输出(I/O)操作的通用接口,open()内建函数成功打开文件会返回一个文件对象,否则引发一个异常。当操作异常,会产生一个IOError异常。基本语法如下:
file_object = open(file_name, access_mode='r', buffering=-1)
file_name是包含要打开的文件名字的字符串,可以是相对路径或者绝对路径。
可选变量access_mode是一个字符串,代表文件的打开模式,'r'表示读取,'w'表示写入,'a'表示追加。另外,'U'代表通用换行符支持。
使用'r'或者'U'模式打开的文件必须是已经存在的;使用'w'模式打开的文件若已经存在则首先清空,然后(重新)创建;以'a'模式打开的文件是为追加数据做准备的,所有写入的数据都追加到文件的末尾,即使seek到了其他的地方,如果文件不存在,则自动创建。
另外,C语言中fopen()支持的模式在python下也可以使用,如'+'代表读和写。
可选参数buffering用于指示访问文件所采用的缓冲方式,其中0表示不缓冲,1表示只缓冲一行数据,任何其他大于1的值代表使用给定值作为缓冲区的大小。不提供参数或者给定负值代表使用系统默认的缓冲机制。
1 >>> fp = open('/etc/motd') #以读方式打开 2 >>> fp = open('test', 'w') #以写方式打开 3 >>> fp = open('data', 'r+') #以读写方式打开 4 Traceback (most recent call last): 5 File "<stdin>", line 1, in <module> 6 IOError: [Errno 2] No such file or directory: 'data' 7 >>> fp = open(r'test.txt', 'rb') #以二进制读模式打开
2.1 工厂函数file()
open()和file()函数具有相同的功能,可以任意替换,任何使用open()的地方,都可以用file()替换它。
2.2 文件内建方法
open()成功执行返回一个文件对象,所有对该文件的后续操作都将通过这个"句柄"进行,文件方法可以分为四类:输入、输出、文件内移动及杂项操作。
2.3 读出
file.read([size])方法用来直接读取字节到字符串中,最多读取给定数目个字节,如果没有给定size参数(默认值为-1,)或者size值为负,文件将被读取至尾。
1 >>> f = open('test.txt') 2 >>> f.read() 3 'hello\nworld\n'
file.readline([size])方法读取打开文件的一行(读取下个行结束符之前的所有字节)。然后整行,包括行结束符,作为字符串返回。size参数可选,默认为-1,代表读至结束符。如果提供了该参数,那么超过size个字节后会返回不完整的行。
1 >>> f = open('test.txt') 2 >>> f.readline() 3 'hello\n' 4 >>> f.readline() 5 'world\n'
file.readlines([sizehint])方法读取所有(剩余的)行然后把它们作为一个字符串列表返回,可选参数sizeint代表返回的最大字节大小,如果大于0,那么返回的所有行应该大于有sizeint字节(可能稍微大于这个数字,因为需要凑齐缓冲区大小)。
1 >>> f = open('test.txt') 2 >>> f.readlines() 3 ['hello\n', 'world\n']
注:read()或readlines()从文件中读取行时,并不会删除行结束符,这需要程序员来操作。常用的操作如下:
1 >>> f = open('myFile', 'r') 2 >>> data = [line.strip() for line in f.readlines()] 3 >>> f.close()
2.4 写入
file.write(str)方法与read()方法相反,把含有文本数据或二进制数据块的字符串写入到文件中去。
file.writelines(sequence)方法是针对列表的操作,它接受一个字符串列表作为参数,将它们写入到文件,行结束符并不会自动加入,如果需要的话,必须在调用writelines()前给每行结尾加上行结束符。
2.5 文件内移动
seek(offset[, whence])方法(类似于C中的seek()函数)可以在文件中移动文件指针到不同的位置。offset字节代表相对于某个位置偏移量,可选参数whence表示起始位置,默认值为0,代表从文件开头算起(及绝对偏移量),1代表从当前位置算起,2代表从文件末尾算起。
text()方法是对seek()的补充,告诉你当前文件指针在文件中的位置—从文件起始算起,单位为字节。
1 >>> f = open('myFile', 'w+') 2 >>> f.writelines(['hello\n', 'world\n']) 3 >>> f.readlines() 4 [] 5 >>> f.seek(0,0) 6 >>> f.readlines() 7 ['hello\n', 'world\n'] 8 >>> f.close() 9 >>>
3. 文件迭代
一行一行的访问文件:
1 for eachLine in f: 2 ;
在本次循环中,eachLine代表文本文件的一行(包括末尾的行结束符)。
4. 其他
file.close()方法通过关闭文件来结束对它的访问。
file.fileno()方法返回打开的文件的描述符。是一个整型。
file.flush()方法会直接把内部缓冲区中的数据立刻写入文件,而不是被动的等待输出缓冲区被写入。
file.isatty()是一个布尔内建函数,当文件是一个类tty设备时返回True,否则返回False。
file.truncate([size])方法将文件截取到当前文件指针位置或者到给定的size,以字节为单位。如果没有传递size参数,那么默认将截取到文件的当前位置。例如:刚打开了一个文件,然后立即调用truncate()方法,那么文件(内容)实际上被删除,这时候其实是从0字节开始截取的。
file.tell()方法返回当前的文件指针的位置。
5. 文件方法杂项
使用迭代器读取一个文件:
1 >>> filename = raw_input('Enter file name: ') 2 >>> f = open(filename, 'r') 3 >>> for eachLine in f: 4 ... print eachLine, 5 >>> f.close()
写入到文件:
1 import os 2 3 filename = raw_input('Enter file name: ') 4 fobj = open(filename, 'w') 5 while True: 6 aline = raw_input('Enter a line(\'.\' to quit): ') 7 if aline != '.': 8 fobj.write("%s%s" %(aline, os.linesep)) 9 else: 10 break 11 fobj.close()
以读写模式创建一个新的文件(可能是清空了一个现有的文件),再向文件写入数据后,使用seek()方法在文件内部移动,使用tell()方法展示移动过程。
1 >>> f = open('/tmp/x', 'w+') 2 >>> f.tell() 3 0 4 >>> f.write('test line 1\n') 5 >>> f.tell() 6 12 7 >>> f.write('test line 2\n') 8 >>> f.tell() 9 24 10 >>> f.seek(-12, 1) 11 >>> f.tell() 12 12 13 >>> f.readline() 14 'test line 2\n' 15 >>> f.seek(0, 0) 16 >>> f.readline() 17 'test line 1\n' 18 >>> f.readline() 19 'test line 2\n' 20 >>> f.tell() 21 24 22 >>> f.close() 23 >>>
6. 行分隔符和其他文件系统的差异
操作系统间所支持的行分隔符不同。Linux系统中,行分隔符是换行符NEWLINE(\n)字符,Windows32下是(\r\n)。另外路径分隔符也有所差异。
python的os模块可以帮我们解决这些问题。
os模块属性 |
描述 |
linesep |
用于在文件中分隔行的字符串 |
sep |
用于分割文件路径名的字符串 |
pathsep |
用于分割文件路径的字符串 |
curdir |
当前工作目录的字符串名字 |
pathdir |
(当前工作目录的)父目录字符串名字 |
7. 文件内建属性
文件对象还包括一些数据属性,这些属性保存了文件对象相关的附加数据。例如文件名、文件的打开模式、文件是否已被关闭、以及一个标志变量,它可以决定使用print语句打印下一行前是否要加入一个空白字符。属性见附录。
8. 标准文件
标准文件指的是:标准输入(一般指键盘)、标准输出(到显示器的缓冲输出)、和标准错误(到屏幕的非缓冲输出)。python中可以通过sys来访问这些文件的句柄,导入sys模块后,可以使用sys.stdin、sys.stdout和sys.stderr访问。print语句通常是输出到sys.stdout,raw_input()则从sys.stdin接收输入。
1 >>> fobj = sys.stderr 2 >>> fobj.write('abc\n') 3 abc 4 >>> f = sys.stdout 5 >>> f.write('def\n') 6 def
9. 命令行参数
sys模块通过sys.argv属性提供了命令行参数的访问。命令行参数是调用某个程序时除程序名以外的其他参数。
C语言中,argc和argv分别代表参数个数(argument count)和参数向量(argument vector)。argv变量代表一个从命令行上输入的各个参数组成的字符串数组;argc变量代表输入的参数个数。
python中,argc就是sys.argv列表的长度,而该列表的第一项sys.argv[0]永远是程序的名字。
1 import sys 2 3 print 'you entered', len(sys.argv), 'arguments...' 4 print 'they are: ', str(sys.argv) 5 6 V:\python>C:/Python27/python.exe v:/python/argv.py 78 tales 89 hawk 7 you entered 5 arguments... 8 they are: ['v:/python/argv.py', '78', 'tales', '89', 'hawk']
11. 文件系统
对文件系统的访问是大多通过python的os模块实现。os模块除了对进程和线程的运行环境进行管理外,还负责处理大部分的文件系统的操作,这些功能包括删除/重命令文件、遍历目录树、以及管理文件访问权限等,详见附录。
另外,os.path可以完成一些针对路径名的操作。它提供的函数可以完成管理和操作文件路径名的各个部分,获取文件或子目录信息,文件路径查询等操作。详见附录。
11.1 举例
(1)导入os模块,然后检查并确认’/tmp’是否是一个合法目录,并切换到这个临时目录,之后使用getcwd()方法确认当前位置。
1 >>> import os 2 >>> os.path.isdir('/tmp') 3 True 4 >>> os.chdir('/tmp') 5 >>> cwd = os.getcwd() 6 >>> cwd 7 '/tmp'
(2)接下来在临时目录创建一个子目录,然后用listdir()方法确认目录为空。
1 >>> os.mkdir('example') 2 >>> os.chdir('example') 3 >>> cwd = os.getcwd() 4 >>> cwd 5 '/tmp/example' 6 >>> os.listdir(cwd) 7 []
(3)接下来创建一个有两行内容的test文件,之后列出目录确认文件被成功创建。
1 >>> fobj = open('test', 'w') 2 >>> fobj.write('foo\n') 3 >>> fobj.write('bar\n') 4 >>> fobj.close() 5 >>> os.listdir(cwd) 6 ['test']
(4)接下来替换’test’文件名为’filetest.txt’,再列出文件的路径,判断文件是否为文件或者目录,获取(dirname(),basename())元组,获取(filename, extension)元组。
1 >>> os.rename('test', 'filetest.txt') 2 >>> os.listdir(cwd) 3 ['filetest.txt'] 4 >>> path = os.path.join(cwd, os.listdir(cwd)[0]) 5 >>> path 6 '/tmp/example/filetest.txt' 7 >>> os.path.isfile(path) 8 True 9 >>> os.path.isdir(path) 10 False 11 >>> os.path.split(path) 12 ('/tmp/example', 'filetest.txt') 13 >>> os.path.splitext(os.path.basename(path)) 14 ('filetest', '.txt')
(5)最后,显示文件的内容,之后删除之前创建的文件和目录
1 >>> fobj = open(path) 2 >>> for eachLine in fobj: 3 ... print eachLine, 4 ... 5 foo 6 bar 7 >>> fobj.close() 8 >>> os.remove(path) 9 >>> os.listdir(cwd) 10 [] 11 >>> os.chdir(os.pardir) 12 >>> os.rmdir('example')
总结上面的步骤,完整的代码如下:
1 #!/usr/bin/env python 2 3 import os 4 5 for tmpdir in ('/tmp', r'c:\temp'): 6 if os.path.isdir(tmpdir): 7 break 8 else: 9 print 'no temp directory avaliable' 10 tmpdir = '' 11 12 if tmpdir: 13 os.chdir(tmpdir) 14 cwd = os.getcwd() 15 print '*** current temporary directory' 16 print cwd 17 18 print '*** creating example directory' 19 os.mkdir('example') 20 os.chdir('example') 21 cwd = os.getcwd() 22 print '*** new working directory' 23 print cwd 24 print '*** original directory listing' 25 print os.listdir(cwd) 26 print '*** creating test file' 27 fobj = open('test', 'w') 28 fobj.write('foo\n') 29 fobj.write('bar\n') 30 fobj.close() 31 print '*** updated directory listing' 32 print os.listdir(cwd) 33 34 print "renaming 'test' to 'filetest.txt'" 35 os.rename('test', 'filetest.txt') 36 print '*** updated directory listing' 37 print os.listdir(cwd) 38 39 path = os.path.join(cwd, os.listdir(cwd)[0]) 40 print '*** full file pathname' 41 print path 42 print "*** (pathname, basename) == " 43 print os.path.split(path) 44 print '*** (filename, extension) == ' 45 print os.path.splitext(os.path.basename(path)) 46 47 print '*** displaying file contents:' 48 fobj = open(path) 49 for eachLine in fobj: 50 print eachLine, 51 fobj.close() 52 53 print '*** deleting test file' 54 os.remove(path) 55 print '*** updated directory listing:' 56 print os.listdir(cwd) 57 os.chdir(os.pardir) 58 print '*** deleting test directory' 59 os.rmdir('example') 60 print '*** DONE'
输出结果:
1 [root@192 python_code]# python ospathex.py
2 *** current temporary directory
3 /tmp
4 *** creating example directory
5 *** new working directory
6 /tmp/example
7 *** original directory listing
8 []
9 *** creating test file
10 *** updated directory listing
11 ['test']
12 renaming 'test' to 'filetest.txt'
13 *** updated directory listing
14 ['filetest.txt']
15 *** full file pathname
16 /tmp/example/filetest.txt
17 *** (pathname, basename) ==
18 ('/tmp/example', 'filetest.txt')
19 *** (filename, extension) ==
20 ('filetest', '.txt')
21 *** displaying file contents:
22 foo
23 bar
24 *** deleting test file
25 *** updated directory listing:
26 []
27 *** deleting test directory
28 *** DONE
12. 永久存储模块
永久存储解决的是用户需要输入大批数据且数据会多次重复使用的情况,可以把用户的数据归档保存起来供以后调用,这样可以避免每次输入同样的数据。
12.1 pickle和marshal模块
python提供了许多可以实现最小化永久性存储的模块,其中的一组(pickle和marshal)可以用来转换并存储python对象。该过程将基本类型复杂的对象转换为一个二进制数据集合,这样就可以把数据集合保存起来或者通过网络发送,然后再重新把数据集合恢复成原来的对象格式,这个过程被称为数据的扁平化、数据的序列化或者数据的顺序化。
pickle和marshal模块可以对python对象进行转换,但本身并没有提供“永久性存储”的功能,因为他们没有为对象提供名称空间,也没有提供对永久性存储对象的并发写入访问,它们只能存储转换python对象,为保存和传输提供方便。marshal和pickle模块的区别在于marshal只能处理简单的python对象(数字、序列、映射以及代码对象),而pickle还可以处理递归对象。
12.2 DBM风格的模块
*db*系列的模块使用传统的DBM格式写入数据,Python提供了DMB的多种实现:dbhash/bsddb、dbm、gdbm和dumbdbm等。如果不确定,最好使用anydbm模块,它会自动检测系统上已安装的DBM兼容模块,并选择“最好”的一个。
12.3 shelve模块
shelve模块使用anydbm模块寻找合适的DBM模块,然后使用cPickle来完成对存储转换过程。shelve模块允许对数据库文件进行并发的读访问,但不允许共享读/写访问。下图展示了存储转换与永久性存储模块之间的关系,以及为何shelve对象能够成为两者的最好的选择。
附录:
文件对象的访问模式
文件模式 |
操作 |
r |
以读方式打开 |
rU或U |
以读方式打开,同时提供通用换行符支持 |
w |
以写方式打开(必要时情况) |
a |
以追加模式打开(从EOF开始,必要时创建新文件) |
r+ |
以读写模式打开 |
w+ |
以读写模式打开(参见w) |
a+ |
以读写方式打开(参见a) |
rb |
以二进制读模式打开 |
wb |
以二进制写模式打开(参见w) |
ab |
以二进制追加模式打开(参加a) |
rb+ |
以二进制读写模式打开(参见r+) |
wb+ |
以二进制读写模式打开(参见w+) |
ab+ |
以二进制读写模式打开(参见a+) |
文件对象方法
文件对象方法 |
操作 |
file.close() |
关闭文件 |
flie.fileno() |
返回文件的描述符(file description, FD,整型) |
file.flush() |
刷新文件的内部缓冲区 |
file.isatty() |
判断file是否是一个类tty设备 |
file.next() |
返回文件的下一行(类似于file.readline()),或在没有其他行时引发StopIteration异常 |
file.read(size=-1) |
从文件读取size个字节,当未给定size或给定负值的时候,读取剩余的所有字节,然后作为字符串返回。 |
file.readlines(sizeint=0) |
读取文件的所有行并作为一个列表返回(包括所有的行结束符);如果给定sizeint且大于0,那么将返回总和大于为sizeint字节的行(大小由缓冲器容量的下一个值决定,比如说缓冲区的大小为4K的倍数,如果sizeint为15K,则最后可能是16K) |
file.xreadlines() |
用于迭代,可以替代readlines()的一个更高效的方法 |
file.seek(off, whence=0) |
在文件中移动文件指针,从whence(0代表文件起始,1代表当前位置,2代表文件末尾)偏移off字节 |
file.tell() |
返回文件指针在当前文件中的位置 |
file.truncate(size=file.tell()) |
截取文件到最大size字节,默认是当前文件位置 |
file.write(str) |
向文件写入字符串 |
file.writelines(seq) |
向文件写入字符串序列seq,seq应该是一个返回字符串的可迭代对象 |
文件对象属性
文件对象的属性 |
描述 |
file.closed |
表示文件已经关闭,否则为False |
file.encoding |
文件所使用的编码—当Unicode字符串被写入数据时,它们将自动使用file.encoding转换为字节字符串,若file.encoding为None时使用系统默认编码。 |
file.mode |
Access文件打开时使用的访问模式 |
file.name |
文件名 |
file.newlines |
未读取到行分隔符时为None,只有一个行分隔符时为一个字符串,当文件有多种类型的行结束符时,则为一个包含所有当前所遇到过的行结束符的列表 |
file.softspace |
为0表示在输出一数据后,要加上一个空格符,1表示不加。程序员一般不使用这个属性。 |
os模块的文件/目录访问函数
函数 |
描述 |
文件处理 |
|
mkfifo()/mknod() |
创建命名管道/创建文件系统节点 |
remove()/unlink() |
删除文件 |
rename()/renames() |
重命名文件 |
stat() |
返回文件信息 |
symlink() |
创建符号链接 |
utime() |
更新时间戳 |
tmpfile() |
创建并打开('w+b')一个新的临时文件 |
walk() |
生成一个目录树下的所有文件名 |
目录/文件夹 |
|
chdir()/fchdir() |
改变当前工作目录/通过一个文件描述符改变当前工作目录 |
chroot() |
改变当前进程的根目录 |
listdir() |
列出指定目录的文件 |
getcwd()/getcwdu() |
返回当前工作目录/功能相同,当返回一个Unicode对象 |
mkdir()/makedirs() |
创建目录/创建多层目录 |
mkdir()/removedirs() |
删除目录/删除多层目录 |
访问/权限 |
|
access() |
检验权限模式 |
chmod() |
改变权限模式 |
chown()/lchown() |
改变owner和group ID/功能相同,但不会跟踪链接 |
umask() |
设置默认权限模式 |
文件描述符操作 |
|
open() |
底层的操作系统open(对于文件,使用标准的内建open()函数) |
read()/write() |
根据文件描述符读取/写入数据 |
dup()/dup2() |
复制文件描述符号/功能相同,但是是复制到另一个文件描述符 |
设备号 |
|
makedev() |
从major和mirror设备号创建一个原始设备号 |
major()/minor() |
从原始设备号获取major()/mirror设备号 |
os.path模块中的路径名访问函数
函数 |
描述 |
分隔 |
|
basename() |
去掉目录路径,返回文件名 |
dirname() |
去掉文件名,返回目录路径 |
join() |
将分离的各部分组合成一个路径名 |
split() |
返回(dirname(),basename())元组 |
splitdrive() |
返回(drivename, pathname)元组 |
splittext() |
返回(filename, extension)元组 |
信息 |
|
getatime() |
返回最近访问时间 |
getctime() |
返回文件创建时间 |
getmtime() |
返回最近文件修改时间 |
getsize() |
返回文件大小(以字节为单位) |
查询 |
|
exists() |
指定路径(文件或目录)是否存在 |
isabs() |
指定路径是否是绝对路径 |
isdir() |
指定路径是否存在且为一个目录 |
isfile() |
指定路径是否存在且为一个文件 |
islink() |
指定路径是否存在且为一个符号链接 |
ismount() |
指定路径是否存在且为一个挂载点 |
samefile() |
两个路径名是否指向同一个文件 |