ブログトップ 記事一覧 ログイン 無料ブログ開設

forest book

2009-07-12

直接的にコンテンツを読み込む代わりのメモリマップファイル

| 22:54 | 直接的にコンテンツを読み込む代わりのメモリマップファイル - forest book を含むブックマーク はてなブックマーク - 直接的にコンテンツを読み込む代わりのメモリマップファイル - forest book 直接的にコンテンツを読み込む代わりのメモリマップファイル - forest book のブックマークコメント

mmap - Memory-map files instead of reading the contents directly

サンプルコードのダウンロードはこちら

本稿は上記リンク元の和訳になります

本稿は Creative Commons 表示-非営利-継承 3.0でライセンスされています

転載ミス、誤訳等については適宜修正します

Creative Commons 表示-非営利-継承 3.0 アメリカ合衆国

目的: 直接的にコンテンツを読み込む代わりのメモリマップファイル

Python バージョン: 2.1以上

メモリマップファイルを作成するために mmap() 関数を使ってみましょう。UnixWindows で、以下のように議論されていない mmap() への引数と振る舞いの違いがあります。詳細については、ライブラリドキュメントを参照してください。

最初の引数 fileno は、ファイルオブジェクトの fileno() メソッドか、又は os.open() のファイルオブジェクトになりますmmap() を呼び出す前にファイルをオープンしたので、あなたはそのクローズに責任を持つ必要があります。

mmap() への2番目の引数は、マップするファイルのバイトサイズになります。もし、値がゼロならば、完全なファイルがマップされます。Windows ではゼロの長さのマッピングを作成することはできません。もし、サイズが現在のファイルサイズよりも大きいならば、ファイルは拡張されます。

オプションキーワード引数である access は、両方のプラットホームでサポートされています。read-only には ACCESS_READ を、write-through (メモリへの書き込みが直接的にファイルへ書き込まれる)には ACCESS_WRITE を、copy-on-write (ファイルへ書き込まれないメモリへの書き込み)には ACCESS_COPY を使用してください。

ファイルと文字列 API

メモリマップファイルは、必要に応じて、ファイルのようなオブジェクトや変更可能な文字列として扱うことができます。1つのマップファイルは、 close(), flush(), read(), readline(), seek(), tell() と write() のような、期待したファイル API メソッドをサポートします。それはまた、スライシングや find() メソッドのような機能で、文字列 API もサポートします。

サンプルデータ

全ての例は、少しのロレム・イプサムを含む、テキストファイル lorem.txt を使用します。参考までに、そのファイルのテキストは以下になります:

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec
egestas, enim et consectetuer ullamcorper, lectus ligula rutrum leo, a
elementum elit tortor eu quam. Duis tincidunt nisi ut ante. Nulla
facilisi. Sed tristique eros eu libero. Pellentesque vel arcu. Vivamus
purus orci, iaculis ac, suscipit sit amet, pulvinar eu,
lacus. Praesent placerat tortor sed nisl. Nunc blandit diam egestas
dui. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Aliquam viverra fringilla
leo. Nulla feugiat augue eleifend nulla. Vivamus mauris. Vivamus sed
mauris in nibh placerat egestas. Suspendisse potenti. Mauris massa. Ut
eget velit auctor tortor blandit sollicitudin. Suspendisse imperdiet
justo.

読み込み

read-only アクセスのためにファイルをマップするには、access=mmap.ACCESS_READ を引数に渡してください:

import mmap

f = open('lorem.txt', 'r')
try:
    m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    try:
        print 'First 10 bytes via read :', m.read(10)
        print 'First 10 bytes via slice:', m[:10]
        print '2nd   10 bytes via read :', m.read(10)
    finally:
        m.close()
finally:
    f.close()

この例では、ファイルポインタを前に進める read() を呼び出したとしても、ファイルポインタはリセットされたのでスライシング操作は、同じ最初の10バイトを返します。そのファイルポインタは最後のアクセスを追跡していて、2回目のために最初の10バイトを返すスライシング操作を行った後で、そのファイルの次の10バイトを返す read() を呼びます。

$ python mmap_read.py 
First 10 bytes via read : Lorem ipsu
First 10 bytes via slice: Lorem ipsu
2nd   10 bytes via read : m dolor si

書き込み

メモリマップファイルへ書き込む必要がある場合、それをマッピングする前に読み書きモード(‘w’ ではなく ‘r+’)でそれをオープンしてください。それから、データを変更する API メソッド(write() やスライスへの書き込み等)を使用してください。

ここに ACCESS_WRITE のデフォルトアクセスモードを使用して、ある行の一部を変更するスライスへ書き込みをする例があります。:

import mmap
import shutil

# サンプルファイルをコピー
shutil.copyfile('lorem.txt', 'lorem_copy.txt')

word = 'consectetuer'
reversed = word[::-1]
print 'Looking for    :', word
print 'Replacing with :', reversed

f = open('lorem_copy.txt', 'r+')
try:
    m = mmap.mmap(f.fileno(), 0)
    try:
        print 'Before:', m.readline().rstrip()
        m.seek(0) # rewind

        loc = m.find(word)
        m[loc:loc+len(word)] = reversed
        m.flush()

        m.seek(0) # rewind
        print 'After :', m.readline().rstrip()
    finally:
        m.close()
finally:
    f.close()

ご覧の通り、最初の行の中間にある単語は置換されました。

$ python mmap_write_slice.py 
Looking for    : consectetuer
Replacing with : reutetcesnoc
Before: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec
After : Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit. Donec

ACCESS_COPY モード

ディスク上のファイルに変更を書き込まないようにするには ACCESS_COPY モードを使用してください。

import mmap
import shutil

# サンプルファイルをコピー
shutil.copyfile('lorem.txt', 'lorem_copy.txt')

word = 'consectetuer'
reversed = word[::-1]

f = open('lorem_copy.txt', 'r+')
try:
    m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY)
    try:
        print 'Memory Before:', m.readline().rstrip()
        print 'File Before  :', f.readline().rstrip()
        print

        m.seek(0) # rewind
        loc = m.find(word)
        m[loc:loc+len(word)] = reversed

        m.seek(0) # rewind
        print 'Memory After :', m.readline().rstrip()

        f.seek(0)
        print 'File After   :', f.readline().rstrip()

    finally:
        m.close()
finally:
    f.close()

この例では、mmap ファイルハンドラとは別にファイルハンドラを巻き戻す必要があったのに注意してください。

$ python mmap_write_copy.py 
Memory Before: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec
File Before  : Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec

Memory After : Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit. Donec
File After   : Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec

正規表現

メモリマップファイルは文字列のように動作するので、正規表現のような、文字列の操作をする他のモジュールと一緒に使用することができます。この例は “nulla” を含む全ての文を見つけます。

import mmap
import re

pattern = re.compile(r'(\.\W+)?([^.]?nulla[^.]*?\.)',
                     re.DOTALL | re.IGNORECASE | re.MULTILINE)

f = open('lorem.txt', 'r')
try:
    m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    try:
        for match in pattern.findall(m):
            print match[1].replace('\n', ' ')
    finally:
        m.close()
finally:
    f.close()

2つのグループを含むパターンなので、findall() からの戻り値はタプルのシーケンスになります。print 文は、改行をスペースに置換した match の文を取り出して、その結果は1行で表示されます。

$ python mmap_regex.py 
Nulla facilisi.
Nulla feugiat augue eleifend nulla.

関連項目

mmap
   mmap の標準ライブラリドキュメント
os(未訳)
   os モジュール