- ベストアンサー
テキストから重複した文字列を取り除くスクリプトを作成する方法
- テキストから重複した文字列を取り除くスクリプトを作成する方法について教えてください。
- スクリプトを実行すると、テキスト内の「END」と「END」の間、または「END」と最初・最後の間の重複した文字列が取り除かれます。
- 質問者様が作成したコードでは、「END」と「最初・最後の間」の取り扱いについてわからないとのことです。どのように取り扱えば良いですか?
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
@ARGV = ('datafile'); my %h; print grep { ! exists $h{$_} && ++$h{$_} && !(/^END$/ && undef %h) } <>;
その他の回答 (3)
- Tacosan
- ベストアンサー率23% (3656/15482)
「うまく動かない」というなら, 「何がどう『うまく動かない』のか」くらい書けない? 前のやつも「同一の単語が出現しているとエラーが起きてしまいました」で終わらせてるけど, 「どんなエラーが起きるのか」は書けるはずだよね (そもそも普通に考える限り「エラーが起きる」ことがありえないと思うのだが). それくらいやってもバチはあたらないと思うよ. 本題に入ると, #1 で言われている「@zzには最後のEND以降の部分が残ってます。」というのは, 実際にやってみればすぐわかります. そのままでは「最後の END より後ろの部分」が出力されないはずです. ただ, このプログラムは不自然. 自分だったら sub uniqArray { my %hash; my @result; foreach my $element (@_) { unless ($hash{$element}) { push @result, $element; $hash{$element} = 1; } } @result; } open my $fh, '<', 'datafile'; my @tmp; while (my $line = <$fh>) { chomp($line); push @tmp, $line; if ($line eq 'END') { foreach my $element (uniqArray(@tmp)) { print "$element\n"; } @tmp = (); } } foreach my $element (uniqArray(@tmp)) { print "$element\n"; } くらいかなぁ. #1 で言われるように, 表示するところまでサブルーチンにしそうだけど. 「動かない」とか「エラーが出る」とか答えてくれるのは全くかまわないけど, そういうときは最初に書いたように「どういう入力に対してどんな結果になることを期待したが実際に得られた結果はそれと違ってこんなふうになっている」とか, あるいはエラーが出るというならどんなエラーなのか, メッセージを完全に書いてほしい. それくらいの手間は惜しむものじゃないと思う.
お礼
回答ありがとうございます。以前にもエラーの内容もかくようにおっしゃられていたのを覚えています。 これからは、きちんとエラーの結果の内容も書こうとおもいます。アドバイスありがとうございました。
- shiren2
- ベストアンサー率47% (139/295)
こんな感じですかね。 こういう問題はハッシュを使うと簡単ですよ。 インデントは全角スペースになっています。 #!/usr/bin/perl use strict; my %h; while(<DATA>){ chomp; if(/^END$/){ undef %h; print "END\n"; }else{ if(not exists $h{$_}){ print "$_\n"; $h{$_} = 1; } } } __DATA__ apple best apple END apple beer beer END zero child death zero
お礼
あまりにも簡潔なコードにびっくりしました!上級者のコードがみれて参考になりました。ありがとうございます。
- kmee
- ベストアンサー率55% (1857/3366)
> open(IN, "datafile"); > @xx = <IN>; INはここまでしか使わない(使えない)ので > close(IN); > if ($yy eq "end"){ 「eq」は正しく「等しい文字列」の判定です。大文字小文字は区別されます。 'END' ne 'end' です > foreach $yy (@xx) { ... > } このループが終わった段階で @zzには最後のEND以降の部分が残ってます。 ここでもう一度一連の処理(↓)が必要です。これもsubにしておいてもいいでしょう @uniq = uniqArray(\@zz); foreach my $value ( @uniq ){ print "$value"; # ← あと、 @xx=<IN>で読み込んだ文字列は改行コードまで含まれます。 #ここで\nを入れていると、改行が2つになります #あるいは、\nはそのままにして、 foreach $yy (@xx) {の後で chomp $yy 等で改行コードを取り除くか。 }
お礼
回答ありがとうございます。残念ながら説明がよくわかりませんでした。 open(IN, "datafile"); @xx = <IN>; close(IN); @zz = (); foreach $yy (@xx) { chomp $yy; if ($yy eq 'END'){ @uniq = uniqArray(\@zz); foreach my $value ( @uniq ){ print "$value\n"; } @zz = (); }else{ push(@zz,$yy); } } sub uniqArray{ my $array = shift; my %hash = (); foreach my $value ( @$array ){ $hash{$value} = 1; } return( keys %hash ); } と直すところまではわかりました。しかし > foreach $yy (@xx) { ... > } このループが終わった段階で @zzには最後のEND以降の部分が残ってます。 ここでもう一度一連の処理(↓)が必要です。これもsubにしておいてもいいでしょう の意味がわかりません。 @zz = (); で初期化しているのではありませんか? # ← あと、 @xx=<IN>で読み込んだ文字列は改行コードまで含まれます。 #ここで\nを入れていると、改行が2つになります #あるいは、\nはそのままにして、 foreach $yy (@xx) {の後で chomp $yy 等で改行コードを取り除くか。 とのことですが、 foreach $yy (@xx) { chomp $yy; if ($yy eq 'END'){ @uniq = uniqArray(\@zz); foreach my $value ( @uniq ){ print "$value\n"; } @zz = (); }else{ push(@zz,$yy); } } でよかったのでしょうか? いずれにせよ、直してもうまくうごきませんでした。 どなたかアドバイスお願いします。
お礼
回答ありがとございます。とてもスマートですね。perlは、テキスト操作に関しては本当にCより便利だと感じました。