处理文件的第一步是确定要处理的文件的名字.Python将文件名表示为简单的字符串,另外还提供了一些工具,用于由os.path中平台独立的标准组成部分构造文件名.用os中的listdir()可以列出一个目录中的内容,或者使用glob由一个模式建立文件名列表.
glob使用的文件名模式匹配还可以通过fnmatch直接提供,从而可以在其他上下文中使用.
dircache提供了一种高效的方式,能够扫描和处理文件系统中一个目录的内容,如果不能提前知道文件名,在这种情况下处理文件时dircache会很有用.
明确文件名之后,可以用os.stat()和stat中的常量检查其他特性,如权限或文件大小
应用需要随机访问文件时,利用linecache可以很容易的按行号读取行.文件的内容在缓存中维护,所以要当心内存消耗.
有些情况下需要创建草稿文件来临时保存数据,或者将数据移动到一个永久位置之前需要用临时文件存储,此时tempfile就很有用.它提供了一些类,可以安全而稳妥的创建临时文件和目录.可以保证文件名是唯一的,其中包含随机的组成部分,因此不容易猜出.
程序通常需要把文件作为一个整体来处理,而不考虑其内容.shutil模块包含了一些高级文件操作,如复制文件和目录,以及设置权限.
filecmp模块通过查看文件和目录包含的字节来完成文件和目录比较.
内置的file类可以用于读写本地文件系统上可见的文件.不过,通过read()和write()接口访问大文件时,程序的性能可能会受影响,因为文件从磁盘移动到应用可见的内存时会涉及多次数据复制.使用mmap可以告诉操作系统使用其虚拟内存子系统,将文件的内容直接映射到程序可以访问的内存,从而避免在操作系统与file对象内部缓冲区和自建进行复制.
如果文件数据中使用了非ASCII字符,通常会采用一种Unicode数据格式保存.codecs模块会自动处理编码和解码,所以很多情况下,完全可以使用一个非ASCII文件而无需任何其他修改.
如果测试代码依赖于从文件读写数据,对于这些测试代码,StringIO提供了一个内存中流对象,它就像一个文件,不过不驻留在磁盘上.
作用:解析,构建,测试以及处理文件名和路径
使用os.path模块中包含的函数,很容易编写在多个平台上处理文件的代码.即使程序不打算在平台之间移植,也应当使用os.path来完成可靠的文件名解析.
路径解析依赖于os中定义的一些变量:
os.sep---路径各部分之间的分隔符("\"或"/")
os.extsep---文件名与文件"扩展名"之间的分隔符(".")
os.pardir---路径中表示目录树的上一级的部分(例如"..")
os.curdir---路径中指示当前目录的部分(例如".")
>>> os.sep, os.extsep, os.pardir, os.curdir ('\\', '.', '..', '.')split()函数将路径分解为两个单独的部分,并返回包含这些结果的一个tuple.这个tuple的第二个元素是路径的最后一个部分,第一个元素则是最后这个部分之前的所有内容.
import os.path for path in ['/one/two/three', '/one/two/three/', '/', '.', '', ]: print '%15s : %s' % (path, os.path.split(path))解释器显示如下:
>>> /one/two/three : ('/one/two', 'three') /one/two/three/ : ('/one/two/three', '') / : ('/', '') . : ('', '.') : ('', '')basename()函数返回的值等价于split()值的第二部分, dirname()函数返回分解路径得到的第一部分.
import os.path for path in ['/one/two/three', '/one/two/three/', '/', '.', '', ]: print '%15s : %s | %s' % (path, os.path.dirname(path), os.path.basename(path))
解释器显示如下:
>>> /one/two/three : /one/two | three /one/two/three/ : /one/two/three | / : / | . : | . : |splitext()根据扩展名而不是目录分隔符(与split()不同)来分解路径:
import os.path for path in ['filename.txt', 'filename', '/path/to/filename.txt', '/', '', 'my-archive.tar.gz', 'no-extension.', ]: print '%21s :' % path, os.path.splitext(path)解释器显示如下:
>>> filename.txt : ('filename', '.txt') filename : ('filename', '') /path/to/filename.txt : ('/path/to/filename', '.txt') / : ('/', '') : ('', '') my-archive.tar.gz : ('my-archive.tar', '.gz') no-extension. : ('no-extension', '.')commonprefix()取一个路径列表作为参数,将返回一个字符串,表示所有路径中都出现的公共前缀.这个值可能表示一个根本不存在的路径,而且也不考虑路径分隔符,所以这个前缀可能并不落在一个分隔符边界上:
import os.path paths = ['/one/two/three/four', '/one/two/threefold', '/one/two/three/', ] for path in paths: print 'PATH:', path print print 'PREFIX:', os.path.commonprefix(paths)解释器显示如下:
>>> PATH: /one/two/three/four PATH: /one/two/threefold PATH: /one/two/three/ PREFIX: /one/two/three
通过join()函数来建立路径:
import os.path for parts in [('one', 'two', 'three'), ('/', 'one', 'two', 'three'), ('/one', '/two', '/three'), ]: print parts, ":", os.path.join(*parts)如果要连接的某个参数以os.sep开头,前面的所有参数都会丢弃,这个新参数会成为返回值的开始部分:
>>> ('one', 'two', 'three') : one\two\three ('/', 'one', 'two', 'three') : /one\two\three ('/one', '/two', '/three') : /three我们还可以处理包含"可变"部分的路径,这些"可变"部分可以自动扩展.如 expanduser()可以将波浪线(~)字符转换为用户主目录名:
import os.path for user in ['', 'dhellmann', 'postgresql']: lookup = '~' + user print '%12s : %s' % (lookup, os.path.expanduser(lookup))解释器显示如下:
>>> ~ : C:\Users\fzyz_sb ~dhellmann : C:\Users\dhellmann ~postgresql : C:\Users\postgresqlexpandvars()更为通用,它会扩展路径中出现的所有shell环境变量:
>>> import os >>> import os.path >>> os.environ['MYVAR'] = 'VALUE' >>> os.path.expandvars('path/to/$MYVAR') 'path/to/VALUE'
使用join()或利用嵌入变量由单独的字符串组合路径时,得到的路径最后可能会有多余的分隔符或相对路径部分.使用normpath()可以清除这些内容.
import os.path for path in ['one//two//three', 'one/./two/./three', 'one/../alt/two/three', ]: print '%20s : %s' % (path, os.path.normpath(path))解释器显示如下:
>>> one//two//three : one\two\three one/./two/./three : one\two\three one/../alt/two/three : alt\two\three
备注:
1. 注意"/../"直接被删除..
2. 以下的代码运行环境由win8改为ubuntu系统。
要把一个相对路径转换为一个绝对文件名,可以使用abspath().
import os import os.path os.chdir('/tmp') for path in ['.', '..', './one/two/three', '../one/two/three', ]: print '%17s : %s' % (path, os.path.abspath(path))解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py . : /tmp .. : / ./one/two/three : /tmp/one/two/three ../one/two/three : /one/two/three
除了处理路径,os.path还包括一些用来获取文件属性的函数,类似于os.stat()返回的结果:
import os import time import os.path print 'File :', __file__ print 'Access time :', time.ctime(os.path.getatime(__file__)) print 'Modified time :', time.ctime(os.path.getmtime(__file__)) print 'Change time :', time.ctime(os.path.getctime(__file__)) print 'Size :', os.path.getsize(__file__)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py File : test.py Access time : Thu Mar 5 14:47:37 2015 Modified time : Thu Mar 5 14:47:35 2015 Change time : Thu Mar 5 14:47:35 2015 Size : 295
程序玉道一个路径名时,通常需要知道这个路径是指示一个文件,目录还是一个符号链接,另外还要知道它是否确实存在。os.path包含了一些函数用来测试所有这些条件:
import os FILENAMES = [__file__, os.path.dirname(__file__), '/', './broken_link', ] for file in FILENAMES: print 'File :', file print 'Absolute :', os.path.isabs(file) print 'Is File? :', os.path.isfile(file) print 'Is Dir? :', os.path.isdir(file) print 'Is Link :', os.path.islink(file) print 'Mountpoint? :', os.path.ismount(file) print 'Exists? :', os.path.exists(file) print 'Link Exists? :', os.path.lexists(file) print解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py File : test.py Absolute : False Is File? : True Is Dir? : False Is Link : False Mountpoint? : False Exists? : True Link Exists? : True File : Absolute : False Is File? : False Is Dir? : False Is Link : False Mountpoint? : False Exists? : False Link Exists? : False File : / Absolute : True Is File? : False Is Dir? : True Is Link : False Mountpoint? : True Exists? : True Link Exists? : True File : ./broken_link Absolute : False Is File? : False Is Dir? : False Is Link : False Mountpoint? : False Exists? : False Link Exists? : False
os.path.walk()会遍历一个树中的所有目录,病调用所提供的一个函数,将目录名和该目录中的各内容的名字作为参数传入该函数:
import os import os.path import pprint def visit(arg, dirname, names): print dirname, arg for name in names: subname = os.path.join(dirname, name) if os.path.isdir(subname): print ' %s/' % name else: print ' %s' % name print if not os.path.exists('example'): os.mkdir('example') if not os.path.exists('example/one'): os.mkdir('example/one') with open('example/one/file.txt', 'wt') as f: f.write('contents') with open('example/two.txt', 'wt') as f: f.write('contents') os.path.walk('example', visit, '(User data)')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py example (User data) two.txt one/ example/one (User data) file.txt
作用:使用UNIX shell规则查找与一个模式匹配的文件名
使用以下代码创建示例文件:
import os import os.path if not os.path.exists('dir'): os.mkdir('dir') fileArr = ['file.txt', 'file1.txt', 'file2.txt', 'filea.txt', 'fileb.txt'] for fileName in fileArr: fName = os.path.join('dir', fileName) open(fName, 'wt') if not os.path.exists('dir/subdir'): os.mkdir('dir/subdir') fName = os.path.join('dir/subdir', 'subfile.txt') open(fName, 'wt')运行后,会生成如下的文件目录:
leichaojian@leichaojian-ThinkPad-T430:~$ ll dir total 12 drwxrwxr-x 3 leichaojian leichaojian 4096 3月 5 15:15 ./ drwxr-xr-x 23 leichaojian leichaojian 4096 3月 5 15:15 ../ -rw-rw-r-- 1 leichaojian leichaojian 0 3月 5 15:15 file1.txt -rw-rw-r-- 1 leichaojian leichaojian 0 3月 5 15:15 file2.txt -rw-rw-r-- 1 leichaojian leichaojian 0 3月 5 15:15 filea.txt -rw-rw-r-- 1 leichaojian leichaojian 0 3月 5 15:15 fileb.txt -rw-rw-r-- 1 leichaojian leichaojian 0 3月 5 15:15 file.txt drwxrwxr-x 2 leichaojian leichaojian 4096 3月 5 15:15 subdir/ leichaojian@leichaojian-ThinkPad-T430:~$ ll dir/subdir total 8 drwxrwxr-x 2 leichaojian leichaojian 4096 3月 5 15:15 ./ drwxrwxr-x 3 leichaojian leichaojian 4096 3月 5 15:15 ../ -rw-rw-r-- 1 leichaojian leichaojian 0 3月 5 15:15 subfile.txt
星号(*)匹配一个文件名段中的0个或多个字符:
import glob for name in glob.glob('dir/*'): print name解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py dir/filea.txt dir/file.txt dir/file2.txt dir/subdir dir/fileb.txt dir/file1.txt同理,如果想显示子目录,则需要包含具体的路径:
>>> import glob >>> for name in glob.glob('dir/*/*'): ... print name, ... dir/subdir/subfile.txt
问号(?)也是一个通配符,它会匹配文件名中该位置的单个字符:
>>> import glob >>> for name in glob.glob('dir/file?.txt'): ... print name ... dir/filea.txt dir/file2.txt dir/fileb.txt dir/file1.txt
使用[a-z]的区间可以匹配多个字符:
>>> import glob >>> for name in glob.glob('dir/*[0-9].*'): ... print name ... dir/file2.txt dir/file1.txt
作用: 从文件或导入的Python模块获取文本行,维护一个结果缓存,从而可以更高高效的从相同文件读取多行文本。
linecache_data.py:
import os import tempfile lorem = """Lorem ipsum dolor sit amet, consecteture adipiscing elit. Vivamus eget edit. In posuere mi non risus. Mauris id quam posuere lectus sollicitudin varius. Praesent at mi. Nunc eu velit. Sed augue massa, fermentum id, nonummy a, nonummy sit amet, ligula. Curabitur eros pede, egestas at, ultricies ac, apellentesque eu, tellus. Sed sed odio sed mi luctus mollis. Integer et nulla ac augue convallis accumsan. Ut felis. Donec lectus sapien, elementum nec, condimentum ac, interdum non, tellus. Aenean viverra, mauris vehicula semper porttitor, ipsum odio consectetuer lorem, ac imperdiet eros odio a sapien. Nulla mauris tellus, aliquam non, egestas a, nonummy et, erat. Vivamus sagittis porttitor eros.""" def make_tempfile(): fd, temp_file_name = tempfile.mkstemp() os.close(fd) f = open(temp_file_name, 'wt') try: f.write(lorem) finally: f.close() return temp_file_name def cleanup(filename): os.unlink(filename)
linecache模块读取的文件行号从1开始:
import linecache from linecache_data import * filename = make_tempfile() print 'SOURCE:' print '%r' % lorem.split('\n')[4] print print 'CACHE:' print '%r' % linecache.getline(filename, 5)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py SOURCE: 'fermentum id, nonummy a, nonummy sit amet, ligula. Curabitur' CACHE: 'fermentum id, nonummy a, nonummy sit amet, ligula. Curabitur\n'
返回值通常在行末尾都包含一个换行符,所以如果文本行为空,那么返回值就是一个换行符:
import linecache from linecache_data import * filename = make_tempfile() print 'BLANK: %r' % linecache.getline(filename, 8) cleanup(filename)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BLANK: '\n'
如果所请求的行号超出文件中的合法行号范围,或者读取一个不存在的文件,getline()会返回一个空串。
import linecache from linecache_data import * filename = make_tempfile() not_there = linecache.getline(filename, 500) print 'NOT THERE: %r includes %d characters' % (not_there, len(not_there)) no_such_file = linecache.getline('this_file_does_not_exist.txt', 1) print 'NO FILE: %r' % no_such_file cleanup(filename)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py NOT THERE: '' includes 0 characters NO FILE: ''
由于linecache在生成traceback跟踪记录时使用相当频繁,其关键特性之一就是能够通过指定模块的基名在导入路径中查找Python源模块。
import linecache import os module_line = linecache.getline('linecache.py', 3) print 'MODULE:' print repr(module_line) file_src = linecache.__file__ if file_src.endswith('.pyc'): file_src = file_src[:-1] print '\nFILE:' with open(file_src, 'r') as f: file_line = f.readlines()[2] print repr(file_line)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py MODULE: 'This is intended to read lines from modules imported -- hence if a filename\n' FILE: 'This is intended to read lines from modules imported -- hence if a filename\n'
作用:创建临时文件系统对象
要想安全的创建具有唯一名称的临时文件,以防止被试图破坏应用或窃取数据的人猜出,这并不容易。tempfile模块提供了多个函数来安全的创建临时文件兄台哪个资源。TemporaryFile()打开并返回一个未命名的文件,NamedTemporaryFile()打开并返回一个命名文件,mkdtemp()会创建一个临时目录,并返回其目录名。
使用TemporaryFile()来创建临时文件,不论通过调用close()还是结合使用上下文管理器API和with语句来关闭文件,文件都会关闭时自动删除。
import os import tempfile filename = '/tmp/guess_my_name.%s.txt' % os.getpid() temp = open(filename, 'w+b') try: print 'temp:', temp print 'temp.name:', temp.name finally: temp.close() os.remove(filename) print print 'TemporaryFile:' temp = tempfile.TemporaryFile() try: print 'temp:', temp print 'temp.name:', temp.name finally: temp.close()解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py temp: <open file '/tmp/guess_my_name.3668.txt', mode 'w+b' at 0x7f897f36b540> temp.name: /tmp/guess_my_name.3668.txt TemporaryFile: temp: <open file '<fdopen>', mode 'w+b' at 0x7f897f36bae0> temp.name: <fdopen>备注:使用TemporaryFile()返回的文件没有文件名。
NamedTemporaryFile()函数会创建一个文件,但不会断开其链接,所以会保留文件名(用name属性访问)。
import os import tempfile with tempfile.NamedTemporaryFile() as temp: print 'temp:' print ' ', temp print 'temp.name:' print ' ', temp.name print 'Exists after close:', os.path.exists(temp.name)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py temp: <open file '<fdopen>', mode 'w+b' at 0x7fdb707415d0> temp.name: /tmp/tmp6xQyNO Exists after close: False
需要多个临时文件时,可能更方便的做法是用mkdtemp()创建一个临时目录,病打开目录中的所有文件。
import os import tempfile directory_name = tempfile.mkdtemp() print directory_name os.removedirs(directory_name)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py /tmp/tmpugNdZw
文件名使用以下公式生成:
dir + prefix + random + suffix
所以,dir,prefix和suffix可以传递给TemporaryFile(),NamedTemporaryFile()和mkdtemp().
import os import tempfile with tempfile.NamedTemporaryFile( suffix = '_suffix', prefix = 'prefix_', dir = '/tmp', ) as temp: print 'temp:' print ' ', temp print 'temp.name:' print ' ', temp.name解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py temp: <open file '<fdopen>', mode 'w+b' at 0x7f0589399540> temp.name: /tmp/prefix_Yq_bjJ_suffix
如果没有使用dir参数指定明确的目标位置,临时文件的路径会有所改变:
import os import tempfile print 'gettempdir():', tempfile.gettempdir() print 'gettempprefix():', tempfile.gettempprefix()解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py gettempdir(): /tmp gettempprefix(): tmp
作用:高级文件操作
copyfile()将源的内容复制到目标,如果美柚权限写目标文件则产生IOError:
from shutil import * from glob import glob print 'BEFORE:', glob('test.*') copyfile('test.py', 'test.py.copy') print 'AFTER:', glob('test.*')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BEFORE: ['test.py~', 'test.py'] AFTER: ['test.py~', 'test.py', 'test.py.copy']copyfile()的实现使用了底层函数copyfileobj(),其第三个参数是读入块的一个缓冲区长度:
from shutil import * import os from StringIO import StringIO import sys class VerboseStringIO(StringIO): def read(self, n = -1): next = StringIO.read(self, n) print 'read(%d) bytes' % n return next lorem_ipsum = """ajfioajf fjiafh fjeaoi hfewkjfiuahf fjoiejf fajid af fjeoi fasf fjaeifa fjeoiwafj afiew faof""" print 'Default:' input = VerboseStringIO(lorem_ipsum) output = StringIO() copyfileobj(input, output) print print 'All at once:' input = VerboseStringIO(lorem_ipsum) output = StringIO() copyfileobj(input, output, -1) print print 'Blocks of 256:' input = VerboseStringIO(lorem_ipsum) output = StringIO() copyfileobj(input, output, 256)默认行为是使用大数据读取。使用-1会一次读入所有输入。
解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Default: read(16384) bytes read(16384) bytes All at once: read(-1) bytes read(-1) bytes Blocks of 256: read(256) bytes read(256) bytes
copy()函数类似UNIX的命令行cp,会以同样的方式解释输出名。如果指定的目标指示一个目录而不是一个文件,会使用源文件的基名在该目录中创建一个新文件:
from shutil import * import os os.mkdir('example') print 'BEFORE:', os.listdir('example') copy('test.py', 'example') print 'AFTER:', os.listdir('example')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BEFORE: [] AFTER: ['test.py']copy2()类似于copy,但是复制到新文件的元数据中会包含访问和修改时间:
先创建test2.py文件,并删除文件目录example:
leichaojian@leichaojian-ThinkPad-T430:~$ touch test2.py leichaojian@leichaojian-ThinkPad-T430:~$ ll test2.py -rw-rw-r-- 1 leichaojian leichaojian 0 3月 6 09:18 test2.py leichaojian@leichaojian-ThinkPad-T430:~$ rm -rf example测试用例如下:
from shutil import * import os import time def show_file_info(filename): stat_info = os.stat(filename) print '\tMode :', stat_info.st_mode print '\tCreated:', time.ctime(stat_info.st_ctime) print '\tAccessed:', time.ctime(stat_info.st_atime) print '\tModified:', time.ctime(stat_info.st_mtime) os.mkdir('example') print 'SOURCE:' show_file_info('test2.py') copy2('test2.py', 'example') print 'DEST:' show_file_info('example/test2.py')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py SOURCE: Mode : 33204 Created: Fri Mar 6 09:18:41 2015 Accessed: Fri Mar 6 09:18:41 2015 Modified: Fri Mar 6 09:18:41 2015 DEST: Mode : 33204 Created: Fri Mar 6 09:23:13 2015 Accessed: Fri Mar 6 09:23:13 2015 Modified: Fri Mar 6 09:18:41 2015
默认的,在UNIX下创建一个新文件时,它会根据当前用户的umask接受权限。要把权限从一个文件复制到另一个文件,可以使用copymode():
首先,我们得查看以下用于测试的文件的权限:
leichaojian@leichaojian-ThinkPad-T430:~$ ll test.py -rw-rw-r-- 1 leichaojian leichaojian 305 3月 6 09:28 test.py leichaojian@leichaojian-ThinkPad-T430:~$ touch file_to_change.txt leichaojian@leichaojian-ThinkPad-T430:~$ chmod ugo+w file_to_change.txt leichaojian@leichaojian-ThinkPad-T430:~$ ll file_to_change.txt -rw-rw-rw- 1 leichaojian leichaojian 0 3月 6 09:28 file_to_change.txt测试用例如下:
from shutil import * import os from commands import * with open('file_to_change.txt', 'wt') as f: f.write('content') os.chmod('file_to_change.txt', 0444) print 'BEFORE:' print getstatus('file_to_change.txt') copymode('test.py', 'file_to_change.txt') print 'AFTER:' print getstatus('file_to_change.txt')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BEFORE: -r--r--r-- 1 leichaojian leichaojian 7 3月 6 09:30 file_to_change.txt AFTER: -rw-rw-r-- 1 leichaojian leichaojian 7 3月 6 09:30 file_to_change.txt备注:注意权限的更改
我们也可以使用copystat()来复制其状态:
from shutil import * import os import time def show_file_info(filename): stat_info = os.stat(filename) print '\tMode :', stat_info.st_mode print '\tCreated :', time.ctime(stat_info.st_ctime) print '\tAccessed :', time.ctime(stat_info.st_atime) print '\tModified :', time.ctime(stat_info.st_mtime) with open('file_to_change.txt', 'wt') as f: f.write('content') os.chmod('file_to_change.txt', 0444) print 'BEFORE:' show_file_info('file_to_change.txt') copystat('test.py', 'file_to_change.txt') print 'ALTER:' show_file_info('file_to_change.txt')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BEFORE: Mode : 33060 Created : Fri Mar 6 09:47:23 2015 Accessed : Fri Mar 6 09:28:51 2015 Modified : Fri Mar 6 09:47:23 2015 ALTER: Mode : 33204 Created : Fri Mar 6 09:47:23 2015 Accessed : Fri Mar 6 09:47:23 2015 Modified : Fri Mar 6 09:46:53 2015
要把一个目录从一个位置复制到另一个位置,可以使用copytree().这会递归遍历源目录树,将文件复制到目标。目标目录不能已存在。
from shutil import * from commands import * print 'BEFORE:' print getoutput('ls -rlast /tmp/example') copytree('/usr/lib/python2.7/sqlite3', '/tmp/example') print '\nAFTER:' print getoutput('ls -rlast /tmp/example')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BEFORE: ls: cannot access /tmp/example: No such file or directory AFTER: total 20 4 -rw-r--r-- 1 leichaojian leichaojian 1037 3月 23 2014 __init__.py 4 -rw-r--r-- 1 leichaojian leichaojian 2804 3月 23 2014 dump.py 4 -rw-r--r-- 1 leichaojian leichaojian 2644 3月 23 2014 dbapi2.py 4 drwxr-xr-x 2 leichaojian leichaojian 4096 7月 23 2014 . 4 drwxrwxrwt 6 root root 4096 3月 6 10:00 ..而删除一个目录及其中的内容,可以rmtree():
from shutil import * from commands import * print 'BEFORE:' print getoutput('ls -rlast /tmp/example') rmtree('/tmp/example') print '\nAFTER:' print getoutput('ls -rlast /tmp/example')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BEFORE: total 20 4 -rw-r--r-- 1 leichaojian leichaojian 1037 3月 23 2014 __init__.py 4 -rw-r--r-- 1 leichaojian leichaojian 2804 3月 23 2014 dump.py 4 -rw-r--r-- 1 leichaojian leichaojian 2644 3月 23 2014 dbapi2.py 4 drwxr-xr-x 2 leichaojian leichaojian 4096 7月 23 2014 . 4 drwxrwxrwt 6 root root 4096 3月 6 10:00 .. AFTER: ls: cannot access /tmp/example: No such file or directory要把一个文件或目录从一个位置移动到另一个位置,可以使用move():
from shutil import * from glob import glob with open('example.txt', 'wt') as f: f.write('contents') print 'BEFORE:', glob('example*') move('example.txt', 'example.out') print 'AFTER:', glob('example*')解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py BEFORE: ['examples.desktop', 'example', 'example.txt'] AFTER: ['example.out', 'examples.desktop', 'example']
作用:建立内存映射文件而不是直接读取内容
建立一个文件的内存映射将使用操作系统虚拟内存系统直接访问文件系统中的数据,而不是使用常规的I/O函数。内存映射通常可以提高I/O性能,因为使用内存映射时,不会对每一个访问都有一个单独的系统调用,而且不需要在缓冲区之间复制数据---内核和用户应用都会直接访问内存。
内存映射文件可以看做是可修改的字符串或类文件对象,这取决于具体的需要。映射文件支持一般的文件API方法,如close(),flush(),read(),readline(),seek(),tell()和write().它还支持字符串API,提供分片等特性以及类似find()的方法。
示例文件所使用的文本lorem.txt如下:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec egestas, enim et consectetuer ullamcorper, lectus ligula retrum leo, a elementum elit tortor eu quam. Duis tincidunt nisi ut ante. Nulla facilisi. Sed tristique eros eu libero. Pellentesque vel arcu. Vivamus purus orci, iaculis ac, suscipit sit amet, pulvinar eu, lacus. Praesent placerat tortor sed nisl. Nunc blandit diam egestas dui. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam viverra fringilla leo. Nulls feugiat augue eleifend nulla. Vivamus mauris. Vivamus sed mauris in nibh placerat egestas. Suspendisse potenti. Mauris massa. Ut eget velit auctor tortor blandit sollicitudin. Suspendisse imperdiet justo.
使用mmap()函数可以创建一个内存映射文件。第一个参数是文件描述符,可以来自file对象的fileno()方法,或者来自os.open().调用者在调用mmap()之前负责打开文件,不再需要文件时要负责将其关闭。
mmap()的第二个参数是映射的文件部分的大小(以字节为单位)。如果这个值为0,则映射整个文件,如果大小大于文件的当前大小,则会扩展该文件:
可选参数为权限:ACCESS_READ,ACCESS_WRITE,ACCESS_COPY:
import mmap import contextlib with open('lorem.txt', 'r') as f: with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)) as m: print '0~10 read:', m.read(10) print '0~20 read:', m[:20] print '10~20 read:', m.read(10)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py 0~10 read: Lorem ipsu 0~20 read: Lorem ipsum dolor si 10~20 read: m dolor si备注:切片不会导致文件指针的偏移
要建立内存映射文件来接收更新,映射之前首先要使用'r+'打开文件以完成追加。而mmod默认的访问方式是ACCESS_WRITE:
import mmap import contextlib import shutil shutil.copyfile('lorem.txt', 'lorem_copy.txt') word = 'consectetuer' reversed = word[::-1] print 'Looking for :', word print 'Replacing with:', reversed with open('lorem_copy.txt', 'r+') as f: with contextlib.closing(mmap.mmap(f.fileno(), 0)) as m: print 'Before:' print m.readline().rstrip() m.seek(0) loc = m.find(word) m[loc:loc + len(word)] = reversed m.flush() m.seek(0) print 'After:' print m.readline().rstrip() f.seek(0) print 'File:' print f.readline().rstrip()解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Looking for : consectetuer Replacing with: reutetcesnoc Before: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec After: Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit. Donec File: Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit. Donec复制模式:
使用访问设置ACCESS_COPY时不会将修改写入磁盘的文件:
import mmap import contextlib import shutil shutil.copyfile('lorem.txt', 'lorem_copy.txt') word = 'consectetuer' reversed = word[::-1] with open('lorem_copy.txt', 'r+') as f: with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY)) as m: print 'Memory Before:' print m.readline().rstrip() print 'File Before:' print f.readline().rstrip() m.seek(0) loc = m.find(word) m[loc:loc + len(word)] = reversed m.flush() m.seek(0) print 'Memory After:' print m.readline().rstrip() f.seek(0) print 'File After:' print f.readline().rstrip()解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Memory Before: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec File Before: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec Memory After: Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit. Donec File After: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec
作用:编码器和解码器,用于转换文本的不同表示
codecs模块提供了流接口和文件接口来转换数据。通常用于处理Unicode文本,不过也提供了其他编码来满足其他用途。
输出unicode串时,会使用某种标准机制编码,使得以后可以将这个字节序列重构为同样的文本串。编码值的字节不一定与码点值完全相同,编码只是定义了在两个值集之间转换的一种方式。读取Unicode数据时还需要编码,这样才能把接收到的字节转换为unicode类使用的内部表示:
示例为格式化字符串:codecs_to_hex.py
import binascii def to_hex(t, nbytes): chars_per_item = nbytes * 2 hex_version = binascii.hexlify(t) return ' '.join( hex_version[start:start + chars_per_item] for start in xrange(0, len(hex_version), chars_per_item)) if __name__ == '__main__': print to_hex('abcdef', 1) print to_hex('abcdef', 2)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python codecs_to_hex.py 61 62 63 64 65 66 6162 6364 6566
我们使用unicode来打印文本pi:π,并将其进行UTF-8,UTF-16的编码:
备注: 以下代码测试均在window8系统下,64位.
from codecs_to_hex import to_hex text = u'pi:π' print 'Raw :', repr(text) print 'UTF-8 :', to_hex(text.encode('utf-8'), 1) print 'UTF-16 :', to_hex(text.encode('utf-16'), 2)解释器显示如下:
>>> Raw : u'pi:\xa6\xd0' UTF-8 : 70 69 3a c2 a6 c3 90 UTF-16 : fffe 7000 6900 3a00 a600 d000给定一个编码字节序列(作为一个str实例),decode()方法会将其转换为码点,并作为一个unicode实例返回转换后的序列:
from codecs_to_hex import to_hex text = u'pi:π' encoded = text.encode('utf-8') decoded = encoded.decode('utf-8') print 'Original :', repr(text) print 'Encoded :', to_hex(encoded, 1), type(encoded) print 'Decoded :', repr(decoded), type(decoded)解释器显示如下:
>>> Original : u'pi:\xa6\xd0' Encoded : 70 69 3a c2 a6 c3 90 <type 'str'> Decoded : u'pi:\xa6\xd0' <type 'unicode'>
处理I/O操作时,编码和解码字符串尤其重要.不论是写至一个文件,一个套接字还是另一个流,数据都必须进行适当的编码.
codecs提供了最简单的接口可以用来代替内置open()函数,额外增加了两个参数来指定编码和所需的错误处理技术:
from codecs_to_hex import to_hex import codecs import sys encodes = ['utf-8', 'utf-16', 'utf-32'] for encoding in encodes: filename = encoding + '.txt' print 'Writting to ', filename with codecs.open(filename, mode = 'w', encoding=encoding) as f: f.write(u'pi:\xa6\xd0') nbytes = {'utf-8' : 1, 'utf-16' : 2, 'utf-32' : 4}.get(encoding, 1) print 'File contents:' with open(filename, mode='rt') as f: print to_hex(f.read(), nbytes)解释器显示如下:
>>> Writting to utf-8.txt File contents: 70 69 3a c2 a6 c3 90 Writting to utf-16.txt File contents: fffe 7000 6900 3a00 a600 d000 Writting to utf-32.txt File contents: fffe0000 70000000 69000000 3a000000 a6000000 d0000000
在不同计算机系统之间传输数据时,多字节编码(如UTF-16和UTF-32)会引发一个问题.不同系统中使用的高字节和低字节的顺序不同.数据的这个特性,称为字节序(endianness),它依赖于硬件体系结构等因素,还取决于操作系统和应用开发人员作出的选择.
在UTF-16的世界里,0xFFFE和0xFEFF不是合法字符,而是用来指示字节序.
import codecs from codecs_to_hex import to_hex for name in ['BOM', 'BOM_BE', 'BOM_LE', 'BOM_UTF8', 'BOM_UTF16', 'BOM_UTF16_BE', 'BOM_UTF16_LE', 'BOM_UTF32', 'BOM_UTF32_BE', 'BOM_UTF32_LE', ]: print '{:12} : {}'.format(name, to_hex(getattr(codecs, name), 2))解释器显示如下:
>>> BOM : fffe BOM_BE : feff BOM_LE : fffe BOM_UTF8 : efbb bf BOM_UTF16 : fffe BOM_UTF16_BE : feff BOM_UTF16_LE : fffe BOM_UTF32 : fffe 0000 BOM_UTF32_BE : 0000 feff BOM_UTF32_LE : fffe 0000可以由codecs中的解码器自动检测和处理字节序,不过编码时也可以显式的指定字节序:
import codecs from codecs_to_hex import to_hex if codecs.BOM_UTF16 == codecs.BOM_UTF16_BE: bom = codecs.BOM_UTF16_LE encoding = 'utf_16_le' else: bom = codecs.BOM_UTF16_BE encoding = 'utf_16_be' print 'Native order :', to_hex(codecs.BOM_UTF16, 2) print 'Selected order :', to_hex(bom, 2) encoded_text = u'pi:\u03c0'.encode(encoding) print '{:14} : {}'.format(encoding, to_hex(encoded_text, 2)) with open('nonnative-encoded.txt', mode='wb') as f: f.write(bom) f.write(encoded_text) with open('nonnative-encoded.txt', mode='rb') as f: raw_bytes = f.read() print 'Raw :', to_hex(raw_bytes, 2) with codecs.open('nonnative-encoded.txt', mode='r', encoding='utf-16', ) as f: decoded_text = f.read() print 'Decoded:', repr(decoded_text)
解释器显示如下:
>>> Native order : fffe Selected order : feff utf_16_be : 0070 0069 003a 03c0 Raw : feff 0070 0069 003a 03c0 Decoded: u'pi:\u03c0'
正确的设置编码很重要,有以下原因:如果读文件时未能正确的配置编码,就无法正确的解释数据,数据则有可能被破坏或者无法解码.并不是所有Unicode字符都可以采用所有编码表示,所以如果写文件时使用了错误的编码,就会生成一个错误,数据也可能丢失.
codecs也使用了同样的5个错误处理选项:
错误模式 |
描述 |
strict |
如果数据无法转换,产生一个异常 |
replace |
将无法编码的数据替换为一个特殊的标志字符 |
ignore |
跳过数据 |
xmlcharrefreplace |
XML字符(仅适用编码) |
backslashreplace |
转义序列(仅适用编码) |
最常用的错误条件是向一个ASCII输出流(如一个常规文件或sys.stdout)写Unicode数据时候接收到一个UnicodeEncodeError.
import codecs import sys errors = ['strict', 'replace', 'ignore', 'xmlcharrefreplace', 'backslashreplace'] text = u'pi: \u03c0' for error_handling in errors: try: with codecs.open('encode_error.txt', 'w', encoding='ascii', errors=error_handling) as f: f.write(text) except UnicodeEncodeError, err: print 'ERROR:', err else: with open('encode_error.txt', 'rb') as f: print 'File contents:', repr(f.read())strict:最安全,但是产生异常可能会导致程序崩溃
replace: 确保不会产生任何错误,其代价是可能会丢失一些无法转换为所需编码的数据
ignore: 无法编码的数据会被丢弃
xmlcharrefreplace: 使用XML字符引用来完成替换
backslashreplace: 类似于unicode来打印
解释器显示如下:
>>> ERROR: 'ascii' codec can't encode character u'\u03c0' in position 4: ordinal not in range(128) File contents: 'pi: ?' File contents: 'pi: ' File contents: 'pi: π' File contents: 'pi: \\u03c0'
数据解码时也有可能遇到错误,特别是在使用了错误的编码时:
import codecs import sys from codecs_to_hex import to_hex errors = ['strict', 'replace', 'ignore'] text = u'pi: \u03c0' print 'Original :', repr(text) with codecs.open('decode_error.txt', 'w', encoding='utf-16') as f: f.write(text) with open('decode_error.txt', 'rb') as f: print 'File contents:', to_hex(f.read(), 1) for error_handling in errors: with codecs.open('decode_error.txt', 'r', encoding='utf-8', errors=error_handling) as f: try: data = f.read() except UnicodeDecodeError, err: print 'ERROR:', err else: print 'Read :', repr(data)strict: 如果不能正确的解码字节流,strict错误处理模式会产生一个异常.在这种情况下,产生UnicodeDecodeError的原因是尝试将UTF-16 BOM的部分转换为一个使用UTF-8解码器的字符.
ignore:导致解码器跳过不合法的字节
replace: 非法的字节会替换为\uFFFD
解释器显示如下:
>>> Original : u'pi: \u03c0' File contents: ff fe 70 00 69 00 3a 00 20 00 c0 03 ERROR: 'utf8' codec can't decode byte 0xff in position 0: invalid start byte Read : u'\ufffd\ufffdp\x00i\x00:\x00 \x00\ufffd\x03' Read : u'p\x00i\x00:\x00 \x00\x03'
之所以会产生UnicodeEncodeError异常,最常见的原因是代码中视图将unicode数据打印到控制台或一个UNIX管道,而sys.stdout未配置一个编码:
import codecs import sys text = u'pi: π' print 'Default encoding:', sys.stdout.encoding print 'TTY:', sys.stdout.isatty() print text这里采取unicode会产生一个错误!
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py File "test.py", line 4 SyntaxError: Non-ASCII character '\xcf' in file test.py on line 4, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details备注: 本章unicode的代码无论在win8/64位系统,还是在ubuntu/64位系统上,都存在运行错误的情况。所以剩余的章节直接略过。
作用:使用一个类文件API处理文本缓冲区
StringIO提供了一种方便的做法,可以使用文件API(read(),write()等等)处理内存中的文本。
try: from cStringIO import StringIO except: from StringIO import StringIO output = StringIO() output.write('This goes into the buffer.') print >>output, 'And so does this.' print output.getvalue() output.close() input = StringIO('Inital value for read buffer') print input.read()解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py This goes into the buffer.And so does this. Inital value for read buffer
作用: 处理UNIX式文件名比较
fnmatch()根据一个模式来比较一个文件名,并返回一个布尔值,指示二者是否匹配。如果操作系统使用一个区分大小写的文件系统,这个比较则是区分大小写的:
import fnmatch import os pattern = '*.py' print 'Pattern:', pattern print files = os.listdir('.') for name in files: if fnmatch.fnmatch(name, pattern): print 'Filename: %-25s' % name解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Pattern: *.py Filename: codecs_to_hex.py Filename: test.py Filename: test2.py Filename: linecache_data.py
要测试一个文件名序列,可以使用filter(),它会返回与模式参数匹配的文件名列表:
import fnmatch import os pattern = '*.py' print 'Pattern:', pattern print files = os.listdir('.') print fnmatch.filter(files, pattern)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Pattern: *.py ['codecs_to_hex.py', 'test.py', 'test2.py', 'linecache_data.py']
在内部,fnmatch将glob模式转换为一个正则表达式,并使用re模块来比较文件名和模式。translate()函数就是将glob模式转换为正则表达式的公共API:
import fnmatch pattern = '*.py' print 'Pattern :', pattern print 'Regex :', fnmatch.translate(pattern)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Pattern : *.py Regex : .*\.py\Z(?ms)
作用: 缓存目录列表,目录的修改时间改变时会更新
dircache模块从文件系统读取目录列表,并保存在内存中。
dircache API中的主要函数是listdir(),这是os.listdir()的一个包装器。给定一个路径,每次调用dircache.listdir()时,这个函数会返回同样的list对象,除非目录的修改日期有改变。
用于测试的example目录:
leichaojian@leichaojian-ThinkPad-T430:~/example$ ll total 8 drwxrwxr-x 2 leichaojian leichaojian 4096 3月 6 16:01 ./ drwxr-xr-x 23 leichaojian leichaojian 4096 3月 6 15:55 ../ -rw-rw-r-- 1 leichaojian leichaojian 0 3月 6 16:01 1.py -rw-rw-r-- 1 leichaojian leichaojian 0 3月 6 16:01 2.py -rw-rw-r-- 1 leichaojian leichaojian 0 3月 6 16:01 3.py -rw-rw-r-- 1 leichaojian leichaojian 0 3月 6 09:18 test2.py实例代码如下:
import dircache path = './example' first = dircache.listdir(path) second = dircache.listdir(path) print 'Contents:' for name in first: print ' ', name print print 'Identical:', first is second print 'Equal :', first == second解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Contents: 1.py 2.py 3.py test2.py Identical: True Equal : True
dircache模块还提供了另一个有趣的函数:annotate(),它会修改list(),向表示目录的名称末尾增加一个“/”:
import dircache from pprint import pprint import os path = '../..' contents = dircache.listdir(path) annotated = contents[:] dircache.annotate(path, annotated) fmt = '%25s\t%25s' print fmt % ('ORIGINAL', 'ANNOTATED') print fmt % (('-' * 25,) * 2) for o, a in zip(contents, annotated): print fmt % (o, a)解释器部分显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py ORIGINAL ANNOTATED ------------------------- ------------------------- bin bin/ boot boot/ cdrom cdrom/ dev dev/ etc etc/ home home/ initrd.img initrd.img initrd.img.old initrd.img.old lib lib/
作用:比较文件系统中的文件和目录
filecmp_mkexamples.py:
import os def mkfile(filename, body=None): with open(filename, 'w') as f: f.write(body or filename) return def make_example_dir(top): if not os.path.exists(top): os.mkdir(top) curdir = os.getcwd() os.chdir(top) os.mkdir('dir1') os.mkdir('dir2') mkfile('dir1/file_only_in_dir1') mkfile('dir2/file_only_in_dir2') os.mkdir('dir1/dir_only_in_dir1') os.mkdir('dir2/dir_only_in_dir2') os.mkdir('dir1/common_dir') os.mkdir('dir2/common_dir') mkfile('dir1/common_file', 'this file is the same') mkfile('dir2/common_file', 'this file is the same') mkfile('dir1/not_the_same') mkfile('dir2/not_the_same') mkfile('dir1/file_in_dir1', 'This is a file in dir1') os.mkdir('dir2/file_in_dir1') os.chdir(curdir) return if __name__ == "__main__": os.chdir(os.path.dirname(__file__) or os.getcwd()) make_example_dir('example') make_example_dir('example/dir1/common_dir') make_example_dir('example/dir2/common_dir')运行指令如下:
leichaojian@leichaojian-ThinkPad-T430:~$ find example example example/dir1 example/dir1/common_file example/dir1/file_only_in_dir1 example/dir1/file_in_dir1 example/dir1/dir_only_in_dir1 example/dir1/common_dir example/dir1/common_dir/dir1 example/dir1/common_dir/dir1/common_file example/dir1/common_dir/dir1/file_only_in_dir1 example/dir1/common_dir/dir1/file_in_dir1 example/dir1/common_dir/dir1/dir_only_in_dir1 example/dir1/common_dir/dir1/common_dir example/dir1/common_dir/dir1/not_the_same example/dir1/common_dir/dir2 example/dir1/common_dir/dir2/file_only_in_dir2 example/dir1/common_dir/dir2/dir_only_in_dir2 example/dir1/common_dir/dir2/common_file example/dir1/common_dir/dir2/file_in_dir1 example/dir1/common_dir/dir2/common_dir example/dir1/common_dir/dir2/not_the_same example/dir1/not_the_same example/dir2 example/dir2/file_only_in_dir2 example/dir2/dir_only_in_dir2 example/dir2/common_file example/dir2/file_in_dir1 example/dir2/common_dir example/dir2/common_dir/dir1 example/dir2/common_dir/dir1/common_file example/dir2/common_dir/dir1/file_only_in_dir1 example/dir2/common_dir/dir1/file_in_dir1 example/dir2/common_dir/dir1/dir_only_in_dir1 example/dir2/common_dir/dir1/common_dir example/dir2/common_dir/dir1/not_the_same example/dir2/common_dir/dir2 example/dir2/common_dir/dir2/file_only_in_dir2 example/dir2/common_dir/dir2/dir_only_in_dir2 example/dir2/common_dir/dir2/common_file example/dir2/common_dir/dir2/file_in_dir1 example/dir2/common_dir/dir2/common_dir example/dir2/common_dir/dir2/not_the_same example/dir2/not_the_same
cmp()用于比较文件系统中的两个文件.
import filecmp print 'common_file:' print filecmp.cmp('example/dir1/common_file', 'example/dir2/common_file'), print filecmp.cmp('example/dir1/common_file', 'example/dir2/common_file', shallow=False) print 'not_the_same:' print filecmp.cmp('example/dir1/not_the_same', 'example/dir2/not_the_same'), print filecmp.cmp('example/dir1/not_the_same', 'example/dir2/not_the_same', shallow=False) print 'identical:' print filecmp.cmp('example/dir1/file_only_in_dir1', 'example/dir1/file_only_in_dir1'), print filecmp.cmp('example/dir1/file_only_in_dir1', 'example/dir1/file_only_in_dir1', shallow=False)shallow参数告诉cmp()除了文件的元数据外,是否还要查看文件的内容。默认情况下,会使用由os.stat()得到的信息完成一次浅比较,而不查看内容。对于同时创建的相同大小的文件,如果不比较其内容,会报告为相同。
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py common_file: True True not_the_same: True False identical: True True
对于大目录树的递归比较或者完成更完整的分析,dircmp类会更有用,而report()会打印比较两个目录的报告(1. 不会递归比较其子目录。 2. 不会比较其内容):
>>> import filecmp >>> filecmp.dircmp('example/dir1', 'example/dir2').report() diff example/dir1 example/dir2 Only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1'] Only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2'] Identical files : ['common_file', 'not_the_same'] Common subdirectories : ['common_dir'] Common funny cases : ['file_in_dir1']而更详细的递归比较,可以使用report_full_colsure():
>>> filecmp.dircmp('example/dir1', 'example/dir2').report_full_closure() diff example/dir1 example/dir2 Only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1'] Only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2'] Identical files : ['common_file', 'not_the_same'] Common subdirectories : ['common_dir'] Common funny cases : ['file_in_dir1'] diff example/dir1/common_dir example/dir2/common_dir Common subdirectories : ['dir1', 'dir2'] diff example/dir1/common_dir/dir2 example/dir2/common_dir/dir2 Identical files : ['common_file', 'file_only_in_dir2', 'not_the_same'] Common subdirectories : ['common_dir', 'dir_only_in_dir2', 'file_in_dir1'] diff example/dir1/common_dir/dir2/common_dir example/dir2/common_dir/dir2/common_dir diff example/dir1/common_dir/dir2/dir_only_in_dir2 example/dir2/common_dir/dir2/dir_only_in_dir2 diff example/dir1/common_dir/dir2/file_in_dir1 example/dir2/common_dir/dir2/file_in_dir1 diff example/dir1/common_dir/dir1 example/dir2/common_dir/dir1 Identical files : ['common_file', 'file_in_dir1', 'file_only_in_dir1', 'not_the_same'] Common subdirectories : ['common_dir', 'dir_only_in_dir1'] diff example/dir1/common_dir/dir1/common_dir example/dir2/common_dir/dir1/common_dir diff example/dir1/common_dir/dir1/dir_only_in_dir1 example/dir2/common_dir/dir1/dir_only_in_dir1
除了生成打印报告,dircmp还能计算文件列表,可以在程序中直接使用。
import filecmp import pprint dc = filecmp.dircmp('example/dir1', 'example/dir2') print 'Left:' pprint.pprint(dc.left_list) print '\nRight:' pprint.pprint(dc.right_list)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Left: ['common_dir', 'common_file', 'dir_only_in_dir1', 'file_in_dir1', 'file_only_in_dir1', 'not_the_same'] Right: ['common_dir', 'common_file', 'dir_only_in_dir2', 'file_in_dir1', 'file_only_in_dir2', 'not_the_same']可以向构造函数传入一个要忽略的名字列表(该列表中指定的名字将被忽略),对输入进行过滤。默认情况下,RCS,CVS和tags等名字会被忽略:
import filecmp import pprint dc = filecmp.dircmp('example/dir1', 'example/dir2', ignore=['common_file']) print 'Left:' pprint.pprint(dc.left_list) print '\nRight:' pprint.pprint(dc.right_list)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Left: ['common_dir', 'dir_only_in_dir1', 'file_in_dir1', 'file_only_in_dir1', 'not_the_same'] Right: ['common_dir', 'dir_only_in_dir2', 'file_in_dir1', 'file_only_in_dir2', 'not_the_same']两个输入目录中共有的文件名会保存在common,各目录独有的文件会列在left_only和right_only中:
import filecmp import pprint dc = filecmp.dircmp('example/dir1', 'example/dir2', ) print 'Common:' pprint.pprint(dc.common) print 'Left:' pprint.pprint(dc.left_only) print '\nRight:' pprint.pprint(dc.right_only)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Common: ['not_the_same', 'common_file', 'file_in_dir1', 'common_dir'] Left: ['dir_only_in_dir1', 'file_only_in_dir1'] Right: ['dir_only_in_dir2', 'file_only_in_dir2']公共成员可以进一步分解为文件,目录和“有趣”元素(两个目录中类型不同的内容,或者os.stat()指出错误的地方)
import filecmp import pprint dc = filecmp.dircmp('example/dir1', 'example/dir2', ) print 'Common:' pprint.pprint(dc.common) print '\nDirectories:' pprint.pprint(dc.common_dirs) print '\nFiles:' pprint.pprint(dc.common_files) print '\nFunny:' pprint.pprint(dc.common_funny)解释器显示如下:
leichaojian@leichaojian-ThinkPad-T430:~$ python test.py Common: ['not_the_same', 'common_file', 'file_in_dir1', 'common_dir'] Directories: ['common_dir'] Files: ['not_the_same', 'common_file'] Funny: ['file_in_dir1']