在Python脚本中使用"for line in"迭代器写入文件时遇到问题
这个问题看起来很简单,但经过一天的阅读各种教程和手册后,我还是得问。
我正在用几个嵌套的循环写很多行到几个文件里,插入一些固定的字符串,并且不断地从其他文件复制行。结果输出的内容只是固定字符串和我想复制的所有行的单一副本,而不是多个副本的组合。
我写了一个测试脚本,想看看它是否能模拟这个行为,但它表现得很好:
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 个回答
我猜测,当你在输入文件中逐行读取时(在内层循环里),整个文件被当作一行来读取,所以只会进行一次循环。
我不太确定,但可能的原因是混用了Windows和Linux的文件,以及Python解释器之间的差异("\r\n"和"\n"这两种换行符的编码),不过这里可能有人能纠正我。
一个通用的小建议是,当你在处理Linux和Windows的文本文件时,可以使用 dos2unix
或 unix2dos
这些命令行工具来转换你的文件。
根据你提供的示例输入文件和一个虚构的 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
看起来这个for循环只执行了一次。
我不太明白为什么会这样——可能是因为行结束符有问题,导致整个文件被当作一行来读取。
比如说,如果输入脚本文件的行结束符是\r
,而Python却期待的是\n
的行结束符。
可以试试把打开文件的模式改成'rU',而不是'r'。