2017-06-15 42 views
20

ファイルのフォルダ(20 MBから100 MBの範囲のサイズ)を読み込み、各行のデータを変更し、ファイルのコピーPython writelines()とwrite()の巨大な時間差

このコードを90 MBファイル(〜900,000行)で実行すると、ファイルに書き込むのにかかる時間として140秒が表示されました。ここではwritelines()を使用しました。そこで、ファイルの書き込み速度を向上させるさまざまな方法を探しました。ほとんどの記事では、write()writelines()は、1つの連結文字列を書いているので、何の違いも見られません。私はまた、唯一の次のステートメントにかかる時間がチェック:

new_string = '\n'.join(new_my_list) + '\n' 

をそして、それはわずか0.4秒を要したので、撮影した大規模な時間があるため、リストを作成するではなかったです。 だけwrite()を試して、私はこのコードを試してみました:

with open(inputPath, 'r+') as myRead: 
    my_list = myRead.readlines() 
    new_my_list = clean_data(my_list) 
with open(outPath, 'w+') as myWrite: 
    tempT = time.time() 
    myWrite.write('\n'.join(new_my_list) + '\n') 
    print(time.time() - tempT) 
print(inputPath, 'Cleaning Complete.') 

をそして、それは2.5秒を印刷しました。なぜ同じデータでも、write()writelines()のファイル書き込み時間に大きな違いはありますか?これは正常な動作ですか、あるいは私のコードに何か間違っていますか?どちらの場合も出力ファイルは同じように見えるので、データが失われていないことがわかります。

+2

upvote。 –

+0

また、私のclean_data()関数は各行を取り除きますので、余分な改行は削除されます。 –

答えて

37

file.writelines()は、の反復可能文字列のを想定しています。その後、ループして、イテレート可能な各文字列に対してfile.write()を呼び出します。 Pythonでは、このメソッドはこれを行います:

def writelines(self, lines) 
    for line in lines: 
     self.write(line) 

あなたは1つの大きな文字列を渡しており、文字列も文字列の繰り返し可能です。あなたが反復するとき個々の文字、長さ1の文字列を得る。len(data)file.write()に別々に呼び出すと効果的です。また、書き込みバッファを一度に1文字ずつ作成するので、それは遅いです。

file.writelines()に1つの文字列を渡さないでください。代わりにリストやタプルなどの反復可能なものを渡します。

あなたは、たとえば、ジェネレータ式に追加改行で個々の行に送ることができます:あなたがからclean_data()発電、洗浄ラインをもたらす、あなたはストリーミングができ、データを作ることができれば、今

myWrite.writelines(line + '\n' for line in new_my_list) 

あなたのデータクリーニングジェネレータを介して入力ファイルを読み込み、読み込みバッファと書き込みバッファに必要以上のメモリを使用せずに出力ファイルに出力します。

with open(inputPath, 'r+') as myRead, open(outPath, 'w+') as myWrite: 
    myWrite.writelines(line + '\n' for line in clean_data(myRead)) 

さらに、clean_data()を更新して、改行を含む行を出力することも考えています。

マルタインの答えを補完するものとして
+0

'myWrite.writelines( '\ n'.join(my_list)+' \ n ')'はmyWrite.writelines( "{} \ n" my_listのxに対して.format(x)さらに速くなる。ビルドするリストはありません。 –

+0

@ Jean-FrançoisFabre:リストまたはタプル*やその他の繰り返し可能なものを渡すように指定している理由です。 :-) –

+0

@ Jean-FrançoisFabre:バッファがいっぱいになるまで、バッファはまだそれらの行を連結するので、メモリ節約の尺度かもしれません。 'clean_data()'がジェネレータだった場合に役立ちます。 –

2

'write(arg)'メソッドは文字列を引数として受け取ります。一度それを呼び出すと、それは直接書きます。これははるかに速い理由です。 writelines()メソッドを使用している場合と同じように、iteratorとしてstringのリストが必要です。したがって、たとえあなたがwritelinesにデータを送信しても、それはイテレータを取得しているとみなし、イテレータを反復しようとします。イテレータであるため、反復処理して書き込むまでには時間がかかります。

これは明確ですか?

+0

しかし、まだそれは単一の文字列ではありませんか?それは1つの値を繰り返しますか?それは書き込み速度にどのように影響しますか? –

+1

ええ、 'myWrite.writelines(['\ n'.join(my_list)+' \ n '])のようなものをお勧めします。 – mgilson

+3

@ArjunBalgovind:1つの文字列は別々の文字の繰り返し可能です。 –

5

、最良の方法は、最後に改行を追加し、最初の場所でjoinを使用してリストを構築することを避けるために

ちょうどwritelinesに発電機の理解を通過しないだろう:不要なメモリ割り当てと期待結果とwritelinesを使用するツイスト方法を見つけると、予期しない警告を求める(理解以外)なしループ

myWrite.writelines("{}\n".format(x) for x in my_list) 
関連する問題