ここに掲載しているPerlスクリプトは動作状況を確認するためのもので、十分なエラー処理(不用意な入力データの検証等)を行なっていません。このページのスクリプトはGNU GPLフリーソフトウエアとして利用(改変、再配布等)出来ます。
目次 |
CREATE TABLE Albums (id INTEGER PRIMARY KEY, albumRoot INTEGER NOT NULL, relativePath TEXT NOT NULL, DATE DATE, caption TEXT, collection TEXT, icon INTEGER, UNIQUE(albumRoot, relativePath) );
CREATE TABLE Images (id INTEGER PRIMARY KEY, album INTEGER, name TEXT NOT NULL, STATUS INTEGER NOT NULL, category INTEGER NOT NULL, modificationDate DATETIME, fileSize INTEGER, uniqueHash TEXT, UNIQUE (album, name) );
CREATE TABLE ImageComments (id INTEGER PRIMARY KEY, imageid INTEGER, TYPE INTEGER, LANGUAGE TEXT, author TEXT, DATE DATETIME, comment TEXT, UNIQUE(imageid, TYPE, LANGUAGE, author) );
JP-olwp1.jpg,"安芸の宮島 (Torii Gate of Itsukushima Jinja Shrine)" JP-olwp2.jpg,"桜 (Cherry Tree, Japan)" JP-olwp3.jpg,"清流 (River in Akita Prefecture, Japan)" JP-olwp4.jpg,"庭園 (Waterfall in Tokyo Garden, Japan)" JP-olwp5.jpg,"樹氷 (Snow at Zao Park, Japan)"
#!/usr/bin/perl # GNU GPL Free Software. (C) 2010 INOUE Hirokazu # このファイルは UTF-8 で保存すること. save this file in UTF-8 use strict; use warnings; use DBI; use Text::CSV_XS; my $flagDebug = 0; # 詳細表示する場合は 1 を代入する my $database = './digikam4.db'; # digiKam のデータベースファイル my $data_source = "dbi:SQLite:dbname=$database"; my $dbh = undef; my $sth = undef; my $strQuery = undef; # DBIのSQL prepare用 my $nSelectAlbum = 0; # 選択されたAlbumsテーブルのid my @arrPaths = undef; # AlbumsテーブルのクエリでrelativePathの結果配列を格納する my $arrRefPathsWoComment = undef; # export対象でコメント無しの(id,ファイル名,コメント)配列 my $arrRefPathsWComment = undef; # export対象でコメント有りの(id,ファイル名,コメント)配列 my $nTargetFilesWoComment = undef; # export対象でコメント無しの件数(ファイル数) my $nTargetFilesWComment = undef; # export対象でコメント有りの件数(ファイル数) my $csv = undef; # CSVファイル作成用 my $i = 0; my $strTemp = undef; print("digiKamの特定ディレクトリの画像とそのコメント一覧をCSVにエクスポートします\n\n"); eval{ $dbh = DBI->connect($data_source) or die("DBI error : fail to open SQLite db (db file = $database)\n"); ### ディレクトリ一覧を表示して、対象フォルダをユーザに選択させる $strQuery = "select id,relativePath from Albums;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute() or die("$sth->errstr \n"); print("ディレクトリ一覧\nno. ディレクトリ名\n----+-----------------\n"); while(@arrPaths = $sth->fetchrow_array()) { printf("%03d : %s\n", $arrPaths[0], $arrPaths[1]); } print("コメントを書き込むディレクトリの no. を指定してください : "); $strTemp = <STDIN>; chomp($strTemp); $nSelectAlbum = $strTemp; printf("select : %03d\n", $nSelectAlbum); ### ユーザが選択したディレクトリが存在するか確認し、ディレクトリ名を画面表示する $strQuery = "select id,relativePath from Albums where id=?;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nSelectAlbum) or die("$sth->errstr \n"); if(@arrPaths = $sth->fetchrow_array()) { if($sth->rows() != 1) { $dbh->disconnect(); die("ディレクトリの選択が間違っています\n"); } printf("対象ディレクトリ : %s (Albums.id=%d)\n", $arrPaths[1], $nSelectAlbum); } else { $dbh->disconnect(); die("ディレクトリの選択が間違っています\n"); } ### コメントが無いファイル一覧を配列に格納する $strQuery = "select Images.id, Images.name from Albums,Images on Albums.id=Images.album where Albums.id=? and Images.id not in (select Images.id from Albums,Images,ImageComments on Albums.id=Images.album and ImageComments.Imageid=Images.id where Albums.id=? and ImageComments.type=1);"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nSelectAlbum,$nSelectAlbum) or die("$sth->errstr \n"); $arrRefPathsWoComment = $sth->fetchall_arrayref(); $nTargetFilesWoComment = $sth->rows(); ### コメントが有るファイル一覧を配列に格納する $strQuery = "select Images.id, Images.name, ImageComments.comment from Albums,Images,ImageComments on Albums.id=Images.album and ImageComments.Imageid=Images.id where Albums.id=? and ImageComments.type=1;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nSelectAlbum) or die("$sth->errstr \n"); $arrRefPathsWComment = $sth->fetchall_arrayref(); $nTargetFilesWComment = $sth->rows(); ### 見つかったexport対象ファイルの数を画面表示 printf("ファイル数 = %d (うちコメント有り %d 個, コメント無し %d 個)\n", $nTargetFilesWComment + $nTargetFilesWoComment, $nTargetFilesWComment, $nTargetFilesWoComment); ### 見つかったexport対象ファイルの一覧を画面表示 if($flagDebug != 0) { print("対象ファイル一覧\nid ファイル名 コメント\n----+---------------------+----\n"); for($i=0; $i<$nTargetFilesWComment; $i++) { printf("%03d %-20s %s\n", $arrRefPathsWComment->[$i]->[0], $arrRefPathsWComment->[$i]->[1], substr($arrRefPathsWComment->[$i]->[2],0,32)); } for($i=0; $i<$nTargetFilesWoComment; $i++) { printf("%03d %-20s <コメント無し>\n", $arrRefPathsWoComment->[$i]->[0], $arrRefPathsWoComment->[$i]->[1]); } } $dbh->disconnect(); ### エクスポートするかどうか確認し、ファイル名を入力させる print("以上の内容をエクスポートしますか? (Y) : "); $strTemp = <STDIN>; chomp($strTemp); if(uc($strTemp) ne 'Y') { print("ファイルを出力せず、終了しました\n"); exit(); } print("出力するファイル名を入力 : "); $strTemp = <STDIN>; chomp($strTemp); if($strTemp eq '') { print("ファイル名が入力されなかったため、終了しました\n"); exit(); } ### 出力用ファイルを開く open(FH_OUT, ">$strTemp") or die("ファイル $strTemp に書き込めません"); $csv = Text::CSV_XS->new({binary=>1}); # 日本語の場合は binary を ON にする ### コメント有りのCSVデータを作成し、出力ファイルに書き込む for($i=0; $i<$nTargetFilesWComment; $i++) { if(defined($arrRefPathsWComment->[$i]->[2])){ $arrRefPathsWComment->[$i]->[2] =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g; } # 改行をエスケープ $csv->combine($arrRefPathsWComment->[$i]->[1], $arrRefPathsWComment->[$i]->[2]); print(FH_OUT $csv->string() . "\n") or die("ファイル $strTemp への書き込み異常"); } ### コメント無しのCSVデータを作成し、出力ファイルに書き込む for($i=0; $i<$nTargetFilesWoComment; $i++) { $csv->combine($arrRefPathsWoComment->[$i]->[1], ""); print(FH_OUT $csv->string() . "\n") or die("ファイル $strTemp への書き込み異常"); } close(FH_OUT) or die("ファイル $strTemp を close 出来ませんでした"); print("ファイル $strTemp への書き込み正常終了\n"); }; if($@){ # evalによるエラートラップ:エラー時の処理 die("プログラム エラー : ".$@."¥n"); exit(); } print("スクリプト終了\n"); ### スクリプト末端 EOF
#!/usr/bin/perl # GNU GPL Free Software. (C) 2010 INOUE Hirokazu # このファイルは UTF-8 で保存すること. save this file in UTF-8 use strict; use warnings; use DBI; use Text::CSV_XS; my $flagDebug = 1; # 詳細表示する場合は 1 を代入する my $database = './digikam4.db'; # digiKam のデータベースファイル my $data_source = "dbi:SQLite:dbname=$database"; my $dbh = undef; my $sth = undef; my $strQuery = undef; my $nSelectAlbum = 0; my @arrPaths = undef; my @arrFileAndComment = undef; # CSVから読み込んだファイル名とコメントの配列 my $arrRefQueryResult = undef; # SQLクエリ結果を一時的に保存 my $nTargetFiles = 0; # CSV内のデータ行数 my $strTemp = undef; my $i = 0; print("digiKamの特定フォルダの画像に、CSVよりコメントをインポートします\n\n"); eval{ ### CSVファイルを読み込む print("インポートするデータが入ったCSVファイル名を入力 : "); $strTemp = <STDIN>; chomp($strTemp); if($strTemp eq '') { print("ファイル名が入力されなかったため、終了しました\n"); exit(); } my $csv = Text::CSV_XS->new({binary=>1}); open(FH_IN, "<$strTemp") or die("ファイル $strTemp を読み込めません"); $nTargetFiles = 0; while(<FH_IN>) { # CSV各行をパースして、ファイル名とコメントを配列$arrFileAndCommentに格納 my $strLine = $_; if($strLine eq ''){ next; } $csv->parse($strLine) or next; my @arrFields = $csv->fields(); $arrFileAndComment[$nTargetFiles][0] = $arrFields[0]; $arrFields[1] =~ s/<br \/>/\x0A/g; # 改行のエスケープ"<br />"を0x0aに戻す $arrFileAndComment[$nTargetFiles][1] = $arrFields[1]; $nTargetFiles++; } close(FH_IN) or die("ファイル $strTemp を close 出来ませんでした"); ### CSVから読み込んだデータの画面表示 printf("CSVから読み込み完了。データ行数 = %d\n", $nTargetFiles); if($flagDebug != 0) { print("CSV内のデータ一覧\nid ファイル名 コメント\n----+--------------------+--------------------\n"); for($i=0; $i<$nTargetFiles; $i++) { $strTemp = $arrFileAndComment[$i][1]; $strTemp =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g; # 画面表示での改行を抑止 printf("%03d %-20s %s\n", $i, $arrFileAndComment[$i][0], substr($strTemp,0,16)); } } ### 書き込み対象とするdigiKamディレクトリの選択 print("データを書き込むdigiKamディレクトリを選択します\n"); # 自動コミットOFFでデータベースを開く $dbh = DBI->connect($data_source, "", "", {PrintError => 1, AutoCommit => 0}) or die("DBI error : fail to open SQLite db (db file = $database)\n"); $strQuery = "select id,relativePath from Albums;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute() or die("$sth->errstr \n"); print("ディレクトリ一覧\nno. ディレクトリ名\n----+-----------------\n"); while(my @arrPaths = $sth->fetchrow_array()) { printf("%03d %s\n", $arrPaths[0], $arrPaths[1]); } print("コメントを書き込むディレクトリの no. を指定してください : "); $strTemp = <STDIN>; chomp($strTemp); $nSelectAlbum = $strTemp; printf("select : %03d\n", $nSelectAlbum); ### 選択されたdigiKamのディレクトリが正しいかチェックする $strQuery = "select id,relativePath from Albums where id=?;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nSelectAlbum) or die("$sth->errstr \n"); if(@arrPaths = $sth->fetchrow_array()) { if($sth->rows() != 1) { $dbh->disconnect(); die("ディレクトリの選択が間違っています\n"); } printf("対象ディレクトリ : %s(id = %d)\n", $arrPaths[1], $nSelectAlbum); } else { $dbh->disconnect(); die("ディレクトリの選択が間違っています\n"); } ### digiKamディレクトリ内のファイル数を画面表示する $strQuery = "select count(*) from Albums,Images on Albums.id=Images.album where Albums.id=?;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nSelectAlbum) or die("$sth->errstr \n"); $arrRefQueryResult = $sth->fetchall_arrayref(); printf("CSVデータ行数 = %d\n", $nTargetFiles); printf("ディレクトリ内ファイル数=%d\n", $arrRefQueryResult->[0]->[0]); if($flagDebug != 0){ print("書き換え結果表示\n id ファイル名 旧コメント 新コメント\n-+----+---------------------+-----------------+----------------\n"); } else{ print("N:New, U:Update\n");} my $nWriitenLines = 0; for($i=0; $i<$nTargetFiles; $i++) { $strQuery = "select Images.id from Albums,Images on Albums.id=Images.album where Albums.id=? and Images.name=?;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nSelectAlbum,$arrFileAndComment[$i][0]) or die("$sth->errstr \n"); $arrRefQueryResult = $sth->fetchall_arrayref(); if($sth->rows() != 1){ next; } my $nId = $arrRefQueryResult->[0]->[0]; # DB内のImages.id $strQuery = "select Images.id, ImageComments.comment from Albums,Images,ImageComments on Albums.id=Images.album and ImageComments.Imageid=Images.id where Albums.id=? and Images.name=?;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nSelectAlbum,$arrFileAndComment[$i][0]) or die("$sth->errstr \n"); my $arrRefComment = $sth->fetchall_arrayref(); if($sth->rows() >= 1){ # DB内にコメントが存在する if($arrRefComment->[0]->[1] ne $arrFileAndComment[$i][1]) { $strQuery = "update ImageComments set comment=? where imageid=? and type=1;"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($arrFileAndComment[$i][1],$nId) or die("$sth->errstr \n"); $sth->finish() or die("データベース 書き込み時 finish 失敗"); if($flagDebug != 0) { $arrFileAndComment[$i][1] =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g; # 画面表示での改行を抑止 $arrRefComment->[0]->[1] =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g; printf("U %d %-20s %-16s %-16s\n", $nId, $arrFileAndComment[$i][0], substr($arrRefComment->[0]->[1],0,16), substr($arrFileAndComment[$i][1],0,16)); } else{ print("U"); } $nWriitenLines++; } elsif($flagDebug == 0){ print("."); } } else{ # DB内にコメントが存在しない if($arrFileAndComment[$i][1] ne '') { $strQuery = "insert into ImageComments (imageid, type, language,comment) values (?,1,'x-default',?);"; $sth = $dbh->prepare($strQuery) or die("$dbh->errstr \n"); $sth->execute($nId,$arrFileAndComment[$i][1]) or die("$sth->errstr \n"); $sth->finish() or die("データベース 書き込み時 finish 失敗"); if($flagDebug != 0) { $arrFileAndComment[$i][1] =~ s/\x0D\x0A|\x0D|\x0A/<br \/>/g; # 画面表示での改行を抑止 printf("N %d %-20s %-16s %-16s\n", $nId, $arrFileAndComment[$i][0],'', substr($arrFileAndComment[$i][1],0,16)); } else{ print("N"); } $nWriitenLines++; } elsif($flagDebug == 0){ print("."); } } } if($flagDebug == 0){ print("\n"); } if($nWriitenLines <= 0) { print("コメントの書き換え対象がないため、終了しました\n"); $dbh->rollback; $dbh->disconnect(); exit(); } ### エクスポートするかどうか最終確認 printf("以上の %d 個のコメントをdigikamデータベースに書き込みますか? (Y) : ", $nWriitenLines); $strTemp = <STDIN>; chomp($strTemp); if(uc($strTemp) ne 'Y') { print("\ndigiKamデータベースを更新せず、終了しました\n"); $dbh->rollback; $dbh->disconnect(); exit(); } $dbh->commit() or die("データベース commit 失敗"); printf("\n書き換えファイル数=%d\n", $nWriitenLines); $dbh->disconnect(); }; if($@){ # evalによるエラートラップ:エラー時の処理 if(defined($dbh)) { $dbh->rollback; $dbh->disconnect(); } die("プログラム エラー : ".$@."¥n"); } print("スクリプト終了\n"); ### スクリプト末端 EOF