C言語関係掲示板

過去ログ

No.410.非常に大きなファイルの高速な行の挿入方法

[戻る] [ホームページ]
No.2836

テキストファイルの高速な行の挿入方法について
投稿者---nock(2002/10/03 10:49:08)


はじめまして
テキストファイルの中に1行挿入しようと思っているのですが
テキストファイルが25Mと非常に大きく次のような操作を
すると非常に処理が重くなってしまいます。

ファイルをバッファに読み込む
      ↓
バッファを編集し1行挿入する
      ↓
バッファの内容をファイルに戻す

という作業をすると当たり前ですが読み込みだけでも
かなりの時間を使ってしまいます。
そこで、ファイル上で直接操作して高速化できる手法など
何かないでしょうか?
何か知っている方いらっしゃいましたらご教授よろしく
お願いいたします

No.2837

Re:テキストファイルの高速な行の挿入方法について
投稿者---かずま(2002/10/03 13:52:34)


> テキストファイルの中に1行挿入しようと思っているのですが
> テキストファイルが25Mと非常に大きく次のような操作を
> すると非常に処理が重くなってしまいます。

> そこで、ファイル上で直接操作して高速化できる手法など
> 何かないでしょうか?

ファイルをメモリーマップすれば、直接操作しているように見えます。
OS が Windows か Unix かによって、やり方が違いますが、ここでは、
Windows でのサンプルプログラム insertline.c を示します。

  insertline file.txt 65536 abcdefg

を実行すると、25MB のファイル file.txt の 64KB 目以降を 9バイト
後ろへずらし、空いたところへ "abcdefg\r\n" の 9バイトを挿入します。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *mmap(const char *name)
{
    HANDLE h, v;  void *q;

    h = CreateFile(name, (GENERIC_READ | GENERIC_WRITE), 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (h == INVALID_HANDLE_VALUE) return NULL;
    v = CreateFileMapping(h, NULL, PAGE_READWRITE, 0, 0, NULL);
    CloseHandle(h);
    if (v == INVALID_HANDLE_VALUE) return NULL;
    q = MapViewOfFile(v, FILE_MAP_WRITE, 0, 0, 0);
    CloseHandle(v);
    return q;
}

int insert(const char *filename, size_t pos, const char *line)
{
    FILE *fp;
    char *buf;
    size_t size;
    size_t len = strlen(line);

    fp = fopen(filename, "r+b");
    if (fp == NULL) return 1;
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);    // ファイルのサイズを取得
    fseek(fp, len + 1, SEEK_CUR);
    putc(0, fp);         // ファイルのサイズを拡張
    fclose(fp);

    buf = mmap(filename);
    if (buf == NULL) return 2;
    memmove(buf + pos + len + 2, buf + pos, size - pos);
    strcpy(buf + pos, line);
    buf[pos + len] = '\r';
    buf[pos + len + 1] = '\n';
    UnmapViewOfFile(buf);
    return 0;
}

int main(int argc, char **argv)
{
    if (argc != 4)
        return printf("usage: %s file position line\n", argv[0]), 1;
    switch (insert(argv[1], atoi(argv[2]), argv[3])) {
    case 1: printf("can't open %s\n", argv[1]); return 1;
    case 2: printf("can't mmap %s\n", argv[1]); return 1;
    }
    return 0;
}