DBD::CSVモジュール(日本語チョー訳)

by Hippo2000(1999/11/2)

DBD::CSVモジュールなのです。使ったことがないモジュールなのですが...。(未確認なだけ)

なおこのドキュメントではDBD::CSVモジュールを入れると入ってくるcsv.htmlを日本語化したものです。わかりにくい部分は本物を見てください。(^^;; 姉妹品のDBD::Fileもよろしく(^_^)

原本の著作権は Jochen Wiedmannさんがお持ちです。Jochen Wiedmannさんにはメールで了解をいただきました。

なお内容等が間違っていたら修正します。ご連絡ください


目次


名前

DBD::CSV - CSVファイルのためのDBIドライバ


概要

    use DBI;
    $dbh = DBI->connect("DBI:CSV:f_dir=/home/joe/csvdb")
        or die "Cannot connect: " . $DBI::errstr;
    $sth = $dbh->prepare("CREATE TABLE a (id INTEGER, name CHAR(10))")
        or die "Cannot prepare: " . $dbh->errstr();
    $sth->execute() or die "Cannot execute: " . $sth->errstr();
    $sth->finish();
    $dbh->disconnect();
    # MS Excelによってエキスポートされるように、「;」をセパレータとして
    # CSVファイルを読み込みます。「;」をエスケープする必要があることに注意してください。
    # そうでなければ、属性のセパレータとして扱われてしまいます。
    $dbh = DBI->connect(qq{DBI:CSV:csv_sep_char=\\;});
    $sth = $dbh->prepare("SELECT * FROM info");
    # 同じ例。今度は「info.csv」をテーブルとして読み込みます:
    $dbh = DBI->connect(qq{DBI:CSV:csv_sep_char=\\;});
    $dbh->{'csv_tables'}->{'info'} = { 'file' => 'csv'};
    $sth = $dbh->prepare("SELECT * FROM info");

警告

これはα版のソフトウェアです:これは単にインターフェース(API)が最終化されていないためだけのために、「α版」になっています。コードの品質や安定性については「α版」というのは当てはまりません。


説明

DBD::CSVモジュールはDBI(Perlのためのデータベース独立インタフェース)のためのドライバの1つです。これはSQL「エンジン」SQL::Statementと抽象DBIドライバDBD::Fileを基本とし、いわゆるCSVファイル(Comma separaed values=カンマ区切りファイル)へのアクセスを実装しています。そのようなファイルはMS AccessやMS Excelデータのエキスポートによく使われます。

詳細については、DBIの詳細については、DBI(3) 、SQL::Statementの詳細についてはSQL::Statement(3)、基本クラスDBD::Fileの詳細についてはDBD::File(3) をご覧ください。


準備(先に必要なこと)

DBD::Fileが使っているシステム依存の機能はflock()関数だけです。そのため(理論的には)このモジュールはflock()が動くすべてのシステム、特に全てのUnixマシーンとWindows NTで機能するはずです。Windows95とMacOSではflock()は使えませんが、このモジュールを使うことができます。

他のDBIドライバとは違って、外部のSQLエンジンや稼動しているサーバが必要ありません。必要なのは以下のPerlモジュールだけです。これらはCPANミラーから利用することができます。例えば:

  ftp://ftp.funet.fi/pub/languages/perl/CPAN/modules/by-module
DBI
DBI (Perlのためのデータベース独立インターフェース), version 1.00 以降のリリース
SQL::Statement
簡単なSQLエンジン
Text::CSV_XS
このモジュールはCSVファイルへの行の書きこみ、読み込みに使われます。

インストール

このモジュール(そして上記の準備するもの)のインストールはとっても簡単です。以下のようにアーカイブから解凍して、取り出します。

    gzip -cd DBD-CSV-0.1000.tar.gz | tar xf -

(これはUnixユーザ用です。WindowsユーザはWinZipまたは同類のものを参照してください)

そして以下のように入力します:

    cd DBD-CSV-0.1000
    perl Makefile.PL
    make
    make test

テストが失敗したら、私に教えてください。そうでなければ次に進みます。

    make install

rootまたは管理者の権限が必要であることに注意してください。もしそれをもっていなければ、あなた自身のディレクトリへのインストール方法の詳細についてExtUtils::MakeMakerをご覧ください。

訳者注:

 ActivePerlではPPMを使ってください。もっと簡単にインストールすることができます。


データベースハンドルの作成

データベースハンドルの作成は通常、データベースサーバへの接続を意味します。

    use DBI;
    my $dbh = DBI->connect("DBI:CSV:f_dir=$dir");

上記のコマンドはドライバが示すテーブル(別名ファイル)を作成あるいはオープンするディレクトリはを読み込みます。デフォルトは現在のディレクトリです。これは以下のものと同じです。

    $dbh = DBI->connect("DBI:CSV:");
    $dbh = DBI->connect("DBI:CSV:f_dir=.");

DSN文字列にセミコロンで区切って、他の属性を設定することもできます。


テーブルの作成、削除

以下のようなコマンドを使って、テーブルを作成したり、削除することができます:

    $dbh->do("CREATE TABLE $table (id INTEGER, name CHAR(64))");
    $dbh->do("DROP TABLE $table");

現在、カラム名は格納されますが、他のデータは格納されないことに気をつけてください。そのためカラムのデータ型(例えばINTEGERやCHAR(x))、カラムの属性(NOT NULL、PRIMARY KEY、...)といった他の情報は黙って無視されます。今後のリリースで、これは変更されるかもしれません。

削除は何の警告もなくファイルを削除します。

詳細についてはDBI(3) をご覧ください。

テーブル名はSQLの文法の制約から、勝手に付けることは出来ません。テーブル名に適切なSQL識別子をつけることをお勧めします。最初の文字は英字、2文字目以降は任意の英数字とします。他のファイルを使いたい場合には、ファイル名を'/'、'./'、'../'から始めなければなりません、そして空白が入ってはいけません。


データの挿入、取り出し、変更

以下の例では、テーブルにあるデータを挿入し、取り出します:

まずデータを挿入します:

    $dbh->do("INSERT INTO $table VALUES (1, "
             . $dbh->quote("foobar") . ")");

'foobar'という単語をエスケープするためにquoteメソッドを使っていることに注意してください。例えバイナリデータが入っていなくても、すべての文字列はエスケープするべきです。

次の例ではパラメータを使っています:

    $dbh->do("INSERT INTO $table VALUES (?, ?)", undef,
             2, "It's a string!");

ここでは、自動的におこなわれるのでquoteメソッドを使う必要がないことに注意してください。このやり方は特に繰り返しに適した設計となっています。パフォーマンスが問題となる場合には、この方法を使うことをお勧めします。

undefについて疑問に思うかもしれません。ご心配なく、それはそのまま扱われます。(^^;; それは私はこれまで使ったことがない属性引数で、prepareメソッドへの2番目の引数として解析されます。

データを取り出すためには、以下のようにします:

    my($query) = "SELECT * FROM $table WHERE id > 1 ORDER BY id";
    my($sth) = $dbh->prepare($query);
    $sth->execute();
    while (my $row = $sth->fetchrow_hashref) {
        print("Found result row: id = ", $row->{'id'},
              ", name = ", $row->{'name'});
    }
    $sth->finish();

また、カラムバインディングを使って同じ例をすると:

    my($query) = "SELECT * FROM $table WHERE id > 1 ORDER BY id";
    my($sth) = $dbh->prepare($query);
    $sth->execute();
    my($id, $name);
    $sth->bind_columns(undef, \$id, \$name);
    while ($sth->fetch) {
        print("Found result row: id = $id, name = $name\n");
    }
    $sth->finish();

もちろん、入力パラメータを使うこともできます。3番目の例を以下に示します:

    my($query) = "SELECT * FROM $table WHERE id = ?";
    my($sth) = $dbh->prepare($query);
    $sth->bind_columns(undef, \$id, \$name);
    for (my($i) = 1;  $i <= 2;   $i++) {
        $sth->execute($id);
        if ($sth->fetch) {
            print("Found result row: id = $id, name = $name\n");
        }
        $sth->finish();
    }

これらのメソッドの詳細は DBI(3) をご覧ください。WHERE節の詳細についてはSQL::Statement(3) をご覧ください。

データ行はUPDATEステートメントで変更されます:

    $dbh->do("UPDATE $table SET id = 3 WHERE id = 1");

さらに行を削除するためにはDELETEステートメントを使います:

    $dbh->do("DELETE FROM $table WHERE id > 1");

エラーの取り扱い

上記の例では、戻り値について一切気にしてきませんでした。これはお勧めできません。そうではなく以下のように書くべきでした(例えば):

    my($query) = "SELECT * FROM $table WHERE id = ?";
    my($sth) = $dbh->prepare($query)
        or die "prepare: " . $dbh->errstr();
    $sth->bind_columns(undef, \$id, \$name)
        or die "bind_columns: " . $dbh->errstr();
    for (my($i) = 1;  $i <= 2;   $i++) {
        $sth->execute($id)
            or die "execute: " . $dbh->errstr();
        if ($sth->fetch) {
            print("Found result row: id = $id, name = $name\n");
        }
    }
    $sth->finish($id)
        or die "finish: " . $dbh->errstr();

これは明らかに冗長です。幸いにも私達にはDBIのRaiseError属性があります:

    $dbh->{'RaiseError'} = 1;
    $@ = '';
    eval {
        my($query) = "SELECT * FROM $table WHERE id = ?";
        my($sth) = $dbh->prepare($query);
        $sth->bind_columns(undef, \$id, \$name);
        for (my($i) = 1;  $i <= 2;   $i++) {
            $sth->execute($id);
            if ($sth->fetch) {
                print("Found result row: id = $id, name = $name\n");
            }
        }
        $sth->finish($id);
    };
    if ($@) { die "SQL database error: $@"; }

これは短いだけでなく、サブルーチンのなかでDBIメソッドを使ったときにさえ機能します。


メタデータ

これらの属性はDBD::FileではなくDBIそのもので扱われます、そのためこれらは期待する通りの動きをします。

    Active
    ActiveKids
    CachedKids
    CompatMode             (未使用)
    InactiveDestroy
    Kids
    PrintError
    RaiseError
    Warn                   (未使用)

以下のDBI属性はDBD::Fileによって扱われます:

AutoCommit
常にオン
ChopBlanks
機能します
NUM_OF_FIELDS
$sth->execute以降、適正です
NUM_OF_PARAMS
$sth->prepare以降、適正です
NAME
$sth->execute以降、適正です;Selectステートメント以外ではundefです。
NULLABLE
実際には機能しません。DBD::CSVが入力データを検査しないので、常にその配列リファレンスを返します。$sth->execute以降、適正です。SELECTステートメント以外ではundefです。
 

以下の属性、メソッドはサポートされていません:

    bind_param_inout
    CursorName
    LongReadLen
    LongTruncOk

追加のDBI属性として、以下のdbh属性を使うことができます:

f_dir
この属性はCSVファイルが開かれるディレクトリを設定するために使われます。通常、これはdbhで設定します。デフォルトは現在のディレクトリ("")です。 しかし、ステートメント・ハンドルで置きかえることができます。
csv_eol
 
csv_sep_char
 
csv_quote_char
 
csv_escape_char
 
csv_class
 
csv_csv
属性 csv_eol,、csv_sep_charcsv_quote_charcsv_escape_char は関連するText::CSV_XSオブジェクトの属性に対応します。 /etc/passwdのような通常のCSVファイルやセミコロンを区切り文字にしているMS Excelが生成したCSVファイルを持っていれば、これらの属性を設定したいでしょう。デフォルトはそれぞれ、"\015\012''、 ';'、 '"' 、''''になります。

これらの属性はデフォルトのText::CSV_XSによってcsv_classのインスタンスを生成するときに使用されます。代わりにインスタンスをcsv_csvとして渡すことができます。後から渡されたものが優先されます。その場合、binary属性がtrueに設定されなければならないことに注意してください。

加えて、csv_tables属性でテーブル毎にこれらの属性を置きかえることもできます。

csv_tables
このハッシュ・リファレンスはテーブルに依存するメタデータを格納するために使われます。どのテーブルにも、キーとしてテーブル名の入った要素を持ち、以下の属性をもったハッシュ・リファレンスを持っています。
 
file
テーブルファイル名。デフォルトは
    "$dbh->{f_dir}/$table"
eol
 
sep_char
 
quote_char
 
escape_char
 
class
 
csv
これらは属性csv_eolcsv_sep_charcsv_quote_charcsv_escape_charcsv_class そして csv_csvに対応します。.違いはこれらはテーブル毎に機能する点で異なります。
col_names
 
skip_first_row
デフォルトでは、DBD::CSVはカラム名はCSVファイルの最初の行に入っているものとしています。そうでなかった場合、col_namesI属性を持ったテーブル名の配列リファレンスを指定することができます。その場合、skip_first_row はFALSEに設定しなければなりません。

空の配列リファレンスを指定すると、ドライバは最初の行を読み、カラムの数を数えて、カラム名をcol0、col1...のように生成します。

例: /etc/passwd をCSVファイルとして使いたいとします。(^_^) 一番簡単な方法は...:

    require DBI;
    my $dbh = DBI->connect("DBI:CSV:f_dir=/etc;csv_eol=\n;"
                           . "csv_sep_char=:;csv_quote_char=;"
                           . "csv_escape_char=");
    $dbh->{'csv_tables'}->{'passwd'} = {
        'col_names' => ["login", "password", "uid", "gid", "realname",
                        "directory", "shell"]
    };
    $sth = $dbh->prepare("SELECT * FROM passwd");

他の方法として、すべての属性をデフォルトのままとして、テーブル毎にそれらを書きかえます:

    require DBI;
    my $dbh = DBI->connect("DBI:CSV:");
    $dbh->{'csv_tables'}->{'passwd'} = {
        'eol' => "\n",
        'sep_char' => ":",
        'quote_char' => undef,
        'escape_char' => undef,
        'file' => '/etc/passwd',
        'col_names' => ["login", "password", "uid", "gid", "realname",
                        "directory", "shell"]
    };
    $sth = $dbh->prepare("SELECT * FROM passwd");

ドライバ独自のメソッド

これらのメソッドはDBD::Fileから継承されています:

data_sources
data_sources メソッドは現在のディレクトリのサブディレクトリのリストを"DBI:CSV:f_dir=$dirname"という形式で返します。

もし他のディレクトリのサブディレクトリを読みたければ、以下のようにしてください:

    my($drh) = DBI->install_driver("CSV");
    my(@list) = $drh->data_sources('f_dir' => '/usr/local/csv_data' );
list_tables
このメソッドは$dbh->{'f_dir'}の中のファイル名のリストを返します。例えば:
    my($dbh) = DBI->connect("DBI:CSV:f_dir=/usr/local/csv_data");
    my(@list) = $dbh->func('list_tables');

SQLの観点からはテーブル名として適切でないものも含めて、ディレクトリに入っている全てのファイルがリストに含まれることに注意してください。上記の「テーブルの作成、削除」のご覧ください。


データの制限

データを挿入あるいは取り出すさいに、驚くことがあるかも知れません:DBD::CSVは、データ型を、特にNULLを正しく扱えません。それは、もし整数値を入れようとするとき起きるかもしれません。取り出すと文字を返すのです。もちろん文字列には数字が入っているので、多分、本当の問題にはならないでしょう。しかし以下のものは動きません:

    $dbh->do("INSERT INTO $table (id, name) VALUES (?, ?)",
             undef, "foo bar");
    $sth = $dbh->prepare("SELECT * FROM $table WHERE id IS NULL");
    $sth->execute();
    my($id, $name);
    $sth->bind_columns(undef, \$id, \$name);
    while ($sth->fetch) {
        printf("Found result row: id = %s, name = %s\n",
              defined($id) ? $id : "NULL",
              defined($name) ? $name : "NULL");
    }
    $sth->finish();

今入れた行は絶対に返ってきません。CSVファイルを調べれば、理由は明らかです。対応する行は以下のようになります:

    "","foo bar"

いいかえれば、NULLは格納されず、空文字が格納されます。CSVファイルはNULL値という概念を持っていません。nameにもNULL値を入れると、驚くべきことに上記の例は動きます。CSVファイルを調べると、またその答えがわかるでしょう:

    ""

いいかえれば、DBD::CSVはNULL値をカラムを減らした行を出力することによって「エミュレート」しているのです。もちろん、これは一番右のカラムがNULL、次のカラムもNULL、...、でも一番左のカラムは絶対にNULLでない場合にのみ機能します。

テーブル名の制限については、上記 テーブルの作成と削除をご覧ください。


今後の予定

DBD::CSVの拡張

CSV ファイルスキャナ
CSVファイルを読み込み、区切り文字、クォート文字、エスケープ文字、行末を自動的に推測しようとする簡単なCSVファイル・スキャナを書きます。

単なるDBD::FileまたはSQL::Statementモジュールの制限:

結合(join)
このモジュールの現行のバージョンでは、SQL::Statementモジュールの基本的な設計が結合とlikeを可能にしているにもかかわらず、1つのテーブルに対するSELECTだけが機能します。
テーブル名マッピング
現在はnames.csvのような名前を持っているファイルを使うことはできません。代わりにファイルのソフトリンクまたは名前の変更をしなければいけません。この代替方法として、例えばdbh属性'table_map'というようなものを考えています。それはハッシュ・リファレンスで、キーがテーブル名なり、値がファイル名になります。
カラム名マッピング
現在、このモジュールはカラム名が先頭の行に入っているものと見なします。多くの場合はこれでよいのですが、カラム名とカラム番号をプログラマから設定する可能性があります。例えばMS Accessは、デフォルトではカラム名をエキスポートしません。

既知のバグ


作者と著作権

(原文のまま)

This module is Copyright (C) 1998 by

    Jochen Wiedmann
    Am Eisteich 9
    72555 Metzingen
    Germany
    Email: joe@ispsoft.de
    Phone: +49 7123 14887

All rights reserved.

You may distribute this module under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.


参考資料

DBI(3), Text::CSV_XS(3), SQL::Statement(3)

DBD::CSVの使い方のヘルプについては、DBIユーザメーリングリストをご覧ください:

  http://www.isc.org/dbi-lists.html

DBIの一般的な情報については以下のサイトをご覧ください:

  http://www.symbolstone.org/technology/perl/DBI

ホーム Perlの小技

ご意見、ご質問はこちらの掲示板で受け付けています。
またメールは河馬屋(Nifty)にお願いします。