项目 | 描述 |
---|---|
搜索引擎 | Bing |
项目 | 描述 |
---|---|
Python | 3.10.6 |
操作系统 | Ubuntu 22.04.2 LTS (64 位) |
由于在下使用的系统为 Linux 系统,为避免出现使用 Vim 等编辑器编辑文件后在文末自动产生的空行,我将使用如下命令向 target.txt 文件中输入本次需要反转的文件内容。
echo -en '北方有佳人,绝世而独立。\n一顾倾人城,再顾倾人国。\n宁不知倾城与倾国,佳人难再得。' > target.txt
其中:
项目 | 描述 |
---|---|
-e | 使得该命令处理的文本中的转义字符可被正常解析(\n 将被解析为换行符)。 |
-n | 使得该命令的输出中,文本末尾不会自动添加换行符。 |
> | 重定向符号,将该命令的输出由终端重定向至文本文件 target.txt 中。 |
文件内容的反转再输入的逐行实现可以有三种思路。
头读尾加方案的具体实现
'''
以写入方式打开一个文件,
即使不对该文件进行操作,
该文件中的内容也将被去除。
'''
# 以写入的方式打开结果文件,以去除
# 结果文件中的原有内容,
# 避免被结果文件中的原有内容干扰。
with open('result.txt', 'w') as fo:
pass
# 以只读当方式打开目标文件
with open('target.txt', 'r') as ft:
# 从目标文件中读取第一行内容并将其反转
content = ft.readline()[::-1]
# 以二进制可读写(追加)的方式打开目标文件
with open('result.txt', 'ab+') as fr:
# 在无法从目标文件中读取到内容时退出循环
while content:
# 向结果文件中写入读取到的单行文件内容
fr.write(content.encode())
# 将文件指针移动至起始位置
fr.seek(0, 0)
# 读取文件中的下一行内容并将其进行反转
content = ft.readline()[::-1]
# 在将目标文件中的内容反转输入到结果文件中后
# ,向终端输出结果文件中的内容。
print('----------')
print(fr.read().decode())
执行效果
结果并不如我们所预料的那般,结果文件中的内容并不是目标文件的反转。
----------
。立独而世绝,人佳有方北
。国人倾顾再,城人倾顾一。得再难人佳,国倾与城倾知不宁
分析:
在每一次将目标文件中单行内容反转再输入到结果文件中后,都使用了 seek() 函数将文件指针移动至 起始位置 。也许这个 起始位置 在追加模式下并不是我们所理解的文件的起始位置。在将内容追加至该文件时,Python 将自动的将文件指针移动至文件的末尾处并开始写入内容。对此,我们做出如下修改以对该设想进行检验:
with open('result.txt', 'w') as fo:
pass
with open('target.txt', 'r') as ft:
content = ft.readline()[::-1]
with open('result.txt', 'ab+') as fr:
while content:
print('输入前文件指针所处的位置', fr.tell(), sep=' -----> ')
fr.write(content.encode())
print('输入后文件指针所处的位置', fr.tell(), sep=' -----> ')
fr.seek(0, 0)
content = ft.readline()[::-1]
print('----------')
print(fr.read().decode())
执行结果
输入前文件指针所处的位置 -----> 0
输入后文件指针所处的位置 -----> 37
输入前文件指针所处的位置 -----> 0
输入后文件指针所处的位置 -----> 37
输入前文件指针所处的位置 -----> 0
输入后文件指针所处的位置 -----> 45
----------
。立独而世绝,人佳有方北
。国人倾顾再,城人倾顾一。得再难人佳,国倾与城倾知不宁
结论
在追加模式下,文件指针的起始位置 seek(0, 0) 为文件的末尾处。在将文件内容输入文件后,文件指针所处的位置恰为被输入至该文件中的内容所占据的字节大小(在 UTF-8 中多数中文字符占据三个字节,ASCII 字符占据一个字节,换行符属于 ASCII 字符)。
该方案并不能很好的解决我们需要解决的问题。
反转再反转方案的具体实现
with open('result.txt', 'w') as f:
pass
with open('target.txt', 'r') as ft:
with open('result.txt', 'a+') as fr:
# 使用 readlines() 函数将文本划分为以一行
# 为元素的列表并将该列表进行反转。
for line in ft.readlines()[::-1]:
# 将每一行内容进行反转后输入到结果文件中。
fr.write(line[::-1])
# 将文件指针置于文件的起始位置,
# 并将读取到的文件内容输出到终端中。
fr.seek(0, 0)
print(fr.read())
执行结果
。得再难人佳,国倾与城倾知不宁
。国人倾顾再,城人倾顾一
。立独而世绝,人佳有方北
尾部反转 头部写入方案的具体实现
with open('result.txt', 'w'):
pass
# 以只读模式打开结果文件
with open('target.txt', 'r') as ft:
# 以二进制可读写模式打开结果文件
with open('result.txt', 'wb+') as fr:
# 使用 readlines() 函数将文本划分为以一行
# 为元素的列表并将该列表进行反转。
for line in ft.readlines()[::-1]:
# 将每一行内容进行反转后输入到结果文件中。
fr.write(line[::-1].encode())
# 将文件指针移动至文件头部
fr.seek(0, 0)
print(fr.read().decode())
发现异常
UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0x9f in position 37: invalid start byte
分析:
出现上述异常的原因是因为存在二进制数据无法使用 UTF-8 进行解码。我们的数据是使用 UTF-8 进行编码的,但却无法使用 UTF-8 进行解码。这只能说明在向结果文件中输入二进制数据时出现了错误,导致二进制数据中的一部分被破坏。
我们可以通过将每一行二进制数据输出至终端,并将结果文件中的二进制数据输入至终端进行对比。
with open('result.txt', 'w'):
pass
with open('target.txt', 'r') as ft:
with open('result.txt', 'wb+') as fr:
for line in ft.readlines()[::-1]:
print(line[::-1].encode())
fr.write(line[::-1].encode())
fr.seek(0, 0)
print(fr.read())
执行结果
b’\xe3\x80\x82\xe5\xbe\x97\xe5\x86\x8d\xe9\x9a\xbe\xe4\xba\xba\xe4\xbd\xb3\xef\xbc\x8c\xe5\x9b\xbd\xe5\x80\xbe\xe4\xb8\x8e\xe5\x9f\x8e\xe5\x80\xbe\xe7\x9f\xa5\xe4\xb8\x8d\xe5\xae\x81’
b’\n\xe3\x80\x82\xe5\x9b\xbd\xe4\xba\xba\xe5\x80\xbe\xe9\xa1\xbe\xe5\x86\x8d\xef\xbc\x8c\xe5\x9f\x8e\xe4\xba\xba\xe5\x80\xbe\xe9\xa1\xbe\xe4\xb8\x80’
b’\n\xe3\x80\x82\xe7\xab\x8b\xe7\x8b\xac\xe8\x80\x8c\xe4\xb8\x96\xe7\xbb\x9d\xef\xbc\x8c\xe4\xba\xba\xe4\xbd\xb3\xe6\x9c\x89\xe6\x96\xb9\xe5\x8c\x97’
b’\n\xe3\x80\x82\xe7\xab\x8b\xe7\x8b\xac\xe8\x80\x8c\xe4\xb8\x96\xe7\xbb\x9d\xef\xbc\x8c\xe4\xba\xba\xe4\xbd\xb3\xe6\x9c\x89\xe6\x96\xb9\xe5\x8c\x97\x9f\xa5\xe4\xb8\x8d\xe5\xae\x81’
观察结果。不难发现,结果文件中的二进制数据(以十六进制的形式进行展现)与单行内容最长的那一行(即目标文件的最后一行)的二进制数据的长度是相同的。
>>> a = b’\n\xe3\x80\x82\xe7\xab\x8b\xe7\x8b\xac\xe8\x80\x8c\xe4\xb8\x96\xe7\xbb\x9d\xef\xbc\x8c\xe4\xba\xba\xe4\xbd\xb3\xe6\x9c\x89\xe6\x96\xb9\xe5\x8c\x97\x9f\xa5\xe4\xb8\x8d\xe5\xae\x81’
>>> len(a)
45
>>> b = b’\xe3\x80\x82\xe5\xbe\x97\xe5\x86\x8d\xe9\x9a\xbe\xe4\xba\xba\xe4\xbd\xb3\xef\xbc\x8c\xe5\x9b\xbd\xe5\x80\xbe\xe4\xb8\x8e\xe5\x9f\x8e\xe5\x80\xbe\xe7\x9f\xa5\xe4\xb8\x8d\xe5\xae\x81’
>>> len(b)
45
并且结果文件中的二进制数据的前大半部分与最后输入结果文件中的那一行(目标文件的第一行)完全一致。
结论
上述现象表明,使用写入模式多次在文件的起始位置写入内容将覆盖原有内容。文件的原有内容并不会因为该文件的头部存在输入而向后移动,导致原有内容被覆盖。因此,
该方案并不能很好的解决我们需要解决的问题。
逐行实现的三种方式中仅 反转再反转 方案能够达成目标。但该方案仍需要将文件全部读取,所以逐行实现并不能很好的解决大文件操作时存在的高额内存占用问题。