'Print' çıktısını python kullanarak bir dosyaya nasıl yönlendirebilirim?


184

Yazdırmayı python kullanarak bir .txt dosyasına yeniden yönlendirmek istiyorum. TÜM bu çıktıyı tek bir dosyaya yeniden yönlendirmek istediğinizde '.' Döngümün her biri için çıktıyı yazdıracak bir 'for' döngüsü var. Bu yüzden koymaya çalıştım

 f = open('output.txt','w'); sys.stdout = f

senaryomun başında. Ancak .txt dosyasında hiçbir şey almıyorum. Benim senaryom:

#!/usr/bin/python

import os,sys
import subprocess
import glob
from os import path

f = open('output.txt','w')
sys.stdout = f

path= '/home/xug/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!'
    ........print....
    ........print....

Peki sorun ne? Bu sys.stdout dışında başka bir yol var mı?

Benim sonuç bakmak gerekir:

Filename: ERR001268.bam
Readlines finished!
Mean: 233
SD: 10
Interval is: (213, 252)

7
Neden kullanmıyorsunuz f.write(data)?
Eran Zimmerman Gönen

evet, ama her bam dosyası için birkaç veri var (ortalama, SD, aralık ...), nasıl bu verileri tek tek koyabilirim?
LookIntoEast

f.write(line)- sonuna bir satır sonu ekler.
Eran Zimmerman Gönen

8
@Eran Zimmerman: Verilere f.write(line)satır sonu eklemez.
hughdbrown

Haklısın, benim hatam. f.write(line+'\n')Ancak, her zaman olabilir ..
Eran Zimmerman Gonen

Yanıtlar:


274

Bunu yapmanın en belirgin yolu bir dosya nesnesine yazdırmak olacaktır:

with open('out.txt', 'w') as f:
    print >> f, 'Filename:', filename     # Python 2.x
    print('Filename:', filename, file=f)  # Python 3.x

Ancak, stdout'u yeniden yönlendirmek de benim için çalışıyor. Muhtemelen böyle bir kerelik bir komut dosyası için iyidir:

import sys

orig_stdout = sys.stdout
f = open('out.txt', 'w')
sys.stdout = f

for i in range(2):
    print 'i = ', i

sys.stdout = orig_stdout
f.close()

Kabuğun kendisinden dışarıdan yönlendirme başka bir iyi seçenektir:

./script.py > out.txt

Diğer sorular:

Senaryonuzdaki ilk dosya adı nedir? Başladığını görmüyorum.

İlk tahminim, glob'un herhangi bir bamfile bulamadığı ve bu nedenle for döngüsü çalışmadığıdır. Klasörün var olup olmadığını kontrol edin ve komut dosyasındaki bamfiles dosyalarını yazdırın.

Ayrıca yolları ve dosya adlarını değiştirmek için os.path.join ve os.path.basename öğelerini kullanın.


Kodunuzun 8. satırı dosya adı adlı bir değişken kullanıyor, ancak henüz oluşturulmadı. Daha sonra döngüde tekrar kullanırsınız, ancak alakalı değildir.
Gringo Suave

2
Gerekmiyorsa sys.stdout'u değiştirmek için kötü uygulama.
makine özlemi

3
@ benim böyle basit bir script için kötü olduğuna ikna olmadım.
Gringo Suave

4
+1 Haha benim oyumu yapabilirsin çünkü kesinlikle yanlış şekilde yapman gerekiyorsa bunu yapmanın doğru yolu ... Ama yine de düzenli dosya çıktısı ile yapman gerektiğini söylüyorum.
makine özlemi

1
Çıktı konsolda nasıl yeniden yönlendirilir ve yazdırılır? Stdrr yeniden yönlendirildiğinde Python'daki "print ()" gösterilemiyor mu?
exteral

70

Baskıyı >>operatörle yeniden yönlendirebilirsiniz .

f = open(filename,'w')
print >>f, 'whatever'     # Python 2.x
print('whatever', file=f) # Python 3.x

Çoğu durumda, dosyaya normal olarak yazmanız daha iyi olur.

f.write('whatever')

veya aralarında boşluklar ile yazmak istediğiniz birkaç öğeniz varsa, örneğin print:

f.write(' '.join(('whatever', str(var2), 'etc')))

2
Çok sayıda çıktı bildirimi varsa bunlar eski haline gelebilir. Posterlerin orijinal fikri geçerlidir; senaryoda başka bir sorun var.
Gringo Suave

1
Posterin orijinal fikri kesinlikle geçersiz. Stdout'u buraya yönlendirmek için bir neden yok, çünkü verileri zaten bir değişkene alıyor.
makine özlemi

Bence "teknik olarak geçerli" demekti, aslında sys.stdoutiyi bir fikir olduğunu değil yönlendirebilirsin .
agf

35

Python 2 veya Python 3 API referansı:

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

Dosya bağımsız değişkeni içeren bir nesne olmalıdır write(string)yöntem; Mevcut değilse veya None, sys.stdoutkullanılacaktır. Yazdırılan bağımsız değişkenler metin dizelerine dönüştürüldüğünden, print()ikili mod dosya nesneleriyle kullanılamaz. Bunlar için file.write(...)kullanın.

Yana dosya nesnesi normalde içeren write()yöntemi, yapmanız gereken tek şey bir geçmektir dosya nesnesini onun argüman haline.

Dosyaya Yazma / Üzerine Yazma

with open('file.txt', 'w') as f:
    print('hello world', file=f)

Dosyaya Yazma / Dosyaya Ekleme

with open('file.txt', 'a') as f:
    print('hello world', file=f)

2
Daha önceki cevapların bazılarının neden dünyayı maymuna dönüştürmek olduğunu karıştırdım sys.stdout:(
Yeo

35

Mükemmel çalışıyor:

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

Şimdi merhaba test.txt dosyasına yazılır. Kapatmak için emin olun stdoutbir ile closedosyadaki tasarruf olmayacak onsuz, içeriği


3
ancak gerçekleştirsek bile sys.stdout.close(), python kabuğuna bir şey ValueError: I/O operation on closed file. yazarsanız, hata imgur.com/a/xby9P olarak gösterilir . Bunu idare etmenin en iyi yolu @Gringo Suave'in kaydettiği şeydir
Mourya

24

Kullanma print, kullanmalogging

sys.stdoutBir dosyayı işaret edecek şekilde değiştirebilirsiniz , ancak bu, bu sorunu ele almanın oldukça karmaşık ve esnek olmayan bir yoludur. Kullanmak yerine modülü printkullanın logging.

İle loggingaynen yaptığınız gibi yazdırabilir stdoutveya çıktıyı bir dosyaya yazabilirsiniz. Hatta (farklı mesaj seviyeleri kullanabilir critical, error, warning, info, debug), örneğin, sadece konsola ana meseleleri yazdırmak için, ama yine de bir dosyaya küçük kod eylemleri log.

Basit bir örnek

İşleme seviyesini içe aktarın logging, alın loggerve ayarlayın:

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # process everything, even if everything isn't printed

Stdout'a yazdırmak istiyorsanız:

ch = logging.StreamHandler()
ch.setLevel(logging.INFO) # or any other level
logger.addHandler(ch)

Bir dosyaya da yazmak istiyorsanız (yalnızca bir dosyaya yazmak istiyorsanız son bölümü atlayın):

fh = logging.FileHandler('myLog.log')
fh.setLevel(logging.DEBUG) # or any level you want
logger.addHandler(fh)

Ardından, nerede kullanırsanız printkullanın loggeryöntemlerden birini kullanın :

# 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')

Daha gelişmiş loggingözellikleri kullanma hakkında daha fazla bilgi edinmek loggingiçin Python belgelerindeki mükemmel eğitimi okuyun .


Merhaba, bu günlük kaydını konsol verisini günlük dosyasına, verilerin alındığı saatte yazmak için kullanmak istiyorum. Ancak günlük işlevini veya kitaplığını düzgün anlayamıyorum. Bana bu konuda yardım edebilir misin
Haris

@haris Python belgelerinin günlüğe kaydetme eğiticisini okuyun ve Yığın Taşması ile ilgili diğer sorulardaki örnekleri inceleyin (birçoğu vardır). Hâlâ çalışmaya devam edemiyorsanız yeni bir soru sorun.
jpyams

12

En kolay çözüm python ile değil; kabuğun içinden. Dosyanızın ilk satırından ( #!/usr/bin/python) UNIX sisteminde olduğunuzu tahmin ediyorum. Sadece printnormalde yaptığınız gibi ifadeler kullanın ve dosyayı betiğinizde hiç açmayın. Dosyayı çalıştırmak için gittiğinizde,

./script.py

dosyayı çalıştırmak için şunu kullanın:

./script.py > <filename>

<filename>çıktının girmesini istediğiniz dosyanın adıyla değiştirirsiniz . >Belirteci aşağıdaki belirteç ile tarif dosyaya grubu Stdout'a (en) kabukları anlatır.

Burada belirtilmesi gereken önemli bir şey, "script.py" nin çalıştırılabilmesi için ./script.pyçalıştırılabilir hale getirilmesi gerektiğidir .

Bu yüzden çalıştırmadan önce ./script.pybu komutu yürütün

chmod a+x script.py (komut dosyasını tüm kullanıcılar için yürütülebilir hale getirin)


3
./script.py> <filename> 2> & 1 stderr'i de yakalamanız gerekir. 2> & 1 bunu yapacak
rtaft

1
@rtaft Neden? Soru, özel printolarak bir dosyanın çıktısını oluşturmak istiyor . Stdout'un (yığın izleri ve benzerleri) hala terminale yazdırılmasını beklemek mantıklı olacaktır.
Aaron Dufour

Bunun işe yaramadığını, benim de işe yaramadığını söyledi. Daha sonra üzerinde çalıştığım bu uygulamanın her şeyi stderr ... idk'e yönlendirecek şekilde yapılandırıldığını keşfettim.
rtaft

5

Linux kullanıyorsanız teekomutu kullanmanızı öneririm . Uygulama şu şekildedir:

python python_file.py | tee any_file_name.txt

Koddaki hiçbir şeyi değiştirmek istemiyorsanız, bunun mümkün olan en iyi çözüm olabileceğini düşünüyorum. Günlükçü de uygulayabilirsiniz, ancak kodda bazı değişiklikler yapmanız gerekir.


1
harika; arıyordu
Vicrobot

4

Bu yanıtı beğenmeyebilirsiniz, ama bence bu doğru. Kesinlikle gerekli olmadıkça stdout hedefinizi değiştirmeyin (belki sadece stdout için çıktı veren bir kütüphane kullanıyorsunuz ??? burada açıkça değil).

İyi bir alışkanlık olarak, verilerinizi bir dize olarak önceden hazırlamalı, sonra dosyanızı açmalı ve her şeyi aynı anda yazmalısınız. Bunun nedeni, giriş / çıkış işlemlerinin dosya tanıtıcısı ne kadar uzun süre açık kalmasıdır, bu dosyada (dosya kilitleme hatası, i / o hatası, vb.) Bir hata oluşması daha olasıdır. Hepsini tek bir işlemde yapmak, ne zaman yanlış gittiğine dair bir soru bırakmaz.

İşte bir örnek:

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')

Ve sonra "veri satırlarınızı" liste öğesi başına bir satır '\n'toplamayı tamamladığınızda, her şeyi çıktı haline getirmek için bunlara bazı karakterlerle katılabilirsiniz; hatta withek güvenlik için çıktı ifadenizi bir bloğa sarın (bir şeyler ters giderse bile çıktı tutamacınızı otomatik olarak kapatır):

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!!!"

Eğer yazma veri çok var ancak bunu verebilir bunu bir seferde bir parça yazmak. Başvurunuzla alakalı olduğunu düşünmüyorum, ancak alternatif:

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()

1
Disk önbellekleme ile orijinalin performansı kabul edilebilir olmalıdır. Ancak bu çözüm, çok fazla çıktı varsa, bellek gereksinimlerini balonlamak gibi bir dezavantaja sahiptir. Muhtemelen burada endişelenecek bir şey olmamasına rağmen, mümkünse bundan kaçınmak genellikle iyi bir fikirdir. Aralık vb. Yerine xrange (py3 aralığı) kullanmakla aynı fikir
Gringo Suave

@Gringo: Bu gereksinimi belirtmedi. Nadiren bir dosyaya bununla ilgili yeterli veri yazıyorum. Bu xrange ile aynı fikir değildir, çünkü xrange i / o dosyasıyla ilgilenmez. Disk önbelleğe alma yardımcı olabilir, ancak büyük bir kod gövdesi için dosya tanıtıcısını açık tutmak hala kötü bir uygulamadır.
makine özlemi

1
Yorumunuz kendisiyle çelişiyor. Dürüst olmak gerekirse, her iki yaklaşımın da performans yönü çok büyük olmayan veriler için önemsizdir. xrange kesinlikle benzerdir, bellekte bir kerede değil, her seferinde tek parça üzerinde çalışır. Belki de bir jeneratör vs listesi daha iyi bir örnektir.
Gringo Suave

@Gringo: Yorumumun kendisiyle nasıl çeliştiğini göremiyorum. Belki de performans yönü önemli değildir, bir dosya tanıtıcısını uzun süre açık tutmak her zaman hata riskini artırır. Programlama dosyasında i / o her zaman doğal olarak kendi programınızda bir şey yapmaktan daha risklidir, çünkü işletim sistemine ulaşmak ve dosya kilitleriyle uğraşmak zorunda kalırsınız. Dosyanız ne kadar kısa olursa, dosya sistemini kodunuzdan kontrol etmemeniz daha iyidir. xrange farklı çünkü dosya i / o ile ilgisi yok ve FYI nadiren xrange kullanıyorum; şerefe
makine özlem

2
@Gringo: Eleştirilerinizi takdir ediyorum ve hararetli tartışmalardan keyif aldım. Bazı noktalarda anlaşamamış olsak da, görüşlerinizi kabul ediyorum, çünkü duruşunuzu almak için iyi bir nedeniniz var. Makul bir şekilde bitirdiğiniz için teşekkürler ve çok iyi geceler. : P
makine özlemi

2

Yeniden yönlendirme stdoutsorununuz için işe yarıyorsa , Gringo Suave'in cevabı bunun nasıl yapılacağı için iyi bir gösteri.

Daha da kolaylaştırmak için, ifadeyi kullanarak özlü bir genelleştirilmiş çağrı sözdizimi için bağlam yöneticilerini kullanan bir sürüm yaptım 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

Kullanmak için aşağıdakileri yapmanız yeterlidir (Suave'in örneğinden türetilmiştir):

with open('out.txt', 'w') as outfile:
    with redirected_stdout(outfile):
        for i in range(2):
            print('i =', i)

printBir modül onu sevmediğiniz bir şekilde kullandığında seçici olarak yeniden yönlendirmek için kullanışlıdır . Tek dezavantajı (ve bu birçok durum için dağıtıcıdır), biri farklı değerlere sahip birden çok iş parçacığı istiyorsa işe yaramaz stdout, ancak daha iyi, daha genel bir yöntem gerektirir: dolaylı modül erişimi. Bunun uygulamalarını bu sorunun diğer cevaplarında görebilirsiniz.


0

Sys.stdout değerinin değiştirilmesi, yazdırılacak tüm çağrıların hedefini değiştirir. Yazdırma hedefini değiştirmek için alternatif bir yol kullanırsanız, aynı sonucu alırsınız.

Hatanız başka bir yerde:

  • sorunuz için kaldırdığınız kodda olabilir (çağrının açılması için dosya adı nereden gelir?)
  • ayrıca verilerin temizlenmesini beklemiyor olabilirsiniz: bir terminale yazdırırsanız, veriler her yeni satırdan sonra temizlenir, ancak bir dosyaya yazdırırsanız, yalnızca stdout arabelleği dolduğunda temizlenir (4096 bayt) çoğu sistemde).

-1

Döngüler için yazdırma işlevini genişletecek bir şey

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

gerek yok ve kullanırken whiledosyayı kapatmaya gerek yokwith
Daniel Stracaboško
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.