- ベストアンサー
索引を目次に入れる方法とは?
- Perl初心者の方が目次に索引を入れる方法について困っているようです。
- 質問文章のデータには、目次と索引の情報がありますが、目次と索引を組み合わせて最終的なデータを作成する方法を知りたいとのことです。
- 質問者は、2ページから始まる目次と索引のデータを組み合わせ、最終的に目次と索引が交互に並んだデータを作成したいと考えています。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
リファレンスを使うと、多少、楽(?)です。 ベタベタに記述すると、以下のような感じ。 索引のページ数から、目次をひっぱってくるところが、効率悪そう… あと、各項目の区切り文字は、スペースにしてます。 -------------------------------- #use Data::Dumper; my @list; open MOKUJI, "目次.txt" or die($!); while(<MOKUJI>){ chomp; my($depth,$title,$from,$to) = split /\s+/; push @list, { depth => $depth, title => $title, from => $from, to => $to, words => [] }; } close MOKUJI; #print Dumper(@list); open SAKUIN,"" or die($!); while(<SAKUIN>){ chomp; my($page,$word) = split /\s+/; foreach(@list){ if($page >= $_->{from} and $page <= $_->{to}){ push @{$_->{words}},{ page => $page, word => $word }; last; } } } close SAKUIN; #print Dumper(@list); foreach(@list){ my $depth = $_->{depth}; print $depth; print $_->{title}; print "\n"; foreach(@{$_->{words}}){ print $depth; print $_->{word}; print "\n"; } } -------------------------------- Data::Dumperは、複雑(?)なデータ構造(特にツリー構造など)の中身を確認するのに便利です。 あとは、CPANあたりで、Tree構造を作るモジュールを探してみるのも手でしょう。 とはいえ、それはそれで面倒なのですが(使い方を覚えるのが一苦労)。 ・リファレンス http://www.kt.rim.or.jp/%7Ekbk/perl5.005/perlref.html ・Data::Dumper http://fleur.hio.jp/perldoc/mix/lib/Data/Dumper.ja.html ・CPAN http://search.cpan.org/search?query=tree&mode=all
その他の回答 (2)
- qtea
- ベストアンサー率77% (38/49)
とりあえず、No2の説明を書いてみます。 #コンテントオブジェクトにして返す sub to_content{ my ($depth, $title, $from, $to) = @_; return { depth => $depth, title => $title, from_page => $from, to_page => $to, last_page => $to, words => [], child_contents => [], parent_content => undef }; } 目次のデータを作って返すサブルーチンです。 引数は、深さ(+の個数)、目次のタイトル、開始ページ、終了ページです。 それぞれの項目を説明すると… depth 深さを保持します。+の個数です。個数で持つのは、その方が比較が楽になるからです。 title 目次のタイトルを保持します。 from_page 目次の開始ページを保持します。 to_page 目次の終了ページを保持します。 last_page 目次の下に目次がぶらさがっているので、ぶら下がっている、一番最後の目次の終了ページ数を保持します。これは、索引をぶら下げる時に、余計な探索を少しでも減らせるようと考えてつけました。もしかしたら無駄な努力だったかもしれません。 words 索引の単語とページ数を保持します。リストのリファレンスで持ちます。 child_contents ぶら下がる目次を保持します。リストのリファレンスで持ちます。 parent_content 親の目次を保持します。どの目次にぶら下がるのかを探索する時に、上からではなく、下から探索したいと考えたので(その方が場合によっては楽)つけています。 つづきは、また、ひまなときに…
お礼
ありがとうございました
- qtea
- ベストアンサー率77% (38/49)
>補足なんですが、目次の"+"が1つなら索引の"+"は2つ、というように目次よりも索引の"+"をひとつ増やしたい場合と、 >索引,ページ数と表したい場合はどのようにすればよいか教えていただけたら幸いです。お願いします。 下のほうの、 ------- foreach(@{$_->{words}}){ print $depth; print $_->{word}; print "\n"; } ------- この部分を↓に変更すればよいかと思います。 ------- foreach(@{$_->{words}}){ print "+"; #追加 print $depth; print $_->{word}; print ","; #追加 print $_->{page}; #追加 print "\n"; } ------- ただ、目次の「+」を見ていると、目次はツリー構造にしたいのかな?と、ふと思いました。 目次(2)は目次(1)にぶらさがっており、目次(4)は目次(3)にぶらさがっている、という感じなのかな?と… なので、それに対応してみたものを作ってみました。 結構、長いです。かなり、ごちゃごちゃしてます。 サンプルなので、ファイルを読みこむところなどは、自分で用意してください。 ------------------------- use Data::Dumper; use strict; #コンテントオブジェクトにして返す sub to_content{ my ($depth, $title, $from, $to) = @_; return { depth => $depth, title => $title, from_page => $from, to_page => $to, last_page => $to, words => [], child_contents => [], parent_content => undef }; } #索引オブジェクトにして返す sub to_index{ my ($page, $word) = @_; return { page => $page, word => $word, parent_content => undef }; } #目次リストを作成する sub make_contents_list{ my ($content, $before_contet) = @_; if($content->{depth} == $before_contet->{depth}){ $before_contet->{parent_content}->{last_page} = $content->{to_page}; $content->{parent_content} = $before_contet->{parent_content}; push @{$before_contet->{parent_content}->{child_contents}}, $content; }elsif($content->{depth} > $before_contet->{depth}){ $before_contet->{last_page} = $content->{to_page}; $content->{parent_content} = $before_contet; push @{$before_contet->{child_contents}}, $content; }else{ make_contents_list($content, $before_contet->{parent_content}); } } #ページ数から目次を検索して返す(見つからなければ、undefを返す) sub find_content_from_page{ my ($content, $page) = @_; if($page >= $content->{from_page} and $page <= $content->{to_page}){ return $content; }else{ foreach(@{$content->{child_contents}}){ if($page >= $_->{from_page} and $page <= $_->{to_page}){ return $_; }elsif($page >= $_->{from_page} and $page <= $_->{last_page}){ return find_content_from_page($_,$page); } } } return undef; } #単語から索引オブジェクトを検索して返す(見つからなければ、undefを返す) #左辺値が配列なら、全ての同じ単語を検索して、全て返す #左辺値がスカラーなら、最初に見つかった単語を返す sub find_word{ my ($content, $word_text) = @_; my @words; foreach(@{$content->{words}}){ if($_->{word} eq $word_text){ if(wantarray){ push @words, $_; }else{ return $_; } } } foreach(@{$content->{child_contents}}){ if(wantarray){ my @ret_word = find_word($_, $word_text); if(defined @ret_word){ push @words, @ret_word; } }else{ my $ret_word = find_word($_, $word_text); if(defined $ret_word){ return $ret_word; } } } return wantarray ? @words : undef; } #目次と索引の一覧の表示 sub print_content{ my $content=shift; my $mark = "+" x $content->{depth}; $mark .= " " if(length($mark) > 0); print $mark, $content->{title}, "\n"; foreach(@{$content->{words}}){ print "+", $mark, " ", $_->{word}, ",", $_->{page}, "\n"; } foreach(@{$content->{child_contents}}){ print_content($_); } } #目次に索引オブジェクトを入れる sub insert_index{ my ($content, $word) = @_; my $content = find_content_from_page($content, $word->{page}); if(defined $content){ $word->{parent_content}=$content; push @{$content->{words}}, $word; } } #指定の索引オブジェクトから一番上までの道筋をリストで返す sub get_word_road{ my ($word) = @_; my @road = get_content_road($word->{parent_content}); push @road, $word->{parent_content}; return @road; } #指定の目次オブジェクトから一番上までの道筋をリストで返す sub get_content_road{ my ($content) = @_; my @road; my $i = 0; while(defined $content->{parent_content}){ unshift @road, $content->{parent_content}; $content=$content->{parent_content}; } return @road; } #目次一覧 my $book = to_content(0,'テスト文書',0,0); #前回の目次オブジェクトを記憶しておく変数 my $before_contet = $book; #目次データを読みこみ、目次オブジェクトのツリーを作成する while(<DATA>){ #目次データと索引データを空行で分けているので、空行が来たら、ストップ last if /^$/; chomp; my ($mark, $title, $from, $to) = split /\s+/, $_; my $content =to_content( length($mark), $title, $from, $to ); #目次一覧を現在の目次と前回の目次から作成する make_contents_list($content, $before_contet); #前回の目次オブジェクトとして保持する $before_contet = $content; } #索引データを読みこみ、目次オブジェクトにつっこむ while(<DATA>){ chomp; my($page, $word_text) = split /\s+/; my $word = to_index($page, $word_text); insert_index($book, $word); } #とりあえず、確認 #print Dumper($book); print_content($book); print "----\n"; #単語を探す my $word = find_word($book, "索引(5)"); if(defined $word){ my @road = get_word_road($word); my @titles = map{$_->{title}} @road; print join(" > ", @titles, $word->{word}); print ", ", $word->{page}, "\n"; }else{ print "見つかりませんでした。"; } my @word = find_word($book, "索引(3)"); if(defined @word){ foreach(@word){ my @road = get_word_road($_); my @titles = map{$_->{title}} @road; print join(" > ", @titles, $_->{word}); print ", ", $_->{page}, "\n"; } }else{ print "見つかりませんでした。"; } #下記はテストデータ __END__ + 目次(1) 1 5 ++ 目次(2) 5 7 ++ 目次(3) 8 10 +++ 目次(4) 11 15 ++ 目次(5) 16 20 + 目次(6) 21 25 2 索引(1) 5 索引(2) 7 索引(3) 12 索引(3) 9 索引(4) 11 索引(5) -------------------------
お礼
ありがとうございました
補足
ありがとうございます。大変参考になりました! 補足なんですが、目次の"+"が1つなら索引の"+"は2つ、というように目次よりも索引の"+"をひとつ増やしたい場合と、 索引,ページ数と表したい場合はどのようにすればよいか教えていただけたら幸いです。お願いします。