在Python脚本中使用"for line in"迭代器写入文件时遇到问题

0 投票
4 回答
707 浏览
提问于 2025-04-15 19:37

这个问题看起来很简单,但经过一天的阅读各种教程和手册后,我还是得问。

我正在用几个嵌套的循环写很多行到几个文件里,插入一些固定的字符串,并且不断地从其他文件复制行。结果输出的内容只是固定字符串和我想复制的所有行的单一副本,而不是多个副本的组合。

我写了一个测试脚本,想看看它是否能模拟这个行为,但它表现得很好:

for i in range(10):
    f = open('output.txt','w')

    f.write( "---------------------------\n" )

    FILE1 = open('test1.txt','r')
    for line in FILE1:
        f.write( "... compliments of loop #1 ...\n" )
        f.write( line )
    FILE1.close()

    f.write( "\n##########################\n" )

    FILE2 = open('test2.txt','r')
    for line in FILE2:
        f.write( "... compliments of loop #1 ...\n" )
        f.write( line )
    FILE2.close()

    f.write( "\n++++++++++++++++++++++++++\n" )

输出结果正如预期:固定字符串和复制的字符串交替出现。然而,我真正的、复杂的脚本却没有这样做。我不想把整个脚本贴在这里,但会包括我认为相关的部分(而且我可能也会搞错,因为我不太明白发生了什么)。它引用了一个对象数组——我不会包括类,因为它似乎表现正常。

for i in range(10):
    print "script-%s-%i-%s%s" % (cities[i].user,i,cities[i].name,cities[i].coords)

    f = open("script-%s-%i-%s%s" % (cities[i].user,i,cities[i].name,cities[i].coords),'w')

    f.write( "//\n" )
    f.write( "//\n// %s - %s            %s\n" % (cities[i].user,cities[i].name,cities[i].coords) )
    f.write( "//\n" )
    f.write( "//\n" )

    npc10 = open("script-%s-npc10-%i.txt" % (cities[i].user,i),'r')
    for line in npc10:
        f.write( "ifgosub ( m_city.AnyIdleHero(%s) == false ) wait_for_big_hero\n" % (cities[i].hero) )
        f.write( "ifgosub ( m_city.IsArmyReady(a:%i,s:%i,w:%i,wo:%i) == false ) gosub check_npc10\n" % (lvl10.arch,lvl10.scout,lvl10.warr,lvl10.work) )
        f.write( "ifgosub ( m_city.IsArmyReady(a:%i,s:%i,w:%i,wo:%i) == false ) farm_npc5\n" % (lvl10.arch,lvl10.scout,lvl10.warr,lvl10.work) )
        f.write( "ifgosub ( m_city.AnyIdleHero(%s) == false ) wait_for_big_hero\n" % (cities[i].hero) )
        f.write( line )
    npc10.close()

    f.write( "\n//\n" )
    f.write( "label farm_npc5\n" )

    npc5 = open("script-%s-npc5-%i.txt" % (cities[i].user,i),'r')
    for line in npc5:
        f.write( "sleep 5\n" )
        f.write( line )
    npc5.close()

    f.write( "\n//\n" )
    # ... 107 lines of static f.write's

    f.close()

这是一个输入文件的样本( script-%s-npc10-%i.txt" % (cities[i].user,i) )- 它们都很相似:

attack 456,357 Alfred a:9215,t:185,wo:200,w:2000,s:200 //距离: 1 任务时间: 8分钟 52秒
attack 159,357 Alfred a:9215,t:185,wo:200,w:2000,s:200 //距离: 1 任务时间: 8分钟 52秒
attack 159,215 Alfred a:9215,t:185,wo:200,w:2000,s:200 //距离: 1 任务时间: 12分钟 34秒

这是一个输出文件的样本
( "script-%s-%i-%s%s" % (cities[i].user,i,cities[i].name,cities[i].coords) ):

//
// user1 - cityname1 (456,456)
//
//
ifgosub ( m_city.AnyIdleHero(Alfonso) == false ) wait_for_big_hero
ifgosub ( m_city.IsArmyReady(a:92150,s:2000,w:2000,wo:2000) == false ) gosub check_npc10
ifgosub ( m_city.IsArmyReady(a:92150,s:2000,w:2000,wo:2000) == false ) farm_npc5
ifgosub ( m_city.AnyIdleHero(Alfonso) == false ) wait_for_big_hero
attack 456,357 Alfred a:9215,t:185,wo:200,w:2000,s:200 //距离: 1 任务时间: 8分钟 52秒
attack 159,357 Alfred a:9215,t:185,wo:200,w:2000,s:200 //距离: 1 任务时间: 8分钟 52秒
attack 159,215 Alfred a:9215,t:185,wo:200,w:2000,s:200 //距离: 1 任务时间: 12分钟 34秒
//
label farm_npc5
sleep 5
attack 354,159 Alfred b:50,t:40 //距离: 1 任务时间: 13分钟 20秒
attack 789,654 Alfred b:50,t:40 //距离: 2 任务时间: 26分钟 40秒
attack 125,456 Alfred b:50,t:40 //距离: 2 任务时间: 29分钟 48秒
//
[...]

这有什么不同?为什么固定字符串不重复,而复制的行却重复?

答案是:我在OSX的TextEdit中写了源文件,所以它们的换行符是 '\r',正如gnibbler指出的。根据他的提示,我找到了6 PEP 278: 通用换行符支持。使用文件模式 'rU' 解决了这个问题。

谢谢大家!

4 个回答

0

我猜测,当你在输入文件中逐行读取时(在内层循环里),整个文件被当作一行来读取,所以只会进行一次循环。

我不太确定,但可能的原因是混用了Windows和Linux的文件,以及Python解释器之间的差异("\r\n"和"\n"这两种换行符的编码),不过这里可能有人能纠正我。

一个通用的小建议是,当你在处理Linux和Windows的文本文件时,可以使用 dos2unixunix2dos 这些命令行工具来转换你的文件。

1

根据你提供的示例输入文件和一个虚构的 cities+lvl10 变量,这段脚本的运行效果是正常的:对于输入中的每一行“attack”,它会先打印出多行“ifgosub”,然后再打印出“attack”这一行。

你遇到的问题最可能的原因是,遍历文件时并不是逐行读取,而是一次性读取所有行。在我的实验中,如果我选择使用 CR (\r) 作为行结束符(这个约定在 MacOS 9.x 之前是这样使用的),就会出现这种情况。也许输入文件中某些奇怪的字符编码也会导致类似的问题。

为了验证这个假设,你可以尝试像这样遍历你的文件:

i = 0
npc10 = open("script-%s-npc10-%i.txt" % (cities[i].user,i),'r')
lines = [line for line in npc10]
npc10.close()

之后你可以检查一下 len(lines),看看它是否大于 1。

顺便提一下,从 Python 2.6(或者 2.5 加上 from __future__ import with_statement)开始,你可以使用上下文管理器来确保文件被正确关闭:

with open("script-%s-npc10-%i.txt" % (cities[i].user,i),'r') as npc10:
    for line in npc10:
        #....
    # the file gets closed automatically after the with-statement
1

看起来这个for循环只执行了一次。

我不太明白为什么会这样——可能是因为行结束符有问题,导致整个文件被当作一行来读取。

比如说,如果输入脚本文件的行结束符是\r,而Python却期待的是\n的行结束符。

可以试试把打开文件的模式改成'rU',而不是'r'。

撰写回答