Python 项目之实现文件内容的反转再输入(一)完全反转
文章目录
- 参考
- 描述
- 项目
- 完全反转
- 文件指针
- 再探讨
- 验证
- seek() 函数
- 优化
- 文末的空行
- 猜想
参考
项目 | 描述 |
---|---|
搜索引擎 | Bing |
描述
项目 | 描述 |
---|---|
Python | 3.10.6 |
操作系统 | Ubuntu 22.04.2 LTS (64 位) |
项目
本项目的目的是将目标文件中的内容进行反转并将反转结果输出到另一个文件(结果文件)中。虽然项目看起来较为简单,但我们将通过三种方式(完全反转、逐行实现及逐字实现)来对此进行实现,并在实现过程中对相关知识进行相应的延展,在最后,我们还将对比三种方式的优劣及其适应场所,相信这能使我们对 Python 中与文件读写的相关操作有更深一步的认识。
举个栗子
文件 target.txt 文件中的内容为:
Hello World
那我们的目的就是将该文件中的内容进行反转并且保存到文件 result.txt 中。最终,result.txt 文件中的内容如下:
dlroW olleH
完全反转
将目标文件中的所有内容全部读取到内存中,并在此基础上对文件内容进行反转。在上述操作均完成后,再将反转后的结果输出到结果文件中。具体实现如下:
# 以只读的方式打开目标文件 target.txt
with open('target.txt', 'r') as ft:
# 获取目标文件中保存的内容并将其输出
content = ft.read()
print(content)
# 分割线
print('-------------')
# 以可读写的方式打开文件 result.txt 文件
with open('result.txt', 'w+') as fr:
# 将从目标文件中读取到的内容进行反转
result = content[::-1]
# 将反转后的结果输出到结果文件 result.txt 中
fr.write(result)
# 读取结果文件中的内容并将其输出
if not fr.read():
print('Not Content')
else:
print(fr.read())
执行结果
Hello World
-------------
Not Content
分析
if not fr.read():
print('Not Content')
else:
print(fr.read())
上述代码的运行结果为 Not Content 。这意味着 fr.read() 函数执行后,没有从文件中读取到任何内容。这并不代表 result.txt 文件中不包含任何内容。在继续探讨前,请允许我先涉及(文件)指针的相关概念。
文件指针
Python 中与文件操作相关的内置函数开始执行相关操作的位置将由文件指针所存储。
在使用 read() 函数时,Python 将从文件指针所指向的位置开始读取文件内容直至文件的末尾。而 write() 函数的操作则因文件的打开文件的模式而异,若以写入模式打开文件,则文件指针将指向文件的起始位置,该函数将从此处开始进行内容的写入。若以追加模式打开文件,则文件指针将指向文件的末尾,该函数将从此处开始进行文件的写入。
再探讨
验证
在上一个示例中,我们先是使用 write() 函数向文件中写入内容。后使用 read() 函数读取文件内容。请注意,使用 write() 函数向文件中写入内容,将导致文件指针指向文件的末尾。而此时使用 read() 函数读取文件内容将无法读取到任何内容,因此程序的输出结果为 Not Content 。这一点我们可以通过函数 tell 来进行验证。
with open('target.txt', 'r') as ft:
content = ft.read()
print(content)
print('-------------')
with open('result.txt', 'w+') as fr:
result = content[::-1]
# 输出此时文件指针所指向的位置
print(fr.tell())
fr.write(result)
if not fr.read():
print('Not Content')
# 输出此时文件指针所指向的位置
print(fr.tell())
else:
print(fr.read())
执行结果
Hello World
-------------
0
Not Content
12
可以看到,在开始向结果文件写入已反转的文件内容时,文件指针所指向的位置为 0,即文件的起始位置。而在向文件中写入文件内容后,文件指针所指向的位置发生了改变,指向了文件的末尾。至于为什么是 12 而不是 11 ,我们稍后进行探讨。
seek() 函数
seek() 函数接受两个参数,其中 offset 为必选参数,base 为可选参数。对应的功能如下表:
项目 | 描述 |
---|---|
offset | 指定文件指针相对偏移基准进行偏移的偏移量,该值可为任意整数,也包括负整数。 |
base | 指定文件指针的偏移基准,默认值为 0 。若该值为 0,则表明文件指针的偏移基准为文件的起始位置。若该值为 1,则表明文件指针的偏移基准为文件指针此时所指向的位置。若该值为 2,则表明文件指针的偏移基准为文件的末尾。 |
注:
- seek() 函数将返回文件指针所指向的位置被修改后的位置。
- 使用 seek() 函数时,不可使文件指针指向一个为负值的位置。否则,Python 将抛出错误。对此,请参考如下示例:
fd = open('result.txt', 'r')
print(fd.seek(-1, 0))
fd.close()
抛出错误
ValueError: negative seek position -1
- 若为 seek() 函数指定的第二个参数的值为 1 或 2,那么你需要将该函数的第一个参数设置为零。否则,Python 将抛出错误。这是因为,若文件不是以二进制的方式打开的话,仅允许以文件的起始位置为基准进行偏移。
- 文件指针指向的位置可以超出该文件中的字符总数,但文件并不会因此制造空白字符来填补文件末尾到指针处的空白,这意味着,若在超出文件内容的位置向文件输入内容将附加到文件的末尾处。对此,请参考如下示例:
# 以二进制可读写的方式打开文件
fo = open('result.txt', 'rb+')
# 将文件移至文件末尾处往后 30 个字符的位置
print(fo.seek(30, 2))
# 向文件中输入内容
#(由于我们是以二进制形式打开文件,故需要对写入的内容进行编码操作)
fo.write('RedHeart'.encode())
# 输出,此时文件指针所指向的位置
print(fo.tell())
# 将指针移动至文件的起始位置
print(fo.seek(0, 0))
# 读取文件中的所有内容
# 由于我们是以二进制形式打开文件,故需要对读取的内容进行解码操作)
print(fo.read().decode())
fo.close()
执行效果
在重复执行五次该程序后,输出的结果如下所示(在首次执行该程序时,文件中没有任何内容):
182
190
0
RedHeartRedHeartRedHeartRedHeartRedHeart
但当我们使用编辑器(此处,我使用的是 gedit 编辑器)对文件进行查看时,你可能得到如下内容:
\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00RedHeart\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00RedHeart\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00RedHeart\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00RedHeart\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00RedHeart
也就是说,Python 会使用二进制数据 \00 填补文件末尾(在向文件输入数据前)到向文件输入内容时文件指针所指向的位置间的空白。
优化
好在 Python 为我们提供了能够修改文件指针所指向的位置的函数 seek(),使我们能够更灵活的处理文件。
我们可以使用 seek() 函数来对上一个示例进行优化,优化后的结果如下:
with open('target.txt', 'r') as ft:
content = ft.read()
print(content)
print('-------------')
with open('result.txt', 'w+') as fr:
result = content[::-1]
fr.write(result)
# 使用 seek 函数将文件指针指向文件的起始位置
fr.seek(0, 0)
if not fr.read:
print('Not Content')
else:
print(fr.read())
执行结果
Hello World
-------------
dlroW olleH
文末的空行
对于读取结果末尾的空行,我的理解原先是:
文件结束符 (Eed Of File,EOF) 的出现标志着文档的结尾。Python 读取到该字符后将停止读取文件,该字符被 Python 读取后将被作为换行符进行解析。因此读取结果中多了一行空行。
为此,我又执行了如下代码对结果文件中的内容进行读取。
with open('result.txt', 'r') as fo:
print(fo.read())
执行结果
dlroW olleH
如果按照原先的猜测,那么输出结果应该为(因为在将反转结果输入到结果文件中后,文件的末尾将再次出现文件结束符,按照猜测,该文件结束符在被读取后将被转换为换行符):
dlroW olleH
这说明推测是错误的。
猜想
目标文件是我使用 Linux 系统中常用的编辑器 Vim 来编写的,而读取文件时是由我们编写的 Python 程序读取的,所以做出如下猜想:
Linux 系统中的编辑器在编写玩内容后将自动在文件末尾添加换行符 \n 。
这点,我们可以通过如下代码来进行验证:
with open('target.txt', 'br') as fo:
print(fo.read())
执行结果
由于我们是以二进制方式将文件打开的,因此在文件的输出中,我们可以看到换行符 \n 。
b'Hello World\n'
文件末尾处的换行符表明我们的猜测是正确的。对于 Linux 编辑器为什么要在文件末尾添加换行符,大家可以使用搜索引擎进行搜索,相信一定能有所收获。