目录
1.2 使用read()、 readline()或者readlines()读文本文件
1 文件输入/输出
数据持久化最简单的类型是普通文件,有时也叫平面文件(flat file)。它仅仅是在一个文件名下的字节流, 把数据从一个文件读入内存,然后从内存写入文件。 Python 很容易实现这些文件操作,它模仿熟悉的和流行的 Unix 系统的操作。
读写一个文件之前需要打开它:
fileobj = open(filename, mode)
下面是对该 open() 调用的简单解释:
• fileobj 是 open() 返回的文件对象;
• filename 是该文件的字符串名;
• mode 是指明文件类型和操作的字符串。
mode 的第一个字母表明对其的操作。
• r 表示读模式。
• w 表示写模式。如果文件不存在则新创建,如果存在则重写新内容。
• x 表示在文件不存在的情况下新创建并写文件。
• a 表示如果文件存在,在文件末尾追加写内容。
mode 的第二个字母是文件类型:
• t(或者省略)代表文本类型;
• b 代表二进制文件。
打开文件之后就可以调用函数来读写数据,之后的例子会涉及。
最后需要关闭文件。
另外一个问题是关于换行符的识别问题,在Unix和Windows中是不一样的(分别是n和rn)。 默认情况下,
Python会以统一模式处理换行符。 这种模式下,在读取文本的时候,Python可以识别所有的普通换行符
并将其转换为单个 \n 字符。 类似的,在输出时会将换行符 \n 转换为系统默认的换行符。 如果你不希
望这种默认的处理方式,可以给 open() 函数传入参数 newline='' ,就像下面这样:
1.1 使用write()写文本文件
出于一些原因,我们没有太多的关于狭义相对论的五行打油诗(limerick1)。下面这首作为源数据:
类似的,为了写入一个文本文件,使用带有 wt 模式的 open() 函数,如果之前文件内容存在则清除并覆盖掉。
以下代码将整首诗写到文件 'relativity' 中:
函数 write() 返回写入文件的字节数。和 print() 一样,它没有增加空格或者换行符。同样,你也可以在一个文本文件中使用 print():
这就产生了一个问题:到底是使用 write() 还是 print() ? print() 默认会在每个参数后面添加空格, 在每行结束处添加换行。 在之前的例子中,它在文件 relativity 中默认添加了一个换行。为了使 print() 与 write() 有同样的输出,传入下面两个参数。
• sep 分隔符:默认是一个空格 ' '
• end 结束字符:默认是一个换行符 '\n'
除非自定义参数,否则 print() 会使用默认参数。在这里,我们通过空字符串替换 print()添加的所有多余输出:
如果源字符串非常大,可以将数据分块,直到所有字符被写入:
第一次写入 100 个字符,然后写入剩下的 50 个字符。
1.2 使用read()、 readline()或者readlines()读文本文件
你可以按照下面的示例那样,使用不带参数的 read() 函数一次读入文件的所有内容。但在读入文件时要格外注意, 1 GB 的文件会用到相同大小的内存。
同样也可以设置最大的读入字符数限制 read() 函数一次返回的大小。下面一次读入 100 个字符,然后把每一块拼接成原来的字符串 poem:
读到文件结尾之后,再次调用 read() 会返回空字符串(''), if not fragment 条件被判为False。此时会跳出 while True 的循环。 当然,你也能使用 readline() 每次读入文件的一行。在下一个例子中,通过追加每一行拼接成原来的字符串 poem:
对于一个文本文件,即使空行也有 1 字符长度(换行字符 '\n'),自然就会返回 True。当文件读取结束后, readline()(类似 read())同样会返回空字符串,也被 while True 判为 False。
读取文本文件最简单的方式是使用一个迭代器(iterator),它会每次返回一行。这和之前的例子类似,但代码会更短:
前面所有的示例最终都返回单个字符串 poem。函数 readlines() 调用时每次读取一行,并返回单行字符串的列表:
1.3 使用write()写二进制文件
如果文件模式字符串中包含 'b',那么文件会以二进制模式打开。这种情况下,读写的是字节而不是字符串。
我们手边没有二进制格式的诗,所以直接在 0~255 产生 256 字节的值:
以二进制模式打开文件,并且一次写入所有的数据:
再次, write() 返回到写入的字节数。
对于文本,也可以分块写二进制数据:
1.4 使用read()读二进制文件
下面简单的例子只需要用 'rb' 打开文件即可:
1.5 使用with自动关闭文件
如果你忘记关闭已经打开的一个文件, 在该文件对象不再被引用之后 Python 会关掉此文件。这也就意味着在一个函数中打开文件, 没有及时关闭它,但是在函数结束时会被关掉。然而你可能会在一直运行中的函数或者程序的主要部分打开一个文件, 应该强制剩下的所有写操作完成后再关闭文件。
Python 的上下文管理器(context manager)会清理一些资源,例如打开的文件。它的形式为 with expression as variable:
完成上下文管理器的代码后,文件会被自动关闭。
1.6 使用其他分隔符或行终止符打印
可以使用在 print() 函数中使用 sep 和 end 关键字参数,以你想要的方式输出。 比如:
使用 end 参数也可以在输出中禁止换行。 比如:
当你想使用非空格分隔符来输出数据的时候,给 print() 函数传递一个 seq 参数是最简单的方案。
有时候你会看到一些程序员会使用 str.join() 来完成同样的事情。 比如:
str.join() 的问题在于它仅仅适用于字符串。 这意味着你通常需要执行另外一些转换才能让它正常工
作。 比如:
你当然可以不用那么麻烦,仅仅只需要像下面这样写:
1.8 读写字节数据
问题:
你想读写二进制文件,比如图片,声音文件等等。
解决方案:
使用模式为 rb 或 wb 的 open() 函数来读取或写入二进制数据。 比如
在读取二进制数据时,需要指明的是所有返回的数据都是字节字符串格式的,而不是文本字符串。 类似的,在写入的时候,必须保证参数是以字节形式对外暴露数据的对象(比如字节字符串,字节数组对象等)。
如果你想从二进制模式的文件中读取或写入文本数据,必须确保要进行解码和编码操作。 比如:
2.1 文件路径名的操作
问题:
你需要使用路径名来获取文件名,目录名,绝对路径等等。
解决方案:
使用 os.path 模块中的函数来操作路径名。 下面是一个交互式例子来演示一些关键的特性:
对于任何的文件名的操作,你都应该使用 os.path 模块,而不是使用标准字符串操作来构造自己的代码。 特别是为了可移植性考虑的时候更应如此,因为 os.path 模块知道Unix和Windows系统之间的差异并且能够可靠地处理类似 Data/data.csv 和 Data\data.csv 这样的文件名。 其次,你真的不应该浪费时间去重复造轮子。 通常最好是直接使用已经为你准备好的功能。
要注意的是 os.path 还有更多的功能在这里并没有列举出来。 可以查阅官方文档来获取更多与文件测试,符号链接等相关的函数说明。
2.2 测试文件是否存在
问题:
你想测试一个文件或目录是否存在。
解决方案:
使用 os.path 模块来测试一个文件或目录是否存在。 比如:
你还能进一步测试这个文件时什么类型的。 在下面这些测试中,如果测试的文件不存在的时候,结果都会返回False:
2.3 获取文件夹中的文件列表
问题:
你想获取文件系统中某个目录下的所有文件列表。
解决方案:
使用 os.listdir() 函数来获取某个目录中的文件列表:
结果会返回目录中所有文件列表,包括所有文件,子目录,符号链接等等。 如果你需要通过某种方式过滤
数据,可以考虑结合 os.path 库中的一些函数来使用列表推导。 比如:
字符串的 startswith() 和 endswith() 方法对于过滤一个目录的内容也是很有用的。 比如:
2.3 重命名和删除文件
rename()方法
rename()方法需要两个参数,当前的文件名和新文件名。
语法:
os.rename(current_file_name, new_file_name)
remove()方法
你可以用remove()方法删除文件,需要提供要删除的文件名作为参数。
语法:
os.remove(file_name)
2.4 目录操作
mkdir()方法
可以使用os模块的mkdir()方法在当前目录下创建新的目录们。你需要提供一个包含了要创建的目录名称的参数。
语法:
os.mkdir("newdir")
chdir()方法
可以用chdir()方法来改变当前的目录。chdir()方法需要的一个参数是你想设成当前目录的目录名称。
getcwd()方法
getcwd()方法显示当前的工作目录。
rmdir()方法
rmdir()方法删除目录,目录名称以参数传递。在删除这个目录之前,它的所有内容应该先被清除。