? 文件对象
? 文件内建函数
? 文件内建方法
? 文件内建属性
? 标准文件
? 命令行参数
? 文件系统
? 文件执行
? 持久存储
? 相关模块
9.1 文件对象
文件对象不仅可以用来访问普通的磁盘文件, 而且也可以访问任何其它类型抽象层面上的"文件"(具有文件类型接口的其它对象).
内建函数 open() 返回一个文件对象, 对该文件进行后继相关的操作都要用到它.
大量的函数也会返回文件对象或是类文件( file-like )对象.
抽象处理的主要原因是许多的输入/输出数据结构更趋向于使用通用的接口.
Unix 的操作系统把文件作为通信的底层架构接口.
文件只是连续的字节序列, 数据的传输经常会用到字节流, 无论字节流是由单个字节还是大块数据组成.
9.2 文件内建函数[open()和file()]
作为打开文件之门的"钥匙", 内建函数 open() [以及 file() ]提供了初始化输入/输出(I/O) 操作的通用接口.
open() 内建函数
Python 语言中的 open() 函数从 Linux 手册的 fopen() 函数中衍生.
open() 内建函数成功打开文件后时候会返回一个文件对象, 否则引发一个错误, 当操作失败, Python 会产生一个 IOError 异常.
内建函 数 open() 的基本语法
file_object = open( file_name, access_mode='r', buffering=-1)
file_name
file_name 是包含要打开的文件名字的字符串, 它可以是相对路径或者绝对路径.
可选 参数 access_mode
access_mode 也是一个字符串, 代表文件打开的模式, 通常, 文件使用模式 'r', 'w', 或是 'a' 模式来打开, 分别代表读取, 写入和追加.
使用 'r' 或 'U' 模式打开的文件必须是已经存在的.
使用 'w' 模式打开的文件若存在则首先清空, 然后(重新)创建.
以 'a' 模式打开的文件是为追加数据作准备的, 所有写入的数据都将追加到文件的末尾. 即使你 seek 到了其它的地方.如果文件不存在, 将被自动创建, 类似以 'w'模式打开文件.
可选参数 buffering
buffering 用于指示访问文件所采用的缓冲方式.
0 表示不缓冲.
1表示只缓冲一行数据.
任何其它大于 1 的值代表使用给定值作为缓冲区大小.
不提供该参数或者给定负值代表使用系统默认缓冲机制, 既对任何类电报机( tty )设备使用行缓冲, 其它设备使用正常缓冲.
一般情况下使用系统默认方式即可.
文件对象的访问模式
文件模式 操作
以读方式打开
以读方式打开, 同时提供通用换行符支持 (PEP 278)
以写方式打开 (必要时清空)
以追加模式打开 (从 EOF 开始, 必要时创建新文件)
以读写模式打开
以读写模式打开 (参见 w )
以读写模式打开 (参见 a )
以二进制读模式打开
以二进制写模式打开 (参见 w )
以二进制追加模式打开 (参见 a )
以二进制读写模式打开 (参见 r+ )
以二进制读写模式打开 (参见 w+ )
以二进制读写模式打开 (参见 a+ )
一些打开文件的例子
fp = open('/etc/motd') # 以读方式打开
fp = open('test', 'w') # 以写方式打开
fp = open('data', 'r+') # 以读写方式打开
fp = open(r'c:\io.sys', 'rb') # 以二进制读模式打开
工厂函数file()
内建类型有对应的内建函数来创建对象的实例.
open() 和 file() 函数具有相同的功能, 可以任意替换.
任何使用 open() 的地方, 都可以使用 file() 替换它.
一般说来, 我们建议使用 open() 来读写文件, 在您想说明您在处理文件对象时使用 file() , 例 如 if instance(f, file) .
通用换行符支持(UNS)
不同平台用来表示行结束的符号是不同的, 例如 \n, \r, 或者 \r\n .
当你使用 'U' 标志打开文件的时候, 所有的行分割符(或行结束符, 无论它原来是什么)通过 Python 的输入方法(例如 read*() )返回时都会被替换为换行符NEWLINE(\n).
文件对象的newlines 属性会记录它曾“看到的”文件的行结 束符.
如果文件刚被打开, 程序还没有遇到行结束符, 那么文件的newlines 为 None .
在第一行被读取后, 它被设置为第一行的结束符.
如果遇到其它类型的行结束符, 文件的newlines 会成为一个包含每种格式的元组.
UNS 只用于读取文本文件. 没有对应的处理文件输出的方法.
在编译 Python 的时候,UNS 默认是打开的, 如果你不需要这个特性, 在运行configure 脚本时,你可以使用 --without-universal-newlines 开关关闭它.
9.3 文件内建方法
文件方法可以分为四类: 输入, 输出, 文件内移动, 以及 杂项操作.
输入
read()
read() 方法用来直接读取字节到字符串中, 最多读取给定数目个字节.
如果没有给定 size参数(默认值为 -1)或者 size 值为负, 文件将被读取直至末尾. 未来的某个版本可能会删除此方法.
readline()
readline() 方法读取打开文件的一行(读取下个行结束符之前的所有字节). 然后整行,包括行结束符,作为字符串返回.
可选的 size 参数, 默认为 -1, 代表读至行结束符. 如果提供了该参数, 那么在超过size 个字节后会返回不完整的行.
readlines()
readlines() 读取所有(剩余的)行然后把它们作为一个字符串列表返回.
可选参数 sizhint 代表返回的最大字节大小. 如果它大于 0 , 那么返回的所有行应该大约有 sizhint 字节(可能稍微大于这个数字, 因为需要凑齐缓冲区大小).
文件迭代(减少对内存的占用)
xreadlines 对象(可以在xreadlines 模块中找到).
xreadlines.xreadlines(file)
file.xreadlines()
for 循环
iter(file)
readinto() (废弃的方法)
readinto() , 它读取给定数目的字节到一个可写的缓冲器对象, 和废弃的buffer() 内建函数返回的对象是同个类型.(由于 buffer() 已经不再支持, 所以 readinto() 被废弃.)
输出
write()
write() 内建方法功能与 read() 和 readline() 相反. 它把含有文本数据或二进制数据块的字符串写入到文件中去.
注意这里并没有"writeline()" 方法, 因为它等价于使用以行结束符结尾的单行字符串调用 write() 方法.
writelines()
和 readlines() 一样,writelines() 方法是针对列表的操作, 它接受一个字符串列表作为参数, 将它们写入文件.
行结束符并不会被自动加入, 所以如果需要的话, 你必须在调用writelines()前给每行结尾加上行结束符.
核心笔记: 保留行分隔符
当使用输入方法如 read() 或者 readlines() 从文件中读取行时, Python 并不会删除行结束 符.
这个操作被留给了程序员. 例如这样的代码在 Python 程序中很常见:
f = open('myFile', 'r')
data = [line.strip() for line in f.readlines()]
f.close()
类似地, 输出方法 write() 或 writelines() 也不会自动加入行结束符. 你应该在向文件写入数据前自己完成:
文件内移动
seek()
seek() 方法(类似 C 中的 fseek() 函数)可以在文件中移动文件指针到不同的位置.
offset字节代表相对于某个位置偏移量.
位置的默认值为 0 , 代表从文件开头算起(即绝对偏移量), 1 代表从当前位置算起, 2 代表从文件末尾算起.
C程序员fseek(0, 1, 2 分别对应着常量 SEEK_SET, SEEK_CUR, 以及 SEEK_END).
text()
text()方法是对 seek() 的补充; 它告诉你当前文件指针在文件中的位置 - 从文件起始算起,单位为字节.
文件迭代
一行一行访问文件很简单:
for eachLine in f:
pass
在这个循环里, eachLine 代表文本文件的一行(包括末尾的行结束符),你可以使用它做任何想 做的事情.
在 Python 2.2 之前, 从文件中读取行的最好办法是使用 file.readlines() 来读取所有数据, 这样程序员可以尽快释放文件资源.
如果不需要这样做, 那么程序员可以调用 file.readline() 一次读取一行.
曾有一段很短的时间, file.xreadlines() 是读取文件最高效的方法.
在 Python 2.2 中, 我们引进了迭代器和文件迭代, 这使得一切变得完全不同, 文件对象成为 了它们自己的迭代器, 这意味着用户不必调用 read*() 方法就可以在for 循环中迭代文件的每一行.
另外我们也可以使用迭代器的 next 方法, file.next() 可以用来读取文件的下一行.
和其它迭代 器一样, Python 也会在所有行迭代完成后引发 StopIteration 异常.
所以请记得, 如果你见到这样的代码, 这是"完成事情的老方法", 你可以安全地删除对 readline() 的调用.
for eachLine in f.readline():
pass
文件迭代更为高效, 而且写(和读)这样的 Python 代码更容易. 如果你是 Python 新人, 那么请使用这些新特性, 不必担心它们过去是如何.
其它
close()
close() 通过关闭文件来结束对它的访问.
Python 垃圾收集机制也会在文件对象的引用计数降至零的时候自动关闭文件.
这在文件只有一个引用时发生, 例如 fp = open(...), 然后 fp 在原文件显式地关闭前被赋了另一个文件对象.
良好的编程习惯要求在重新赋另个文件对象前关闭这个文件.
如果你不显式地关闭文件, 那么你可能丢失输出缓冲区的数据.
fileno()
fileno() 方法返回打开文件的描述符. 这是一个整数, 可以用在如 os 模块( os.read() )的一些底层操作上.
flush()
调用 flush() 方法会直接把内部缓冲区中的数据立刻写入文件, 而不是被动地等待输出缓冲区被写入.
isatty()
isatty() 是一个布尔内建函数, 当文件是一个类 tty 设备时返回 True , 否则返回 False .
truncate()
truncate() 方法将文件截取到当前文件指针位置或者到给定 size , 以字节为单位.
文件方法杂项
核心笔记: 行分隔符和其它文件系统的差异
操作系统间的差异之一是它们所支持的 行分隔符不同.
在 POSIX (Unix 系列或 Mac OS X)系统 上, 行分隔符是换行符 NEWLINE ( \n ) 字符.
在旧的 MacOS 下是 RETURN ( \r ) , 而 DOS 和Wind32 系统下结合使用了两者 ( \r\n ).
另一个不同是路径分隔符(POSIX 使用 "/", DOS 和 Windows 使用 "\", 旧版本的 MacOS 使用 ":"), 它用来分隔文件路径名, 标记当前目录和父目录.
当我们创建要跨这三个平台的应用的时候, 这些差异会让我们感觉非常麻烦(而且支持的平台 越多越麻烦)。
幸运的是 Python 的 os 模块设计者已经帮我们想到了这些问题. os 模块有五个很有用的属性.
Table 9.2 有助于跨平台开发的 os 模块属性
os 模块属性 描述
linesep 用于在文件中分隔行的字符串
sep 用来分隔文件路径名的字符串
pathsep 用于分隔文件路径的字符串
curdir 当前工作目录的字符串名称
pardir (当前工作目录的)父目录字符串名称
不管你使用的是什么平台, 只要你导入了 os 模块, 这些变量自动会被设置为正确的值, 减少了你的麻烦.
print 语句默认在输出内容末尾后加一个换行符, 而在语句后加一个逗号 就可以避免这个行为.
raw_input()不会保留用 户输入的换行符.
9.4 文件内建属性
文件对象除了方法之外,还有一些数据属性, 这些属性保存了文件对象相关的附加数据.
例如
文件名(file.name ),
文件的打开模式 ( file.mode ),
文件是否已被关闭 ( file.closed),
以及一个标志变量, 它可以决定使用 print 语句打印下一行前是否要加入一个空白字符( file.softspace ).
;
9.5 标准文件
三个标准文件
标准输入(一 般是键盘) stdin
标准输出(到显示器的缓冲输出) stdout
标准错误(到屏幕的非缓冲输出) stderr
一般说来, 只要你的程序一执行, 那么你就可以访问三个标准文件.
它们分别是 标准输入(一 般是键盘), 标准输出(到显示器的缓冲输出)和 标准错误(到屏幕的非缓冲输出).
这里所说的"缓冲和"非缓冲"是指 open() 函数的第三个参数.
这些文件沿用的是 C 语言中的命名, 分别为 stdin , stdout 和 stderr .
我们说"只要你的程序一执行就可以访问这三个标准文件", 意思是这些文件已经被预先打开了, 只要知道它们的文件句柄就可以随时访问这些文件.
sys 模块
Python 中可以通过 sys 模块来访问这些文件的句柄.
导入 sys 模块以后, 就可以使用 sys.stdin , sys.stdout 和 sys.stderr 访问.
print 语句通常是输出到 sys.stdout ; 而内建raw_input() 则通常从 sys.stdin 接受输入.
sys.* 是文件
记得 sys.* 是文件, 所以你必须自己处理好换行符. 而 print 语句会自动在要输出的字符串后加上换行符。
9.6 命令行参数
sys.argv
sys 模块通过 sys.argv 属性提供了对命令行参数的访问。
命令行参数是调用某个程序时除程序名以外的其它参数.
argc 和 argv
熟悉 C 语言的读者可能会问了, "argc 哪去了?" argc 和 argv 分别代表参数个数(argument count)和参数向量(argument vector).
argv 变量代表一个从命令行上输入的各个参数组成的字符串数组, argc 变量代表输入的参数个数.
在 Python 中, argc 其实就是 sys.argv 列表的长度, 而该列表的第一项 sys.argv[0] 永远是程序的名称.
总结如下:
? sys.argv 是命令行参数的列表
? len(sys.argv) 是命令行参数的个数(也就是 argc)
两个模块用来辅助处理命令行参数
getopt 模块 (它更简单些, 但是不是很精细)
optparse 模块 (更强大,而且它更面向对象)
命令行参数有用吗?
各个程序的输出通常使用"管道"实现到下个程序输入的转换.
命令行参数使程序员可以在启动一个程序的时候对程序行为做出选择.
9.7 文件系统
os 模块
对文件系统的访问大多通过 Python 的 os 模块实现, os 模块是Python 访问操作系统功能的主要接口.
os 模块实际上只是真正加载的模块的前端, 而真正的那个"模块"明显要依赖与具体的操作系统.
这个"真正"的模块可能是以下几种之一:
posix (适用于 Unix 操作系统),
nt (Win32),
mac(旧版本的 MacOS),
dos (DOS),
os2 (OS/2),
你不需要直接导入这些模块. 只要导入 os 模块, Python 会为你选择正确的模块, 你不需要考虑底层的工作. 根据你系统支持的特性, 你可能无法访问到一些在其它系统上可用的属性.
os 模块还负责处理大部分的文件系统操作
除了对进程和进程运行环境进行管理外, os 模块还负责处理大部分的文件系统操作, 应用程序开发人员可能要经常用到这些.
这些功能包括删除/重命名文件, 遍历目录树, 以及管理文件访问权限等.
os.path 模块
模块 os.path 可以完成一些针对路径名的操作.
模块 os.path 提供的函数可以完成管理和操作文件路径名中的各个部分, 获取文件或子目录信息, 文件路径查询等操作.
os 的子模块 os.path 更多用于文件路径名处理.
这两个模块提供了与平台和操作系统无关的统一的文件系统访问方法.
9.8 文件执行
运行系统其它位置的其它文件
简单地运行一个操作系统命令
调用一个二进制可执行文件
调用其它类型的脚本(可能是 shell 脚本, Perl, 或是 Tcl/Tk)
如果读者有兴趣了解如何启动其它程序,以及如何与它们进行通讯, 或者是 Python 执行 环境的一般信息, 都可以在 14 章里找到答案.
9.9 永久存储模块(数据归档)
Python 提供了许多可以实现最小化永久性储存的模块.
数据的序列化
将比基本类型复杂的对象转换为一个二进制数据集合, 就可以把数据集合保存起来或通过网络发送, 再重新把数据集合恢复原来的对象格式, 被称为数据的扁平化, 数据的序列化, 或者数据的顺序化.
pickle 和 marshal 模块(数据的扁平化, 数据的序列化, 或者数据的顺序化)
marshal 模块和 pickle 模块可以用来转换并储存 Python 对象(只能储存转换 Python 对象, 为保存和传输提供方便)
marshal 模块只能处理简单的 Python 对象(数字, 序列, 映射, 以及代码对象).
pickle 模块还可以处理递归对象, 被不同地方多次引用的对象, 以及用户定义的类和实例.
pickle 模块还有一个增强的版本叫 cPickle , 使用 C 实现了相关的功能.cPickle 是 pickle 的一个更快的 C 语言编译版本.
DBM 风格的模块
*db* 系列的模块使用传统的 DBM 格式写入数据, Python 提供了 DBM 的多种实现: dbhash/bsddb, dbm, gdbm, 以及 dumbdbm 等.
anydbm 模块, 会自动检测系统上已安装的 DBM 兼容模块, 并选择"最好" 的一个.
dumbdbm 模块是功能最少的一个, 在没有其它模块可用时, anydbm 才会选择它.
*db* 系列的模块为用户的对象提供了一个命名空间, 这些对象同时具备字典对象和文件对象的特点. 不过不足之处在于它们只能储存字符串, 不能对 Python 对象进行序列化.
shelve 模块
shelve 模块使用 anydbm 模块寻找合适的 DBM 模块, 然后使用 cPickle 来完成对储存转换过程.
shelve 模块允许对数据库文件进行 并发的读访问, 但不允许共享读/写访问.
图9-1 用于序列化和永久性储存的 Python 模块
核心模块: pickle 和 cPickle
你可以使用 pickle 模块把 Python 对象直接保存到文件里, 而不需要把它们转化为字符串,
也不用底层的文件访问操作把它们写入到一个二进制文件里. pickle 模块会创建一个 Python 语言
专用的二进制格式, 你不需要考虑任何文件细节, 它会帮你干净利索地完成读写对象操作, 唯一需
要的只是一个合法的文件句柄.
pickle 模块中的两个主要函数是 dump() 和 load() . dump() 函数接受一个文件句柄和一个
数据对象作为参数, 把数据对象以特定格式保存到给定文件里. 当我们使用 load() 函数从文件中
取出已保存的对象时, pickle 知道如何恢复这些对象到它们本来的格式. 我们建议你看一看
pickle 和更"聪明"的 shelve 模块, 后者提供了字典式的文件对象访问功能, 进一步减少了程序
员的工作.
cPickle 是 pickle 的一个更快的 C 语言编译版本.
9.10 相关模块
fileinput 模块
fileinput 模块遍历一组输入文件, 每次读取它们内容的一行, 类似 Perl 语言中的不带参数的 "<>" 操作符. 如果没有明确给定文件名, 则默认从命令行读取文件名.
glob 和 fnmatch 模块
glob 和 fnmatch 模块提供了老式 Unix shell 样式文件名的模式匹配, 例如使用星号( * )通配符代表任意字符串, 用问号( ? )匹配任意单个字符.
核心提示: 使用os.path.expanduser() 的波浪号 ( ~ ) 进行扩展
虽然 glob 和 fnmatch 提供了 Unix 样式的模式匹配, 但它们没有提供对 波浪号(用户目录)
字符, ~ 的支持. 你可以使用 os.path.expanduser() 函数来完成这个功能, 传递一个带波浪号的
目录, 然后它会返回对应的绝对路径. 这里是两个例子, 分别运行在 Unix 和 Win32 环境下:
>>> os.path.expanduser('~/py')
'/home/wesley/py'
>>> os.path.expanduser('~/py')
'C:\\Documents and Settings\\wesley/py'
另外 Unix 家族系统还支持 "~user" 这样的用法, 表示指定用户的目录. 还有, 注意 Win32 版本函数没有使用反斜杠来分隔目录路径.
gzip 和 zlib 模块
gzip 和 zlib 模块提供了对 zlib 压缩库直接访问的接口.
gzip 模块是在 zlib 模块上编写的, 不但实现了标准的文件访问, 还提供了自动的 gzip 压缩/解压缩.
bz2 模块
bz2 类似于 gzip , 用于操作 bzip 压缩的文件.
zipfile 模块
zipfile 模块用于创建, 修改和读取 zip 归档文件. ( tarfile 文件实现了针对 tar 归档文件的相同功能 ).
shutil 模块
shutil 模块提供高级的文件访问功能, 包括复制文件, 复制文件的访问权限, 递归地目录树复制, 等等.
tempfile 模块用于生成临时文件(名).
StringIO 模块在字符串对象顶层加入文件操作接口. 这个接口包括文件对象的所有标准方法.
其它的 Python 类文件对象
用于 网络和文件 socket 对象( socket 模块),
用于 管道连接的popen*() 文件对象( os 和 popen2 模块),
用 于底层文件访问的 fdopen() 文件对象(os 模块),
通过 URL ( Uniform Resource Locator 统一资源定位器)建立的到指定 web 服务器的网络连接( urllib 模块)等.
需要注意的是并非所有的标准文件方法都能在这些对象上实现, 同样的,这些对象也提供了一些普通文件没有的功能.