Python f.seek 和字符串切片产生不同的结果

Python f.seek and string slice yields different results

我正在 Python 进行我的一个小型个人项目,该项目将 XML 文件解释为基于文本的控制台游戏的脚本。所有单独的源文件都合并到这个大 XML 文件中。为了不加载整个文件的内容而占用太多内存,我决定使用一个单独的 JSON 文件作为某种 table 内容指向各种 ? (包括标签本身)标签。

这是上述table内容的一个例子: {"loremipsum1": [95, 366], "loremipsum3": [462, 283], "loremipsum_insamefile": [746, 62], "loremipsum2": [809, 603]}。 [,] 中的第一个值包含起始字符(好吧,应该是)“<”,第二个值包含场景本身的长度。 XML本身并不重要,重要的是如何根据这两个参数提取文本块。

95是header的长度;这是 *.

的长度

我使用的方法涉及以下内容:

# fcoords contain the [?,?] value in the table of contents
def parse_scene(readfile,fcoords):
    readfile.seek(fcoords[0],0)
    scene = readfile.read(fcoords[1])
# interpreter implementation would go here, but here's a print statement for now since it keeps on throwing errors
    print(scene)

不幸的是,它没有按计划工作,而是返回类似的内容:

========FSEEK========
nes>
<scene name="loremipsum1">
<text>Lorem ipsum.</text>
<continue></continue>
<text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Etiam cursus tempus ipsum vitae euismod. Suspendisse sit amet nulla in sem sagittis cursus.
</text>
<options>
<link ref="loremipsum2">Nullam sollicitudin</link>
<link ref="loremipsum1">Ut tortor felis</link>
</options>
</s

之后我对该文件进行了一些实验,并使用以下脚本来比较 seek-read 和 read-slice;两者的输出不同,而不仅仅是前面或后面的一两个字符的微小差异。如果我粘贴差异,它会拖出 post 更长的时间,所以如果你想测试它,这里是脚本。

测试脚本

import json
# biblio.json contains the table of contents; feel free to replace this with just biblio = {}
with open("biblio.json",'r',encoding='utf-8') as f2:
    biblio = json.load(f2)
# compiled.xml contains the .xml file attached at the bottom of the post
with open("compiled.xml",'r',encoding='utf-8') as f:
    compiled_str = f.read()
    for pair in biblio.items():
        print("3[92m========FSEEK========3[0m")
        f.seek(pair[1][0])
        print(f.read(pair[1][1]))
        print("3[92m=======SLICE=========3[0m")
        print(compiled_str[pair[1][0]:pair[1][0]+pair[1][1]])
        print("==========================================")

与切片的区别

=======SLICE=========
<scene name="loremipsum1">
<text>Lorem ipsum.</text>
<continue></continue>
<text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Etiam cursus tempus ipsum vitae euismod. Suspendisse sit amet nulla in sem sagittis cursus.
</text>
<options>
<link ref="loremipsum2">Nullam sollicitudin</link>
<link ref="loremipsum1">Ut tortor felis</link>
</options>
</scene>

我尝试过的事情:

  1. 在合并过程中从源中删除了表格
  2. 使用 Microsoft Word 检查带有 space 的字符并向其添加行数,因为它不算 '\n'。结果各不相同,有些与[,的第二个值完全匹配,有些需要增加或减少。
  3. 研究了一下合并是怎么算字符的,好像没有错(有需要的我可以提供合并脚本)
  4. 从 open() 参数中删除了 encoding="utf-8",没有任何改变。
  5. 排除了每个场景结束标签后的换行符;并确保场景被一个分隔(没有重叠)。 seek-read 还是不行。

此处附有使用过的 XML 文件。

<game>
<metadata>
<author>['Anonymous']</author>
<version>0.1.0</version>
</metadata>
<scenes>
<scene name="loremipsum1">
<text>Lorem ipsum.</text>
<continue></continue>
<text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Etiam cursus tempus ipsum vitae euismod. Suspendisse sit amet nulla in sem sagittis cursus.
</text>
<options>
<link ref="loremipsum2">Nullam sollicitudin</link>
<link ref="loremipsum1">Ut tortor felis</link>
</options>
</scene>
<scene name="loremipsum3">
<text>
Fusce a rutrum ligula, vel fringilla ex.
Sed lobortis eu mauris non dictum. Fusce nec diam nec metus gravida consectetur vitae et nunc.
Aenean sed ullamcorper ipsum. Vivamus pharetra eros a erat cursus, eget euismod sapien lobortis.
</text>
</scene>
<scene name="loremipsum_insamefile">
<text>Hi.</text>
</scene>
<scene name="loremipsum2">
<text>Ut tortor felis, sodales a ipsum ac, semper molestie lacus.
Nunc faucibus ultrices nibh id porttitor. Phasellus sed tempus neque.</text>
<continue>Vestibulum pulvinar</continue>
<text>
Vestibulum pulvinar, odio egestas ullamcorper porta, massa tellus sodales ipsum, a porttitor elit lectus pharetra risus.
Quisque et congue justo. Integer in quam diam. Nunc id orci justo. Phasellus sed hendrerit dolor.
</text>
<import ref="loremipsum3"></import>

<options>
<link ref="loremipsum1">Lorem ipsum.</link>
<link ref="loremipsum2">Ut tortor felis.</link>
</options>
</scene>
</scenes>
</game>

备注:

  1. 文件拼装的时候,我用的是\n结尾
  2. 我正在使用 Windows
  3. 所有文件均使用 UTF-8 编码读取
  4. 我正在使用 Python 3.9.7

问题 为什么两种方法(seek-read 和 read-slice)的输出不同?,以及 我该怎么做才能正确找到场景,而不必将整个文件(目前很小)加载到内存中?

(还有那些可收缩的剧透,而不是那些只隐藏文本的剧透,这样我就可以更好地格式化它,因为示例占用了太多space)

事实证明,行尾确实是我的问题。

open(filename,mode,newline="") 修复了它。

这是文档中的引述以供将来参考。

newline controls how universal newlines mode works (it only applies to text mode). It can be None, '', '\n', '\r', and '\r\n'. It works as follows:

When reading input from the stream, if newline is None, universal newlines mode is enabled. Lines in the input can end in '\n', '\r', or '\r\n', and these are translated into '\n' before being returned to the caller. If it is '', universal newlines mode is enabled, but line endings are returned to the caller untranslated. If it has any of the other legal values, input lines are only terminated by the given string, and the line ending is returned to the caller untranslated.

When writing output to the stream, if newline is None, any '\n' characters written are translated to the system default line separator, os.linesep. If newline is '' or '\n', no translation takes place. If newline is any of the other legal values, any '\n' characters written are translated to the given string.

内容的 table 是在源文件的字符串化版本上使用 len() 生成的,它使用 \n 作为行尾,但后来写入编译后的 XML文件,转换为 \r\n。形成内容 table 时不存在额外的 \r 似乎会导致偏移。

(现在我得等两天才能接受我自己的回答哈)