極悪です。
閑舎さんの txtsearch に触発されて、僕も pdf をテキストに変換
した結果を保存して、そこから文字列を検索するスクリプトを書い
てみました。インデックスを作るわけではないので高速とは言えな
いけど、ないよりはましでしょう。
本当は溜まって整理のつかないデバイスのデータシート(全部で 4
GB ある)を検索したくて作りました。家の PC だと 2.5GB ある
pdf からテキストへの変換に 63 分(変換結果のテキストファイル
は 179MB)。そこから二語を検索するのに 40 秒かかりました。ヒ
ット数が多くなるともっと時間がかかると思います。ちなみにこの
スクリプトを書くのに 9 時間かかりました(わっはっは)。
さて、スクリプト(pdfsearch.pl)は環境変数 PDF に設定したデ
ィレクトリ以下の pdf から正規表現で複数の文字列を and 検索で
きます。
pdfsearch.pl を初めて起動すると、環境変数 PDF で指定したディ
レクトリに pdf の内容をテキストに変換して全部くっつけたファ
イル 00legend.txt を作ります。その後、pdfsearch.pl utf8 など
とすると、utf8 という文字列を含んだ pdf の内容(をテキストに
変換したもの)を表示します。検索する語は複数指定できます。
pdf からテキストへの変換は http://www.foolabs.com/xpdf/ にあ
る pdftotext というプログラムを使っています。解説を見ると
EUC とか Shift-JIS にも対応できるようなのですが、やり方がわ
からなくて今回は化け化けのままほっといてます。
テキストへの変換は初回だけで2回目からは行いませんが、オプシ
ョン -f を指定すれば強制的に再変換します。また pdf2text の変
換結果をいじってパラグラフ単位にしてます(空行をパラグラフの
境界としてます)。
検索後を複数指定した場合、ファイルの中身を何回も走査するうえ、
処理が全部終わるまで結果が出てきません。そのときはオプション
-l を指定すると処理単位がファイル全体からパラグラフ単位にな
って結果が速く出てきます。
perl のデータで試した場合の例です。データ少ないので参考にな
らないかな。
{{{
D:%set PDF=D:\DATA\perl
D:%timer "perl pdfsearch.pl"
ok : D:\DATA\perl/Kansai.pm/Seminar/20000319_1st/fukuhara/fukuhara.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20000319_1st/kiyoka/kiyoka.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20000319_1st/yamatomo/yamamoto.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20000520_2nd/funaki/funaki.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20000520_2nd/ivanov/perltalk.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20000520_2nd/moriwaka/moriwaka.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20001202_3rd/hozumi/hozumi.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20001202_3rd/hozumi/hozumi_appendix.pdf
ok : D:\DATA\perl/Kansai.pm/Seminar/20001202_3rd/kiyoka/kiyoka_jcode.pdf
ok : D:\DATA\perl/Kansai.pm/ご案内.pdf
ok : D:\DATA\perl/mkweb/makeweb.pdf
pattern not specifed
21.364000 sec
D:%timer "perl pdfsearch.pl utf8"
"D:\DATA\perl/Kansai.pm/Seminar/20000520_2nd/ivanov/perltalk.pdf" ュ
ops: キ In regexp: ュ ・p{}, ・P{} => * * * match ュ * * * * キ
lib/5.6.0/unicode *** キ use byte; キ use utf8; * * utf8 キ use
charnames `:full'; * **name use charnames `:full'; print
"・N{KATAKANA LETTER A}"; # キ use charnames `:short'; * *
script:char use charnames `:short'; print "・N{katakana:a}";
0.082000 sec
D:%
}}}
スクリプトは下記です。いつものことで、発言をアップするとバグ
が見つかるのですが、ちょくちょく修正はしないし、これ以上の機
能アップ(インデックス作ったりとか)もできないと思いますが、
ここはチガウとか、こうしたらイイよ、というのがありました教え
てください。
--^pdfsearch.pl
use strict;
use File::Recurse;
use Getopt::Std;
use Text::Wrap;
my $PDF2TEXT = q(C:/usr/xpdf/pdftotext.exe -raw -q);
my %opt;
getopts('fl',\%opt);
my $rootdir = $ENV{PDF} or die qq(env var "PDF" not defined\n);
my @pattern = map(qr/$_/,sort{length $b <=> length $a}@ARGV);
$rootdir =~ s/[\/\\]/\\/;
$rootdir =~ s/\\$//;
my $temp_file = $rootdir.q(\\temp_file.tmp);
my $legend_file = $rootdir.q(\\00legend.txt);
END{
unlink $temp_file;
close LEGEND;
}
### テキストファイル作成 ###
if($opt{f} or not -f $legend_file){
if(not -d $rootdir){
die qq(dir "$rootdir" not found\n);
}
open(LEGEND,">$legend_file") or die qq($! "$legend_file"\n);
recurse{
do{{
if(not s/\.pdf$//i){
print qq(skip : $_\n) if 0;
last;
}
my $basename = $_;
my $error_level = `$PDF2TEXT "$basename.pdf" $temp_file`;
if($error_level){
print qq(err : $basename.pdf\n);
next;
}
print qq(ok : $basename.pdf\n);
open(TEMP,$temp_file) or die qq($! "$temp_file"\n);
print LEGEND $basename,".pdf\n";
my($last_line,$last_length);
while(<TEMP>){
s/\s+/\x20/g;
s/^\s//;
s/\s$//;
my $length = length;
if($length){
if($last_length){
print LEGEND "\x20",$_;
}else{
print LEGEND $_;
}
}else{
if($last_length){
print LEGEND "\n";
}else{
;;;
}
}
($last_line,$last_length) = ($_,$length);
}
close TEMP;
print LEGEND "\n";
}}while(0);
}$rootdir;
close LEGEND;
}
### 検索 ###
if(not @pattern){
die qq(pattern not specifed\n);
}
if(defined $opt{l}){
open(LEGEND,$legend_file) or die qq($! "$temp_file");
my $file;
LOOP:while(<LEGEND>){
if($_ eq "\n"){
undef $file;
}elsif(not $file){
chomp;
$file = $_;
}else{
foreach my $pattern (@pattern){
next LOOP unless m/$pattern/;
}
print wrap(qq/"$file"\t/,"\t",$_);
}
}
close LEGEND;
}else{
my(%found,%skip);
foreach my $pattern (@pattern){
my %islocalfound;
open(LEGEND,$legend_file) or die qq($! "$temp_file");
my $file;
LOOP:while(<LEGEND>){
if($_ eq "\n"){
undef $file;
}elsif(not $file){
chomp;
$file = $_;
}elsif($skip{$file}){
;;;
}elsif(m/$pattern/){
++$islocalfound{$file};
push(@{$found{$file}},$_);
}
}
foreach my $file (keys %islocalfound){
next if $islocalfound{$file};
$skip{$file} = 1;
}
close LEGEND;
}
foreach my $file (sort keys %found){
next if $skip{$file};
foreach(@{$found{$file}}){
print wrap(qq/"$file"\t/,"\t",$_);
}
}
}
--$
--
FZH01112@..., http://homepage1.nifty.com/dune/