我想使用Python将打印重定向到一个.txt文件。我有一个for循环,它将打印每个.bam文件的输出,而我想将所有输出重定向到一个文件。所以我试着说:
f = open('output.txt','w')
sys.stdout = f
在我剧本的开头。但是,我在.txt文件中什么也没有得到。
我的剧本是:
#!/usr/bin/python
import os,sys
import subprocess
import glob
from os import path
f = open('output.txt','w')
sys.stdout = f
path= '/home/xxx/nearline/bamfiles'
bamfiles = glob.glob(path + '/*.bam')
for bamfile in bamfiles:
filename = bamfile.split('/')[-1]
print 'Filename:', filename
samtoolsin = subprocess.Popen(["/share/bin/samtools/samtools","view",bamfile],
stdout=subprocess.PIPE,bufsize=1)
linelist= samtoolsin.stdout.readlines()
print 'Readlines finished!'
那么问题是什么呢?除了sys。stdout还有其他方法吗?
我需要我的结果看起来像:
Filename: ERR001268.bam
Readlines finished!
Mean: 233
SD: 10
Interval is: (213, 252)
不要使用打印,使用日志记录
您可以更改sys。Stdout指向文件,但这是一种相当笨拙且不灵活的处理此问题的方法。不要使用print,而是使用logging模块。
使用日志记录,您可以像打印stdout一样进行打印,也可以将输出写入文件。您甚至可以使用不同的消息级别(关键、错误、警告、信息、调试),例如,只将主要问题打印到控制台,但仍然将次要代码操作记录到文件中。
一个简单的例子
导入日志记录,获取日志记录器,并设置处理级别:
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # process everything, even if everything isn't printed
如果你想打印到标准输出:
ch = logging.StreamHandler()
ch.setLevel(logging.INFO) # or any other level
logger.addHandler(ch)
如果你也想写入一个文件(如果你只想写入一个文件,跳过最后一部分):
fh = logging.FileHandler('myLog.log')
fh.setLevel(logging.DEBUG) # or any level you want
logger.addHandler(fh)
然后,无论你在哪里使用打印,请使用记录器方法之一:
# print(foo)
logger.debug(foo)
# print('finishing processing')
logger.info('finishing processing')
# print('Something may be wrong')
logger.warning('Something may be wrong')
# print('Something is going really bad')
logger.error('Something is going really bad')
要了解关于使用更高级日志功能的更多信息,请阅读Python文档中的优秀日志教程。
你可以用file参数重定向打印(在python2中有>>操作符代替)。
f = open(filename,'w')
print('whatever', file=f) # Python 3.x
print >>f, 'whatever' # Python 2.x
在大多数情况下,最好只是正常地写入文件。
f.write('whatever')
或者,如果你有几个项目想用空格写,比如print:
f.write(' '.join(('whatever', str(var2), 'etc')))
如果重定向stdout对您的问题有效,Gringo Suave的回答很好地演示了如何进行重定向。
为了让它更简单,我使用上下文管理器创建了一个版本,使用with语句实现了简洁的通用调用语法:
from contextlib import contextmanager
import sys
@contextmanager
def redirected_stdout(outstream):
orig_stdout = sys.stdout
try:
sys.stdout = outstream
yield
finally:
sys.stdout = orig_stdout
要使用它,你只需要执行以下操作(源自Suave的例子):
with open('out.txt', 'w') as outfile:
with redirected_stdout(outfile):
for i in range(2):
print('i =', i)
当模块以您不喜欢的方式使用它时,它对于选择性地重定向打印非常有用。唯一的缺点(在许多情况下这是解决问题的关键)是,如果想要多个具有不同stdout值的线程,它就不起作用,但这需要一种更好、更通用的方法:间接模块访问。你可以在这个问题的其他答案中看到它的实现。
你可能不喜欢这个答案,但我认为这是正确的答案。除非绝对必要,否则不要改变你的标准输出目标(也许你使用的库只输出到标准输出??这里显然不是这样)。
我认为作为一个好习惯,你应该提前准备好你的数据作为一个字符串,然后打开你的文件,并立即写入整个东西。这是因为输入/输出操作打开文件句柄的时间越长,该文件发生错误的可能性就越大(文件锁定错误、i/o错误等)。只要在一次操作中完成所有操作,就不会有什么时候可能出错的问题。
这里有一个例子:
out_lines = []
for bamfile in bamfiles:
filename = bamfile.split('/')[-1]
out_lines.append('Filename: %s' % filename)
samtoolsin = subprocess.Popen(["/share/bin/samtools/samtools","view",bamfile],
stdout=subprocess.PIPE,bufsize=1)
linelist= samtoolsin.stdout.readlines()
print 'Readlines finished!'
out_lines.extend(linelist)
out_lines.append('\n')
然后,当你收集了你的“数据线”,每个列表项一行,你可以用一些'\n'字符连接它们,使整个东西可输出;甚至可以将输出语句包装在with块中,以获得额外的安全性(即使出现错误,也会自动关闭输出句柄):
out_string = '\n'.join(out_lines)
out_filename = 'myfile.txt'
with open(out_filename, 'w') as outf:
outf.write(out_string)
print "YAY MY STDOUT IS UNTAINTED!!!"
然而,如果你有很多数据要写,你可以一次写一块。我认为这与你的应用程序无关,但这里有另一种选择:
out_filename = 'myfile.txt'
outf = open(out_filename, 'w')
for bamfile in bamfiles:
filename = bamfile.split('/')[-1]
outf.write('Filename: %s' % filename)
samtoolsin = subprocess.Popen(["/share/bin/samtools/samtools","view",bamfile],
stdout=subprocess.PIPE,bufsize=1)
mydata = samtoolsin.stdout.read()
outf.write(mydata)
outf.close()