Python連結テキストファイル


168

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

Pythonでこれを行う「よりエレガントな」方法はありますか?


7
これはpythonではありませんが、シェルスクリプトでは次のようなことができますcat file1.txt file2.txt file3.txt ... > output.txt。Pythonでは、あなたが気に入らない場合readline()は、常にreadlines()または単にありread()ます。
jedwards

1
@jedwardsはモジュールをcat file1.txt file2.txt file3.txt使用してコマンドを実行するだけでsubprocess完了です。しかしcat、Windowsで動作するかどうかはわかりません。
Ashwini Chaudhary

5
注意として、あなたが説明する方法はファイルを読むためのひどい方法です。withステートメントを使用して、ファイルを適切に閉じていることを確認し、を使用するのではなく、ファイルを反復処理して行を取得しf.readline()ます。
Gareth Latty、2012年

テキストファイルがユニコードの場合、@ jedwards catは機能しません。
Avi Cohen

回答:


258

これでうまくいくはずです

大きなファイルの場合:

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はとにかくそれを処理する必要があります。面白かったと思いました


9
これは、大きなファイルの場合、メモリ効率が非常に悪くなります。
Gareth Latty、2012年

1
@ inspectorG4dget:私はあなたに尋ねていませんでした、私はeyquemに尋ねていました、誰があなたの解決策が効率的ではないと不満を言っていました。OPのユースケース、およびeyquemが想定しているどのユースケースに対しても、それが十分に効率的であることは間違いありません。彼がそうではないと考えている場合、それを最適化するよう要求する前にそれを証明するのは彼の責任です。
abarnert 2012年

2
大きなファイルをどのように考えていますか?
ディー

4
@dee:内容がメインメモリに収まらないほど大きなファイル
inspectorG4dget

7
繰り返しますが、これは間違った答えです。shutil.copyfileobjが正しい答えです。
ポールクローリー

193

を使用しshutil.copyfileobjます。

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

import shutil

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)

2
for i in glob.glob(r'c:/Users/Desktop/folder/putty/*.txt'):よく私はforステートメントを置き換えて、ディレクトリ内のすべてのファイルを含めましたが、output_file100年代のGBのように非常に短時間で非常に大きくなり始めました。
R__raki__

10
EOL文字がない場合、各ファイルの最後の文字列を次のファイルの最初の文字列とマージすることに注意してください。私の場合、このコードを使用した後、完全に破損した結果が得られました。通常の結果を得るために、
copyfileobjの

1
@Thelambofgoatそれはその場合の純粋な連結ではないと思いますが、ねえ、あなたのニーズに合ったものは何でも。
HelloGoodbye

59

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

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

この使用例では、ファイルを手動で反復するよりもそれほど単純ではありませんが、他の場合では、すべてのファイルを1つのファイルのように反復する単一のイテレータがあると非常に便利です。(また、実際にfileinputすぐにそれは手段を行うのように各ファイルを閉じをする必要はありませんwithcloseそれぞれが、それはただ1行の貯蓄ではなく、契約の大きなものです。)

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


コメントに記載され、別の投稿で説明されているように、fileinputPython 2.7は指示どおりに機能しません。ここで、コードをPython 2.7準拠にするためのわずかな変更

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

@Lattyware:私が知るほとんどの人fileinputは、それが単純なsys.argv(またはoptparse/ etcの後にargsとして残されたもの)を簡単なスクリプト用の大きな仮想ファイルに変換する方法であり、それを何かに使用することを考えていないと言われていると思いますそれ以外の場合(つまり、リストがコマンドライン引数でない場合)。または、彼らは学びますが、忘れます—私は毎年または2年ごとにそれを再発見し続けます
abarnert '28

1
@abament私for line in fileinput.input()はこの特定のケースで選択する最良の方法ではないと思います:OPはファイルを連結することを望んでおり、実行するのに理論的に長いプロセスである行
ごとに

1
@eyquem:実行するのに長いプロセスではありません。ご指摘のとおり、行ベースのソリューションは一度に1文字を読み取りません。それらはチャンクで読み込み、バッファから行を引き出します。I / O時間は、行の解析時間を完全に圧倒します。そのため、実装者がバッファリングでひどく愚かなことをしない限り、それは同じくらい高速です(そして、適切なバッファで推測しようとするよりも高速かもしれません)自分でサイズを設定します(10000が適切な選択である場合)。
abarnert 2012年

1
@abarnert NO、10000は良い選択ではありません。2の累乗ではなく、途方もなく小さいサイズであるため、実際には非常に悪い選択です。2097152(2 21)、16777216(2 24)、さらには134217728(2 ** 27)が適切なサイズですが、4 GBのRAMでは128 MBは何もありません。
eyquem

2
サンプルコードではない、非常に有効なPython 2.7.10以降用:stackoverflow.com/questions/30835090/...
CnrL

8

私は優雅さについて知りませんが、これはうまくいきます:

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

8
ループを回避することもできます。osをインポートします。os.system( "cat file * .txt >> OutFile.txt")
lib

6
クロスプラットフォームではなく、ファイル名にスペースが含まれていると壊れます
ヒツジの飛行2015

3
これは安全ではありません。また、catファイルのリストを取得できるため、繰り返し呼び出す必要はありません。あなたは簡単に呼び出すことによって、それが安全に作ることができるsubprocess.check_call代わりにos.system
クレマン

5

UNIXコマンドの何が問題になっていますか?(Windowsで作業していない場合):

ls | xargs cat | tee output.txt 仕事をします(あなたが望むなら、あなたはそれをサブプロセスでPythonから呼び出すことができます)


21
これはpythonに関する質問だからです。
ObscureRobot

2
一般的には何も問題はありませんが、この答えは正しくありません(lsの出力をxargsに渡さないでくださいcat * | tee output.txt。ファイルのリストを直接catに渡してください:)。
クレメント2017年

それもファイル名を挿入できるならそれは素晴らしいでしょう。
2018年

@Deqing入力ファイル名を指定するには、cat file1.txt file2.txt | tee output.txt
GoTrained

1
...そして1> /dev/null、コマンドの最後に追加することで、標準出力(ターミナルでの印刷)への送信を無効にすることができます
GoTrained

4
outfile.write(infile.read()) # time: 2.1085190773010254s
shutil.copyfileobj(fd, wfd, 1024*1024*10) # time: 0.60599684715271s

シンプルなベンチマークは、shutilのパフォーマンスが高いことを示しています。


3

@ inspectorG4dget回答の代替手段(日付29-03-2016に対する最良の回答)。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()

アイデアは、「古き良き技術」を利用してバッチファイルを作成して実行することです。その半パイソンがより速く動作します。Windowsで動作します。


3

ディレクトリに多数のファイルがある場合は、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')

2

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/も最速です。


10
これにより、ファイルのサイズによっては、使用可能なメモリよりも大きくなる可能性のある巨大な文字列が生成されます。Pythonはファイルへの簡単な遅延アクセスを提供するため、これは悪い考えです。
Gareth Latty、2012年

2

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

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)


@Lattyware実行が速いと確信しているからです。ちなみに、実際には、コードがファイルを1行ずつ読み取るように命令した場合でも、ファイルはチャンクで読み取られ、キャッシュに入れられ、各行が次々に読み取られます。より適切な手順は、読み取りチャンクの長さをキャッシュのサイズと同じにすることです。しかし、私はこのキャッシュのサイズを決定する方法がわかりません。
2012年

これはCPythonでの実装ですが、それは保証されていません。このように最適化することは悪いアイデアです。システムによっては効果があるかもしれませんが、他のシステムでは効果がないかもしれないからです。
Gareth Latty、2012年

1
はい、もちろん行ごとの読み取りはバッファリングされます。それがまさにそれほど遅くない理由です。(実際、場合によっては、Pythonをプラットフォームに移植した人が10000よりもはるかに優れたチャンクサイズを選択したため、わずかに高速になることもあります。)これのパフォーマンスが本当に重要な場合は、さまざまな実装をプロファイルする必要があります。ただし、99.99…%の確率で、どちらの方法も十分に高速であるか、実際のディスクI / Oが遅い部分であり、コードの動作には関係ありません。
abarnert 2012年

また、バッファリングを手動で最適化する必要がある場合は、os.openand を使用する必要があります。os.readプレーンopenはCのstdioの周りでPythonのラッパーを使用するため、1つまたは2つの追加バッファーが邪魔になるためです。
abarnert

PS、なぜ10000が悪いのか:あなたのファイルはおそらくディスク上にあり、ブロックはバイトの累乗です。4096バイトであるとしましょう。したがって、10000バイトを読み取るとは、2つのブロックを読み取ってから、次のブロックの一部を読み取ることを意味します。さらに10000を読み取るとは、次の残りのブロック、2つのブロック、次のブロックの一部を読み取ることを意味します。あなたが持っている部分的または完全なブロック読み取りの数を数えてください、そしてあなたは多くの時間を無駄にしています。さいわい、Python、stdio、ファイルシステム、カーネルのバッファリングとキャッシングにより、これらの問題のほとんどが隠されますが、なぜ最初からそれらを作成しようとするのでしょうか。
abarnert 2012年

0
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 __name__ == "__main__":
    concatFiles()

-2
  import os
  files=os.listdir()
  print(files)
  print('#',tuple(files))
  name=input('Enter the inclusive file name: ')
  exten=input('Enter the type(extension): ')
  filename=name+'.'+exten
  output_file=open(filename,'w+')
  for i in files:
    print(i)
    j=files.index(i)
    f_j=open(i,'r')
    print(f_j.read())
    for x in f_j:
      outfile.write(x)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.