大きなファイルを一瞬で読み込むテクニック

MB単位のファイルを読むのは.....
みなさま。どうも。ものすごくお久しぶりです。
久々に気まぐれしてみました。というか、大学3年生になったら
気まぐれどころか、ホームページすら管理する暇がなくって......

最近、アルバイトの仕事でプログラムを作ることになりました。
その仕事の中で78MBものテキストファイルを扱う必要があったのですが
横着してPerlでプログラムを作って読み込もうとしたら
もう全然読み込んでくれないのです。
30分くらいほったらかしにしておいてもまだ終わらない......

限界だな。と思ってしかたなくVBでファイルを読み込ませたのですが
「遅い!遅い!遅い!」
一応は読み込みができるのですが、非常に遅いのです。

「もっと早く読み込まないと使えない!」ということで
たしか昔にAPIリファレンスの本で
「ファイルマッピングを使うと高速にファイルI/Oができる」と
書いてあったのを思い出して、早速チャレンジ!

実験してみると、なんと78MBのファイルを
あっという間に読み込めるではありませんか!?

「これは面白い。」ということで、今回の気まぐれは
ファイルマッピングをやってみることにしました。
ファイルマッピングとは?
「で。そのファイルマッピングとはなんぞや?」ということで
まずは解説からはじめましょう。

簡単に言うと、ファイルの内容をメモリ内にコピーしてくれるのです。
普通はファイルを読み込もうと思うと、下のようになります。
Dim temp(100) As String
Open "対象ファイル名.txt" For Input As #1
    Do While Not EOF(1)
        Line Input #1, temp(a)
        a=a+1
    Loop
Close
(いい加減だけど、まぁみんなこんなかな?) このループで何か1MB位のサイズのファイルを読み込んでみてください。 とりあえず、早いマシンでもちょっとは待たされると思います。 しかーし。ファイルマッピングを使うと、1MBなんてものは一瞬! ファイルマッピングは、ファイルのI/O処理を経ることなく ファイルの内容を直接メモリに配置させてくれるのです。 このようなメモリ中の仮想的なファイルを 「メモリマップドファイル」って言うんだって。 なんとなく理解できましたか?論より証拠でやってみましょう。
関係するAPI関数
実験する前に必要なAPI関数を簡単に説明しましょう。
CreateFileMappingファイルマッピングオブジェクトを作成する
MapViewOfFileファイルビューをマッピングする
OpenFileMapping名前付きファイルマッピングオブジェクトをオープンする
UnmapViewOfFileファイルのマップビューをアンマップする
細かい関数の説明は各関数のページを見てください。 とりあえず、手順の流れを示します。 @読み込みたいファイルのハンドルを取得する         ↓ Aオブジェクトを作成する         ↓ Bオブジェクトをオープンして、そのハンドルを取得する         ↓ Cファイルビューをマッピングして、その先頭アドレスを取得         ↓ Dメモリを読み込みたい変数にコピー         ↓ Eファイルビューをアンマップ         ↓ Fハンドルの解放 ここで大切なことがあります。 ファイルマッピングをしている間は ReadFile()関数やWriteFile()関数などのファイルI/Oをしてはいけない ということです。 もちろんVBのInput関数やLine Input関数も同じです。 それは頭において置いてください。 それでは、実際のプログラムを見て見ましょう。
実際のプログラム
いきなり解説もなくプログラムです。
Dim hFileMap As Long, hFile As Long, hOpened As Long, nAddress As Long
Dim FileLength As Long, buffer As String
Dim Buffer2 As String

FileLength = FileLen(filename) + 1

'@ファイルのハンドルを取得
hFile = CreateFileLong(filename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)

    'Aオブジェクトを作成する
    hFileMap = CreateFileMappingLong(hFile, 0, &H2, 0, 0, "test")
        If hFileMap = 0 Then GoTo LabelLast

        'Bオブジェクトをオープンして、そのハンドルを取得する
        hOpened = OpenFileMapping(FILE_MAP_READ, 0, "test")
        If hOpened = 0 Then GoTo LabelLast

            'Cファイルビューをマッピングして、その先頭アドレスを取得
            nAddress = MapViewOfFile(hOpened, FILE_MAP_READ, 0, 0, 0)
            If nAddress = 0 Then GoTo LabelLast

                'Dメモリを読み込みたい変数にコピー
                buffer = String(Filelength, vbNullChar)
                Call MoveMemory(ByVal buffer, ByVal nAddress, FileLength)
                Buffer2 = Left(buffer, InStr(buffer, vbNullChar))

            'Eファイルビューをアンマップ
            Call UnmapViewOfFile(nAddress)

        'Fハンドルの解放
LabelLast:
        Call CloseHandle(hOpened)
    Call CloseHandle(hFileMap)
Call CloseHandle(hFile)

Me.Text1.Text = Buffer2
なんとなく分かりますよね。 とりあえず、ファイルを一気にメモリにコピーできるので非常に簡単。 ということで、後はサンプルファイルで実験してみてさい。
ダウンロード
ダウンロード(CreateFileLong.lzh 14.8KB)