Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

GDBMであそんでみたよ

薄いwrapperから連想配列まで

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2013/06/26 14:00

 ファイルに保存できる汎用の辞書:GDBM(GNU Database Manager)。C-interfaceを提供する本ライブラリを段階的に拡張し、連想配列に仕立てます。

目次

 僕の主戦場はもっぱらWindowsですけど、Linux由来のコードやライブラリのお世話になることも少なくありません。先日もファイルに保存可能な辞書(key-value store)を探してて、GDBM:GNU Database ManagerのWindows版を見つけました。マジなDatabase使うほど大層なものではなく、キーと値の組をファイルに保存し、キーから値が検索できれば十分というシチュエーションだったので、このWindows-port版GDBM、ありがたく使わせていただきました。

GDBMをハダカで使う

 GDBMのトリセツはman pageを一読していただくことにして、まずはそのまま、C-interfaceでお試しコードを書いてみましょう。

list-01
#include <iostream>
#include <cassert>
#include <array>
#include <cstring> // strlen
#include <cstdlib> // free

#define GDBM_STATIC
#include <gdbm.h>

using namespace std;

void store() {
  GDBM_FILE dbf = gdbm_open("trial.db", 512, GDBM_NEWDB, 0644, 0);
  assert( dbf != nullptr );

  array<const char*,4> keys     = { "apple",  "orange", "grape",  "apple" };
  array<const char*,4> contents = { "まっく", "みかん", "ぶどう", "りんご" };

  datum key;
  datum content;

  for ( size_t i = 0; i < keys.size(); ++i ) {
    // キーと値をdatumにセット
    key.dptr = const_cast<char*>(keys[i]);
    key.dsize = strlen(keys[i]) + 1;
    content.dptr = const_cast<char*>(contents[i]);
    content.dsize = strlen(contents[i]) + 1;
    // キーと値のペアを登録
    // ※ GDBM_REPLACEにより、appleに紐づく値は
    //    ("まっく"から"りんご"に)上書きされます
    int result = gdbm_store(dbf, key, content, GDBM_REPLACE);
    assert( result == 0 );
  }
  gdbm_close(dbf);
  cout << "3 entries stored." << endl;
}

void fetch() {
  GDBM_FILE dbf = gdbm_open("trial.db", 512, GDBM_WRCREAT, 0644, 0);
  assert( dbf != nullptr );

  array<const char*,4> keys     = { "apple",  "orange", "grape",  "peach" };

  datum key;
  datum content;

  for ( const char* target : keys ) {
    // キーをdatumにセットし、
    key.dptr = const_cast<char*>(target);
    key.dsize = strlen(target) + 1;
    // キーに紐づいた値を検索
    content = gdbm_fetch(dbf, key);
    if ( content.dptr == nullptr ) {
      cout << target << " not found..." << endl;
    } else {
      cout << target << " = " << content.dptr << endl;
      free(content.dptr);
    }  
  }
  // Database内の全エントリを列挙する
  cout << "--- list all entries ---" << endl;
  key = gdbm_firstkey(dbf);
  while ( key.dptr != nullptr ) {
    char* tmp = key.dptr;
    content = gdbm_fetch(dbf, key);
    cout << key.dptr << " = " << content.dptr << endl;
    free(content.dptr);
    key = gdbm_nextkey(dbf, key);
    free(tmp);
  }
}

int main() {
  store();
  fetch();
}

 関数 store()ではキーと値のペアの登録、fetch()ではキーに紐づいた値の検索と、全エントリの列挙を行っています。GDBMが扱うデータはキー/値のいずれもデータの先頭アドレスとデータの長さ、すなわちバイト列を対象としていますから、(ここでは文字列をキー/値としていますけど)任意の型をキー/値として利用できます。気をつけなきゃならないのは、GDBMから帰ってきたdatum内のdptrが指すのはmalloc()で確保されたメモリ・ブロックであり、これをfree()するのは利用者の責任であることです。free()を忘れると少しずつメモリを食いつぶすことになります。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • επιστημη(エピステーメー)

    C++に首まで浸かったプログラマ。 Microsoft MVP, Visual C++ (2004.01~) だったり わんくま同盟でたまにセッションスピーカやったり 中国茶淹れてにわか茶人を気取ってたり、 あと Facebook とか。 著書: - STL標準講座 (監修) -...

All contents copyright © 2005-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5