- ベストアンサー
Perl。文字出現回数を重複しないでカウントしたい。
perl初心者です。どうぞ助けて下さい。 以下のようにデータがならんでいる時、 pphhhppphhhppppppphhppppppppppppppppppphhh pが連続した文字の連続数は 2、3、7、19 となります。 今、知りたいのはこのようなpの連続数の出現回数です。つまり、たとえばpが19回連続してあるものは何回でてきたか、が知りたいのです。 上の例だと1回が正解となります。 ところが、スクリプトを書くとき、 たとえば以下のようにしたとき、 pが19回なら重複カウントはなくても、 pを2でカウントしたら、 本当に出てきて欲しいこたえは一回なのに、 P=3、7、19のどれにも該当してしまいます。 #!/usr/bin/perl ; open(IN, "text1.txt") or die ; open(OUT, ">text1out.txt"); while(<IN>) { chomp ; if (/(\S+)/) { $name = $1 ; if ($name =~ /^ppppppppppppppppppp/) { {$count++;} ; } print OUT "$count\n" ; } } close (IN) ; close (OUT) ; 正規表現の中を工夫すればいいのだと思いますが、 大変困っております。宜しくお願いします。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
アバウトな実装ですが、こんなんとか: > cat a.pl $_ = 'pphhhppphhhppppppphhppppppppppppppppppphhh'; while ( s/^(.)\1*// ){ $char_map{$1}{ length( $& ) }++; } foreach $c ( sort keys %char_map ){ %char_len_map = %{$char_map{ $c }}; foreach $n ( sort keys %char_len_map ){ print $c,' ', $n, ' ', $char_len_map{ $n }, "\n"; } } 以下が実行結果: > perl a.pl h 2 1 h 3 3 p 19 1 p 2 1 p 3 1 p 7 1 ポイントは、tasekiさんがアドバイスしているように、「連続数が何個あるか」という問題を、 step.1)連続する文字列毎に切り出す。 step.2)切り出した文字列の、文字種と文字列長をカウントする。 という問題に分割して解決してるところ。
その他の回答 (4)
- taseki
- ベストアンサー率66% (155/233)
> コマンド画面上だと不便なので、 > ファイルに出力したい $check_char = 'p'; open(IN, './text1.txt') or die ; $_ = <IN>; close(IN); foreach (split(/[^$check_char]+/)) { $len_list{(length)}++; } open(LOG, '>./MOJICount.log') or die; foreach (sort { $a <=> $b } keys %len_list) { print LOG "$check_char が $_ 個連続している部分は $len_list{$_} ヶ所あります。\n"; } close(LOG);
お礼
本当に助かりました。 御礼が遅くなり申し訳ございません。 ここで失礼致しますが、 みなさんの回答は本当に参考になり、 大変助かりました。 心より御礼を申し上げて、締め切りとさせて頂きます。
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
19個のpを数える場合 ------------------- $_=<IN>; $n= s/(^|[^p])p{19,19}(?!p)//g; print $n;
お礼
御礼が遅くなり申し訳ございません。 有り難うございました!
- taseki
- ベストアンサー率66% (155/233)
> つまり改行はありません。 ということは、whileループやchompは不要ですね。 open(IN, "text1.txt") or die ; $all_data = <IN>; close(IN); で、全データを読み込めます。 > 重複なしにカウント とおっしゃる意味は、たとえば「pppppp」の中には「ppp」が2つ含まれているので「pppが2個」とはカウントしない、という意味でしょうか。 目的の連続数の他はカウントしなくていいなら、ANo.1の方法よりもっと簡単です。配列に各連続数を保存しなくていいので、 たとえば、文字列から「ppppp」を削除、というのを繰り返して、何回繰り返したら無くなったのか?を数えれば良いと思います。 以下サンプルコードです。 -------------------- $text = 'pphhhppphhhppppppphhppppppppppppppppppphhh'; # $textの中に「'p'の7連続が何個あるか」が$resに入る $res = &len_count($text, 'p', 7); print $res; sub len_count { my($txt, $char, $len) = @_; my $cnt = 0; while ($txt =~ s/(?<!\Q$char\E)(?:\Q$char\E){$len}(?!\Q$char\E)//) { $cnt++; } return $cnt; } -------------------- ポイントは、「pに挟まれていないpのX個連続」を探すようにすることです。
お礼
御礼が遅くなり申し訳ございません。 有り難うございました。
- taseki
- ベストアンサー率66% (155/233)
ちょっと解らないのですが、 text1.txtの中身と、実行後のtext1out.txtの中身を教えてください。 text1.txtの中身は、「pphhhppph~」のような行が“複数行”入っている、と考えていいのでしょうか? 「while」ループで“全行”に対してチェックして、条件に合えばカウントアップしますが、条件に合おうが合うまいが毎回(全行分)出力はしてますよね。カウンターのクリアもないようですが。 いまいちコードの意味が解らないのですが…。 単純に「pphhhppph~」のような文字列から「X回連続したpはいくつ含まれるか」を知りたいなら、たとえば以下のロジックでどうでしょうか。 1. 与えられた文字列を、「pではない文字」で区切って配列にする。 2. 配列の各要素の文字数を調べ、文字数をキーとするハッシュをカウントアップする。 4. 知りたい連続数がXなら、ハッシュ{'X'}に、目的の値が入っている。
補足
早々にお返事どうも有難うございます! 本当に助かります!!! 実は、カウントするスクリプトの書き方も よくわかっていない状況です。。。。 text1.txtの中身は pphhhppphが何千何万と続いています。つまり改行はありません。 実行後の text1out.txt はどんなかたちでもいいのです。 ただ、pの連続数が10のものが何個あったか だけでもわかるようになっていれば。。。 ただ、pの連続数は2から20まで、 各連続数ごとに何個あったか重複なしにカウントしたいのです。 ややこしくて本当に申し訳ありません。
補足
感動しております。 本当に感謝です! ただ、結果が多いので、コマンド画面上だと不便なので、 ファイルに出力したいと思い、 以下のように書いてみたのですが、 うまく出力できないのです。 朝からずっと考えていろいろ試しているのですが、 全くできません。 申し訳ありませんが、教えて頂けませんか? #!/usr/local/bin/perl $_ = 'pppppppuuuuuuuuuuuuuuuuuuuufffffffffffffffpppuuu'; open(LOG,">MOJICount.log"); while ( s/^(.)\1*// ){ $char_map{$1}{ length( $& ) }++; } foreach $c ( sort keys %char_map ){ %char_len_map = %{$char_map{ $c }}; foreach $n ( sort keys %char_len_map ){print LOG "$c,' ', $n, ' ', $char_len_map{ $n }, \n"; close(LOG);}}