我想使用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)

当前回答

在python 3中,你可以重新赋值print:

#!/usr/bin/python3

def other_fn():
    #This will use the print function that's active when the function is called
    print("Printing from function")

file_name = "test.txt"
with open(file_name, "w+") as f_out:
    py_print = print #Need to use this to restore builtin print later, and to not induce recursion
   
    print = lambda out_str : py_print(out_str, file=f_out)
    
    #If you'd like, for completeness, you can include args+kwargs
    print = lambda *args, **kwargs : py_print(*args, file=f_out, **kwargs)
    
    print("Writing to %s" %(file_name))

    other_fn()  #Writes to file

    #Must restore builtin print, or you'll get 'I/O operation on closed file'
    #If you attempt to print after this block
    print = py_print

print("Printing to stdout")
other_fn() #Writes to console/stdout

注意,来自other_fn的print仅切换输出,因为print在全局作用域中被重新赋值。如果在函数中赋值print, other_fn中的print通常不受影响。如果我们想影响所有的打印调用,我们可以使用global关键字:

import builtins

def other_fn():
    #This will use the print function that's active when the function is called
    print("Printing from function")

def main():
    global print #Without this, other_fn will use builtins.print
    file_name = "test.txt"
    with open(file_name, "w+") as f_out:

        print = lambda *args, **kwargs : builtins.print(*args, file=f_out, **kwargs)

        print("Writing to %s" %(file_name))

        other_fn()  #Writes to file

        #Must restore builtin print, or you'll get 'I/O operation on closed file'
        #If you attempt to print after this block
        print = builtins.print

    print("Printing to stdout")
    other_fn() #Writes to console/stdout

就我个人而言,我更倾向于通过将输出文件描述符烘焙到一个新函数中来避免使用print函数的要求:

file_name = "myoutput.txt"
with open(file_name, "w+") as outfile:
    fprint = lambda pstring : print(pstring, file=outfile)
    print("Writing to stdout")
    fprint("Writing to %s" % (file_name))

其他回答

这是完美的:

import sys
sys.stdout=open("test.txt","w")
print ("hello")
sys.stdout.close()

现在hello将被写入test.txt文件。确保用close结束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()

如果重定向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值的线程,它就不起作用,但这需要一种更好、更通用的方法:间接模块访问。你可以在这个问题的其他答案中看到它的实现。

它扩展了循环的打印函数

x = 0
while x <=5:
    x = x + 1
    with open('outputEis.txt', 'a') as f:
        print(x, file=f)
    f.close()

我在过去用来输出一些字典的东西如下:

# sample dictionary
the_dict = {'a': 'no', 'c': 'yes', 'b': 'try again'}

# path to output to
dict_path = "D:/path.txt"

# script to output file
with open(dict_path, "w") as f:
    for idx, data in the_dict.items():
        print(idx, data, file=f)

输出的文件如下所示:

a no
c yes
b try again