Help us understand the problem. What is going on with this article?

大きいサイズのテキストファイルをソートする

More than 3 years have passed since last update.

普通はこう?

普通は以下の方法ですが、ファイルサイズ分のメモリを消費してしまいます。

with open('/path/to/not-sorted-file', 'r') as fr:
    new_lines = sorted(fr.readlines()) # ファイルサイズ分メモリに展開

with open('/path/to/sorted-file', 'wb') as fw:
    fw.write(''.join(new_lines))

大きいサイズに対応する

関数を書きました。大体↓のような事をしてます。

  1. ファイルを行毎に読む
  2. 行毎に以下を取っておいて、1 listにまとめる
    • 各行のfile offset値
    • ソートの比較条件にする各行のsubstring
  3. #2のlistをソートする
  4. ファイル fr を開き直す
  5. 書き込み様のファイル fw を開く
  6. #3のlistをforeachし、ファイル fr の位置をfile offset値の箇所に移動する
  7. line = fr.readline() する
  8. fw.write(line) する

この方法だと "substring x 行数" 分はメモリを消費してしまうのが、いただけないところ (;_;)

import os
import uuid
import tempfile

def sort_large_file(filename, key=lambda l: l[:5]):
    '''
    sort large file without on-memory.

    :param str filename: abspath of file.
    :param function key: the function makes sort-key from a line.
    '''
    # ソート前ファイルを退避する.
    tmpname = os.path.join(tempfile.gettempdir(), 'sortlargefile_%s' % (uuid.uuid4().get_hex()))
    os.rename(filename, tmpname)

    # make a list of offsets.
    offset_list = []
    with open(tmpname, 'r') as fr:
        while True:
            offset = fr.tell()
            line = fr.readline()
            if not line:
                break

            keyword = key(line)
            offset_list.append((keyword, offset, ))

    # sort offsets.
    offset_list.sort(key=lambda e: e[0])

    # sort (write to new file).
    with open(filename, 'wb') as fw, open(tmpname, 'r') as fr:
        for keyword, offset in offset_list:
            fr.seek(offset)
            line = fr.readline()
            fw.write(line)

    # remove tmp.
    os.remove(tmpname)

実際に試してみる

以下の様に関数を呼びます。

  • 第1引数はソート対象ファイルのパスです。
  • 第2引数は「1行を引数にとって、ソートの比較条件になるsubstringを返す」関数です。
    • ここでは、CSV行の1列目の要素を切り出すlambdaを与えています。
> sort_large_file('/path/to/your/file', lambda l: l[:l.find(',')])

↓もとのCSVです。

2016-10-01,apple,red
2016-09-29,orange,orange
2015-12-21,banana,yellow

sort_large_file()関数は最終行にも改行が必要。

↓ソートされてこうなります。

2015-12-21,banana,yellow
2016-09-29,orange,orange
2016-10-01,apple,red

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away