blog icon indicating copy to clipboard operation
blog copied to clipboard

Python之旅:第十一章-文件

Open kaindy7633 opened this issue 3 years ago • 0 comments

Table of Contents generated with DocToc

  • Python之旅:第十一章 文件
    • 打开文件
    • 文件的基本方法
      • 读取和写入
      • 使用管道重定向输出
      • 随机存取
      • 读取和写入行
      • 关闭文件
        • 上下文管理器
      • 使用文件的基本方法
    • 迭代文件的内容
      • 每次一个字符(或字节)
      • 每次一行
      • 读取所有内容
      • 使用fileinput实现延迟行迭代
      • 文件迭代器

Python之旅:第十一章 文件

打开文件

要打开文件,可使用函数open,它位于自动导入的模块io中,函数open将文件名作为唯一必不可少的参数,并返回一个文件对象。

>>> f = open('somefile.txt')

如果要通过写入文本来创建文件,这种调用函数open的方法并不能满足要求,为此可以使用open的第二个参数。

调用函数open时,如果只指定文件名,将获得一个可读取的文件对象,如果要写入文件,必须通过指定模式来显式地指出这一点,函数open的参数mode可取值有多个:

描述
r 读取模式(默认值)
w 写入模式
x 独占写入模式
a 附加模式
b 二进制模式(与其他模式结合使用)
t 文本模式(默认值,与其他模式结合使用)
+ 读写模式(与其他模式结合使用)

显式的指定读取模式与不指定模式相同。写入模式让你能够写入文件,并在文件不存在时创建它。独占写入模式在文件已存在时引发FileExistsError异常。在写入模式下打开文件,原有内容将被删除(截断),并从文件开头处开始写入,如果要在既有文件末尾继续写入,可使用附加模式。

+可与其他任何模式结合起来使用,表示即可读取也可写入。例如要打开一个文本文件进行读写,可以使用r+

默认模式为rt,这意味着将把文件视为经过编码的Unicode文本。因此将自动执行解码和编码,且默认使用UTF-8编码。

如果文件包含非文本的二进制数据,如声音剪辑或图像,只需使用二进制模式rb来禁用与文本相关的功能即可。

文件的基本方法

读取和写入

文件最重要的功能是提供和接收数据。如果有一个名为f的文件对象,可使用f.write来写入数据,还可使用f.read来读取数据。

每当调用f.write(string)时,字符串都将写入到文件既有内容的后面。

>>> f = open('somfile.txt', 'w')
>>> f.write('Hello, ')
7
>>> f.write('World!')
6
>>> f.close

读取也一样,只需告诉方法你需要读取多少个字符(二进制模式下是多少个字节)

>>> f = open('somfile.txt', 'r')
>>> f.read(4)
'Hell'
>>> f.read()
'o, World!'

使用管道重定向输出

bashshell中,可以依次输入多条命令,并使用管道将它们链接起来

cat somefile.txt | python somescript.py | sort

管道将一条命令的标准输出链接到下一个命令的标准输入。下面是一个计算包含多少个单词的脚本:

# somescript.py
import sys
text = sys.stdin.read()
words = text.split()
wordcount = len(words)
print('Wordcount: ', wordcount)

读取下面这个文本,并计算有多少个单词

'''
Your mother was a hamster and your
father smelled of elderberries.
'''

cat somefile.txt | python somescript.py

结果如下:

Wordcount: 11

随机存取

前面介绍的读写都是将文件视为流,只能按顺序从头到尾读取,实际上,可在文件中移动读取,这称为随机存取。为此,可使用文件对象的两个方法:seektell.

方法seek(offset[, whence])将当前位置移动到offsetwhence指定的地方。参数offset指定了字节(字符)数,而参数whence默认为io.SEEK_SET(0),这意味着偏移量是相对于文件开头的。参数whence还可以设置为io.SEEK_CUR(1)io.SEEK_END(2),前者表示相对于当前位置进行移动,而后者表示相对于文件末尾进行移动。

>>> f = open(r'/Users/liuzhen/python/somefile.txt', 'w')
>>> f.write('01234567890123456789')
20
>>> f.seek(5)
5
>>> f.write('Hello, World!')
13
>>> f.close()
>>> f = open(r'/Users/liuzhen/python/somefile.txt')
>>> f.read()
'01234Hello, World!89'

方法tell()返回当前位于文件的什么位置

>>> f = open(r'/Users/liuzhen/python/somefile.txt')
>>> f.read(3)
'012'
>>> f.read(2)
'34'
>>> f.tell()
5

读取和写入行

要读取文本中的一行(从当前位置到下一个换行符的文本),可以使用方法readline,滴啊用这个方法可以不提供任何参数,在这种情况下,将读取一行并返回它。也可以提供一个非负整数,指定readline最多读取多少个字符。要读取文件中的所有行,并以列表的方式返回,可以使用方法readlines

方法writelinesreadlines相反,它接收一个字符串列表参数,也可以是任何可迭代对象,并将这些字符串写入到文件或流中。请注意,写入时不会添加换行符,因此你必须自行添加。另外,没有方法writeline

关闭文件

在读取文件后,并忘了调用方法close关闭文件。通常,程序退出时将自动关闭文件对象,但手动调用方法关闭文件总是对的,因为这样可以避免在某些系统中出现问题。

对于写入过的文件,一定要将其关闭,因为Python可能缓冲你写入的数据。如果程序崩溃,数据可能根本不会写入到文件中。如果想重置缓冲并将修改反映到磁盘文件中,但又不想关闭文件,可以使用flush

要确保文件关闭,可以使用一条try/finally语句,并在finally子句中调用close

# 在这里打开文件
try:
    # 将数据写入到文件中
finally:
    file.close()

实际上,Python还提供了一条专门为此设计的语句,那就是with语句:

with open("somefile.txt") as somefile:
    do_something(somefile)

with语句让你能够打开文件并将其赋值给一个变量somefile,你可以将数据写入文件,到达语句末尾时,将自动关闭文件。

上下文管理器

with语句实际上是一个非常通用的结构,它允许你使用上下文管理器。上下文管理器支持两个方法的对象:__enter____exit__(魔法方法,自动调用)。

方法__enter__不接受任何参数,在进入with时被调用,其返回值被赋值给关键字as后面的变量。

方法__exit__接收三个参数:异常类型、异常对象那个和异常跟踪。

文件也可用作上下文管理器,它的方法__enter__返回文件对象本身,而方法__exit__将关闭文件。

使用文件的基本方法

这里有一个简单的文本文件,内容如下:

'''
Welcome to this file
There is nothing here except
This stupid haiku
'''

我们来试试前面介绍过的方法,首先是read(n)

>>> f = open(r'/Users/liuzhen/python/somefile.txt')
>>> f.read(7)
'Welcome'
>>> f.read(4)
' to '
>>> f.close()

接下来是read()

>>> f = open(r'/Users/liuzhen/python/somefile.txt')
>>> print(f.read())
Welcome to this file
There is nothing here except
This stupid haiku
>>> f.close()

下面是readline()

>>> f = open(r'/Users/liuzhen/python/somefile.txt')
>>> for i in range(3):
	      print(str(i) + ': ' + f.readline(), end='')

0: Welcome to this file
1: There is nothing here except
2: This stupid haiku
>>> f.close()

最后是readlines()

>>> import pprint
>>> pprint.pprint(open(r'/Users/liuzhen/python/somefile.txt').readlines())
['Welcome to this file\n',
 'There is nothing here except\n',
 'This stupid haiku']

上面的例子中文件对象会被自动关闭。

下面我们来尝试写入操作,首先是write(string)

>>> f = open(r'/Users/liuzhen/python/somefile.txt', 'w')
>>> f.write('this\nis no\nhaiku')
16
>>> f.close()

然后是writelines(list)

>>> f = open(r'/Users/liuzhen/python/somefile.txt')
>>> lines = f.readlines()
>>> f.close()
>>> lines[1] = "isn't a\n"
>>> f = open(r'/Users/liuzhen/python/somefile.txt', 'w')
>>> f.writelines(lines)
>>> f.close()

迭代文件的内容

有一种比较常见的文件操作是迭代其内容,并在迭代过程中做一些事。看下面的process函数,它将打印每次迭代的字符或行

def process(string):
    print('Processing: ', string)

下面的示例会使用这个函数来模拟一些常见的操作。filename参数表示一些文件。

每次一个字符(或字节)

一种很简单但却不常见的操作是在while循环中使用方法read,来读取文件中每个字符(或二进制模式下的每个字节):

with open(filename) as f:
    char = f.read(1)
    while char:
        process(char)
        char = f.read(1)

上面的示例虽然可以成功运行,但同样的代码出现了两次,所以,我们做了如下优化:

with open(filename) as f:
    while True:
        char = f.read(1)
        if not char: break
        process(char)

每次一行

处理文本文件时,我们通常想做的是迭代行,而不是每个字符,迭代行可以使用方法readline

with open(filename) as f:
    while True:
        line = f.readline()
        if not line: break
        process(line)

读取所有内容

如果要一次读取整个文件,可以使用方法read且不提供任何参数,也就是将整个文件读取到一个字符串中。也可以使用方法readlines,将整个文件读取到一个字符串列表中,每个字符串是一行。

# 使用read迭代
with open(filename) as f:
    for char in f.read():
        process(char)

# 使用readlines迭代行
with open(filename) as f:
    for line in f.radlines():
        process(line)

使用fileinput实现延迟行迭代

如果文件过大,使用readlines会占用过多的内存,当然你可以使用while循环和readline,但在Python中,应首选for循环,我们可以使用一种名为延迟行迭代的方法,只读取实际需要的文本部分。这里使用了fileinput,模块fileinput会负责打开文件,只需给它提供一个文件名即可:

import fileinput
for line in fileinput.input(filename):
    process(line)

文件迭代器

在Python中最常见的就是文件迭代器了。实际上文件是可迭代的,这意味着可在for循环中直接使用它们来迭代行:

with open(filename) as f:
    for line in f:
        process(line)

本章节完毕

本系列目录:

kaindy7633 avatar Mar 08 '21 03:03 kaindy7633