web-dev-qa-db-ja.com

Pythonはテキストファイルを連結する

['file1.txt', 'file2.txt', ...]のように、20個のファイル名のリストがあります。これらのファイルを新しいファイルに連結するためのPythonスクリプトを書きたいと思います。 f = open(...)で各ファイルを開き、f.readline()を呼び出して1行ずつ読み込み、その新しいファイルに1行ずつ書き込むことができます。それは私にとって非常に「優雅」には思えません。

Pythonでこれを行うためのもっと "エレガントな"方法はありますか?

140
JJ Beck

これはそれをするべきです

大きなファイルの場合

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for fname in filenames:
        with open(fname) as infile:
            for line in infile:
                outfile.write(line)

小さなファイルの場合

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for fname in filenames:
        with open(fname) as infile:
            outfile.write(infile.read())

…そして私が考えていたもう一つの興味深いもの

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for line in itertools.chain.from_iterable(itertools.imap(open, filnames)):
        outfile.write(line)

残念ながら、この最後の方法ではいくつかのオープンファイル記述子が残りますが、GCはそれを処理します。おもしろいと思った

217
inspectorG4dget

shutil.copyfileobjを使用してください。

入力ファイルを自動的にチャンクごとに自動的に読み込みます。これはより効率的で入力ファイルを読み込むためのもので、一部の入力ファイルが大きすぎてメモリに収まらなくても機能します。

with open('output_file.txt','wb') as wfd:
    for f in ['seg1.txt','seg2.txt','seg3.txt']:
        with open(f,'rb') as fd:
            shutil.copyfileobj(fd, wfd)
153
Meow

まさにそれが fileinput の目的です。

import fileinput
with open(outfilename, 'w') as fout, fileinput.input(filenames) as fin:
    for line in fin:
        fout.write(line)

このユースケースでは、ファイルを手動で反復するよりも実際にはそれほど簡単ではありませんが、単一のファイルであるかのようにすべてのファイルを反復する単一の反復子を持つことが非常に便利です。 (また、fileinputが完了するとすぐに各ファイルを閉じるという事実は、それぞれのwithまたはcloseが不要であることを意味しますが、それは単なる1行の節約であり、それほど大きな問題ではありません。)

fileinputには他にも気の利いた機能がいくつかあります。たとえば、各行をフィルタリングするだけでファイルのインプレース変更を行うことができます。


コメントで述べられているように、そして別の post で議論されているように、Python 2.7のfileinputは示されているようには動きません。ここでコードをPython 2.7準拠にするためのわずかな修正

with open('outfilename', 'w') as fout:
    fin = fileinput.input(filenames)
    for line in fin:
        fout.write(line)
    fin.close()
51
abarnert

私は優雅さについて知りません、しかしこれは働きます:

    import glob
    import os
    for f in glob.glob("file*.txt"):
         os.system("cat "+f+" >> OutFile.txt")
8
Daniel

UNIXコマンドの何が問題になっていますか(あなたがWindowsで働いていないとすれば):

ls | xargs cat | tee output.txtが仕事をします(必要であればサブプロセスを使ってpythonから呼び出すこともできます)。

5
lucasg

@ inspectorG4dgetの代わりになる(2016年3月29日現在の最良の解答)。私は436MBの3つのファイルでテストしました。

@ inspectorG4dgetの解決策:162秒

次の解決策:125秒

from subprocess import Popen
filenames = ['file1.txt', 'file2.txt', 'file3.txt']
fbatch = open('batch.bat','w')
str ="type "
for f in filenames:
    str+= f + " "
fbatch.write(str + " > file4results.txt")
fbatch.close()
p = Popen("batch.bat", cwd=r"Drive:\Path\to\folder")
stdout, stderr = p.communicate()

「古き良き技術」を利用して、バッチファイルを作成して実行するという考えです。そのセミpythonがより速く動作します。窓のために働きます。

2
João Palma

Fileオブジェクトの.read()メソッドを調べてください。

http://docs.python.org/2/tutorial/inputoutput.html#methods-of-file-objects

次のようなことができます。

concat = ""
for file in files:
    concat += open(file).read()

あるいはもっと '優雅な' python-way:

concat = ''.join([open(f).read() for f in files])

これは、この記事によると: http://www.skymind.com/~ocrow/python_string/ も最速だろう。

2
Alex Kawrykow
outfile.write(infile.read()) 2.1085190773010254s
shutil.copyfileobj(fd, wfd, 1024*1024*10) 0.60599684715271s

簡単なベンチマークは、shutilのパフォーマンスが優れていることを示しています。

2
haoming

ファイルが巨大ではない場合:

with open('newfile.txt','wb') as newf:
    for filename in list_of_files:
        with open(filename,'rb') as hf:
            newf.write(hf.read())
            # newf.write('\n\n\n')   if you want to introduce
            # some blank lines between the contents of the copied files

ファイルが大きすぎて完全に読み取ってRAMに保持することができない場合、ループ内でコピーされる各ファイルをread(10000)などの固定長のまとまりで読み取るには、アルゴリズムが少し異なる必要があります。

1
eyquem

ディレクトリにたくさんのファイルがある場合、glob2は、手書きではなく、ファイル名のリストを生成するのに適した方法です。

import glob2

filenames = glob2.glob('*.txt')  # list of all .txt files in the directory

with open('outfile.txt', 'w') as f:
    for file in filenames:
        with open(file) as infile:
            f.write(infile.read()+'\n')
1
Sharad
def concatFiles():
    path = 'input/'
    files = os.listdir(path)
    for idx, infile in enumerate(files):
        print ("File #" + str(idx) + "  " + infile)
    concat = ''.join([open(path + f).read() for f in files])
    with open("output_concatFile.txt", "w") as fo:
        fo.write(path + concat)

if __== "__main__":
    concatFiles()
0
user2825287

これはPython 3の表現です。

from pathlib import Path

filenames = ['file1.txt', 'file2.txt', ...]
output_file = Path('output_file.txt')
for file in file_list:
    output_file.write_text(Path(file).read_text())
0
Back2Basics