テキストから重複した文字列を取り除くスクリプトを作成する方法

このQ&Aのポイント
  • テキストから重複した文字列を取り除くスクリプトを作成する方法について教えてください。
  • スクリプトを実行すると、テキスト内の「END」と「END」の間、または「END」と最初・最後の間の重複した文字列が取り除かれます。
  • 質問者様が作成したコードでは、「END」と「最初・最後の間」の取り扱いについてわからないとのことです。どのように取り扱えば良いですか?
回答を見る
  • ベストアンサー

なにがおかしいのでしょうか?

先の質問「プログラムのヒントを下さい」でも扱ったのですが、 apple best apple END apple beer beer END zero child death zero のようなテキストから、ENDとENDの間か、ENDと最初もしくは最後の間の重複した文字列を取り除くスクリプトを書こうと思います。出力例は以下のようになってもらいたいです。 apple best END apple beer END zero child death 自分で頑張って下のコードまで書きましたが動きませんでした。特に、ENDと最初もしくは最後の間 の取り扱いがわかりません。どなたかご教授お願いいたします。 open(IN, "datafile"); @xx = <IN>; @zz = (); foreach $yy (@xx) { if ($yy eq "end"){ @uniq = uniqArray(\@zz); foreach my $value ( @uniq ){ print "$value\n"; } @zz = (); }else{ push(@zz,$yy); } } close(IN); sub uniqArray{ my $array = shift; my %hash = (); foreach my $value ( @$array ){ $hash{$value} = 1; } return( keys %hash ); }

noname#182748
noname#182748
  • Perl
  • 回答数4
  • ありがとう数7

質問者が選んだベストアンサー

  • ベストアンサー
  • N60-BASIC
  • ベストアンサー率80% (17/21)
回答No.4

@ARGV = ('datafile'); my %h; print grep { ! exists $h{$_} && ++$h{$_} && !(/^END$/ && undef %h) } <>;

noname#182748
質問者

お礼

回答ありがとございます。とてもスマートですね。perlは、テキスト操作に関しては本当にCより便利だと感じました。

その他の回答 (3)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

「うまく動かない」というなら, 「何がどう『うまく動かない』のか」くらい書けない? 前のやつも「同一の単語が出現しているとエラーが起きてしまいました」で終わらせてるけど, 「どんなエラーが起きるのか」は書けるはずだよね (そもそも普通に考える限り「エラーが起きる」ことがありえないと思うのだが). それくらいやってもバチはあたらないと思うよ. 本題に入ると, #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 で言われるように, 表示するところまでサブルーチンにしそうだけど. 「動かない」とか「エラーが出る」とか答えてくれるのは全くかまわないけど, そういうときは最初に書いたように「どういう入力に対してどんな結果になることを期待したが実際に得られた結果はそれと違ってこんなふうになっている」とか, あるいはエラーが出るというならどんなエラーなのか, メッセージを完全に書いてほしい. それくらいの手間は惜しむものじゃないと思う.

noname#182748
質問者

お礼

回答ありがとうございます。以前にもエラーの内容もかくようにおっしゃられていたのを覚えています。 これからは、きちんとエラーの結果の内容も書こうとおもいます。アドバイスありがとうございました。

  • shiren2
  • ベストアンサー率47% (139/295)
回答No.2

こんな感じですかね。 こういう問題はハッシュを使うと簡単ですよ。 インデントは全角スペースになっています。 #!/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

noname#182748
質問者

お礼

あまりにも簡潔なコードにびっくりしました!上級者のコードがみれて参考になりました。ありがとうございます。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

> 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 等で改行コードを取り除くか。 }

noname#182748
質問者

お礼

回答ありがとうございます。残念ながら説明がよくわかりませんでした。 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); } } でよかったのでしょうか? いずれにせよ、直してもうまくうごきませんでした。 どなたかアドバイスお願いします。

関連するQ&A

  • プログラムのヒントを下さい

    C言語は勉強しましたが、perlはまだまだ初心者の者です。 統計ソフトRの処理のためのスクリプトを書こうと思ってますが、うまくいかずに悩んでいます。 ファイルに以下の用に任意個の文字列が記述されています。ただし、ファイルの途中にはEND が複数かかれており、END以降も文字列が続きます。 apple best china dutch END apple beer END child death zero このファイルを受け取って、 c("apple","best,"chine","dutch"),c("apple","beer"),c("child","death", ,"zero") のようにENDが出てくるまでc(" ",...)を繰り返しを出力するスクリプトを書こうと思っています。 open(IN, "datafile.txt"); @xx = <IN>; print "c("; foreach $yy (@xx) { print "\"$yy\","; } print ")"; close(IN); のように書くとこまではいけたのですが、この先が進めません。どなたかご教授ください。

    • ベストアンサー
    • Perl
  • 連想配列の要素を追加

    表示結果が XX YY ZZ になるようにしてください。 $a = array(); $a['yy'] = 'YY'; $a['zz'] = 'ZZ'; //ここにコードを書く// foreach($a as &k){ echo $k . '<br>'; }

    • 締切済み
    • PHP
  • 先頭の単語が一致した時のデータ追加

    【データ】 (A) A A2 "one" 7 A 3C three 9 B DD "two" 11 C CDE four 25 C 4D five 33 D YY six 27 ・ ・ (B) A okinawa kagoshima miyazaki B kumamoto oita D fukuoka E saga nagasaki ・ (A)と(B)のデータを比較し、先頭の単語が一致した時のみ、先頭の単語を除いた(B)の行を (A)の末尾に加えるという処理をしたいです。 【目標】 A A2 "one" 7 okinawa kagoshima miyazaki A 3C three 9 okinawa kagoshima miyazaki B DD "two" 11 kumamoto oita C CDE four 25 C 4D five 33 D YY six 27 fukuoka ・ ・ 以前、回答して頂いた方法を踏まえ、以下の処理を行いましたが上記のような結果がでません。 宜しくお願いします。 #!/usr/bin/perl open(FILE1, "<aaa.txt") || die "File1 Open Error! \n"; open(FILE2, "<bbb.txt") || die "File2 Open Error! \n"; open(OUT, ">zzz.txt") || die "OUT Open Error! \n"; my @data1 = <FILE1>; my @data2 = <FILE2>; chomp @data1; chomp @data2; foreach my $line1 (@data1) { my @array1 = split(/\t/, $line1); push @{$hash1{$array1[0]}}, @array1[1, -1]; for my $key1 (sort keys %hash1){ foreach my $line2 (@data2) { my @array2 = split(/\t/, $line2); push @{$hash2{$array2[0]}}, @array2[1, -1]; for my $key2 (sort keys %hash2){ if($key1 eq $key2){ print OUT join("\t", ($key2, @{$hash2{$key2}}, @{$hash1{$key1}})), "\n"; } } } } }

    • ベストアンサー
    • Perl
  • ハッシュのリファレンスを用いた処理

    ActivePerl 5.8 , WinXP SP2の環境です。 Perl スクリプトを用い、ファイルから複数のブロックからなる情報をよみとり、個別のハッシュを作り、それをリファレンスの配列としてまとめて後から参照するという操作をしたいのですが、詰まってしまいました。。 例として読み取るファイルは ---input.txt--------- >1 Jan 1 Feb 4 >2 Mar 9 Apr 3 >3 Oct 8 Nov 4 ------------------ ここから1,2,3の個別のハッシュ {Jan => 1 Feb => 4} {Mar=> 9 Apr =>3} {Oct =>8 Nov =>4} を作成し、それぞれのハッシュのリファレンスの配列をつくり、その後からすべてのハッシュの中身を個別に出力させたいと思いました。 次のようなスクリプトを作成したのですが思ったように作動しません。 use strict; open (IN, "input.txt") or die ("cant open file \n"); my $reff; my @array_of_reff; my %hash; my $count = 0; while(<IN>){ my $line = $_; ######ここでは各ブロックの頭の ">"を認識し、2個目以降であれば直前までで作ったハッシュのリファレンス($reff)を配列@array_of_reffに入れる。 if($line =~ /^>/){ if($count >0){ $reff = \%hash ; push (@array_of_reff, $reff); %hash = (); } $count++; } ########ここではアルファベットが入った行を認識して、ハッシュに追加しています if($line =~ /^[A-Za-z]/){           $line =~ /([A-Za-z]+)\s+/; my $month = $1; $line =~ /\s+(\d+)/; my $day= $1; $hash{$month} = $day;     } ###ここはファイルの最後になったら直前まで作っていたハッシュののリファレンス($reff)を配列@array_of_reffに入れる。 if( eof ){ $reff = \%hash ; push (@array_of_reff, $reff); } } #####ハッシュのリファレンスの配列(@array_of_reff)からもとのハッシュを参照し、ハッシュごとに出力 foreach my $reff_of_hash (@array_of_reff){    print "output";    while( (my $key,my $value) = each %$reff_of_hash ){     print "\n" , $key, " : ", $value, ;    } } このスクリプトを実行すると Nov 4 Oct 8 という3つめのハッシュのなかみが3回出力されてしまいます。自分では3つの別のハッシュをつくっているつもりでも、どうやら1種類しか作れていない、もしくはハッシュが上書きされているようなのですが、原因がわかりません。 この例だけ見るとハッシュのリファレンスを使う必要はないのですが、実際にはもうすこし大きいスクリプトで"ハッシュのリファレンスの配列を他のサブルーチンに渡す"ということを想定しており、これが解決できず先に進めない状態です。 アドバイス、解決法がわかったら教えていただけないでしょうか。

    • ベストアンサー
    • Perl
  • 多次元配列を[キー:値]の形でランダム表示に

    <?php $fruit = array( 'apple' => array('ふじ','ジョナゴールド'), 'orange' => array('みかん','バレンシア'), 'grape' => array('巨峰','マスカット') ); foreach ($fruit as $key => $value) { foreach ($value as $key2 => $value2) { echo '<p>' . $key . ' : ' . $value2 . '</p>' . "\n"; } } ?> 上記コードにて多次元配列をループさせて以下の様に表示させています。 apple : ふじ apple : ジョナゴールド orange : みかん orange : バレンシア grape : 巨峰 grape : マスカット これを下の様にシャッフルした形でランダムに表示させたいと思い、自分なりに試行錯誤してみたのですが、思うような結果を出す事が出来ずに悩んでいます。 何か良い方法がありましたらアドバイスを頂ければと思います。 よろしくお願い致します。 grape : 巨峰 apple : ふじ orange : みかん grape : マスカット orange : バレンシア apple : ジョナゴールド

    • ベストアンサー
    • PHP
  • 再帰の仕方

    現在配列の出力結果が↓なのですがこれを array(2) { [0]=> string(6) "orange" [1]=> array(2) { [0]=> string(6) "apple" [1]=> array(2) { [0]=> string(6) "banana" [1]=> array(2) { [0]=> string(10) "Strawberry" } } } } ↓こっちのように変えたい場合の処理がどうしてもできません。 array(2) { [0]=> string(6) "orange" [1]=> string(6) "apple" [2]=> string(6) "banana" [3]=> string(10) "Strawberry" } 今自分がやってる途中のものです↓ $fruit = array("orange", array("apple", array("banana", array("Strawberry")))); function first_array($fruit) { foreach($fruit as $key => $value) { if(! is_array($value) === true ) { echo $value; } else { first_array($value); } } } $new_array = first_array($fruit); echo で orangeapplebananaStrawberry と表示はされるのですが、 配列に入れる方法がわかりません。 普通にこの部分を ~ if(! is_array($value) === true ) { $array[] = $value; } ~ とすると上書きされてしまっているのかな? 一個しかデータが残ってないのです・・・。 ご教授ください。

    • ベストアンサー
    • PHP
  • perl 配列名変数指定するには

    perlプログラムで for文で ループ分の配列定義するには どうしたらよいですか? 下記のようなことができないかと 考えております。 for(my $i = 0; $i < $file_no; $i++){ my @{"segments$i"} =(); #配列定義 my ${"line$i"}="";     #変数定義 my %{"hash$i"}= ();    #ハッシュ定義 open(ARG1,$ARGV[$i]); while(<ARG1>){ ${'line'.$i} = $_; chomp ${'line'.$i}; @{'segments'.$i} = split(/\t/,${'line'.$i});        ${'hash'.$i}{${'segments'.$i}[0]}=${'segments'.$i}[1];     } close(ARG1); } #下記で、その後 各ハッシュに設定したデータをもとに いろいろ計算したい foreach my $a (keys %{'hash'.$i}){ ・・・ } 今は、Can't declare array dereference in "my" at test.pl line XX, near "} =" と 配列定義でエラーとなり処理できません。

    • ベストアンサー
    • Perl
  • ある文字を含む文字列のみ配列にする方法

    var_dump($text); を行うと array(1) { [0]=> string(XX) "あいさつ" } array(1) { [0]=> string(XX) "いい日旅立ち" } array(1) { [0]=> string(XX) "りんご、うまい" } array(1) { [0]=> string(XX) "メロン好き" } …… となるような変数 $text があり、そこから $key = array("ばなな","りんご","メロン"); の配列内にある文字列を含むものだけを新たに格納したいです。 自分では foreach($text as $value){ if(in_array($value, $key)){ $key_text[] = $value; } } と書いて試したのですが、NULLと返ってきてしまいます。 どうやって書けばいいか教えて下さい。 よろしくお願いします。

    • ベストアンサー
    • PHP
  • 静的ハッシュの配列のキーに対応する値の数の多さ順で表示させたい

    ハッシュのキーに対応する値の数の多さ順で表示させたいと考え、下記の所まで試行錯誤しておりますが、どうにも思ったようにソートできずにおります。 #!/usr/bin/perl use strict; my(%a, $i, $j ,$allarray ,@keys ,@keys2 ,%hash ,%files ,$a_mumei_ref ,$key ,$value ,@value ,$x ,$files); # ハッシュの配列を静的に作る %a = ( '0' => [ qw(0) ], '1' => [ qw(1 1) ], '3' => [ qw(3 3 3) ], '7' => [ qw(7 7 7) ], '2' => [ qw(2) ], '4' => [ qw() ], '5' => [ qw() ], '6' => [ qw() ], '8' => [ qw(8 8) ], '9' => [ qw(9) ], ); @keys = sort { $hash{$b} <=> $hash{$a} || length($b) <=> length($a) || $a cmp $b } keys %a; #ハッシュのキーを数字順で表示 foreach (@keys){ print $_ ."\n"; } # 静的に作ったハッシュの配列を取り出してみる foreach $i (sort keys %a) { for ($j = 0; $j <= scalar(@{$a{$i}})-1; $j++) { print '$a{'. $i. '}['. $j. ']='. $a{$i}[$j]. ' '; } $allarray=scalar(@{$a{$i}})-1; print "No$i:kosuu:$allarray"; print "\n"; #配列の値の個数を調べその配列を作成 my($a_mumei) = $allarray; $a_mumei_ref = \$a_mumei; $files{"$i"}=($i,$a_mumei_ref); } #each関数で%filesの中身を表示 while ( ( $key , $value ) = each %files ){ print "key:$key value:$$value\n" ; } #試行錯誤 foreach $x (sort { $files{$b} <=> $files{$a} } keys %files){ print "$x => $files->{$x}\n"; } @keys2 = sort {$hash{$a} <=> $hash{$b}} keys %files; #@keys2 = sort { $hash{$b} <=> $hash{$a} || length($b) <=> length($a) || $a cmp $b } keys %files; #@keys2 = sort { $hash{$a} cmp $hash{$b} } keys %files; print "@keys2\n"; print "\n"; __END__; 私のイメージしておりますのは、ソートした結果がハッシュのキーに対応する値の数の多さ順で下記のように表示させたいのですが、 どのようにすれば可能でございますか、ご教授願えませんでしょうか key:3 value:2・・・この場合valueは配列の個数 key:7 value:2 key:8 value:1 key:1 value:1 key:9 value:0 key:2 value:0 key:0 value:0 key:6 value:-1 key:4 value:-1 key:5 value:-1

    • ベストアンサー
    • Perl
  • arrayである値を持った要素のみを削除するすっきりした方法。

    PHP4で,ある値を持った要素のみを削除したarrayをかえしたいのですが,たとえば,[d]と[e]をいう値をもった要素を削除したい場合, $a = array("a", "b", "c", "d", "e"); $eraseitem = array("d", "e"); $new_a = array(); foreach($a as $value); { if (!in_array($value, $erasesitem)) { $new_a[] = $value; } } とすれば,削除できますが,ほかに何かいい方法はないでしょうか。(デフォルトの関数などを使用してすっきりとできないのでしょうか。) 詳しい方,よろしくお願いします。

    • ベストアンサー
    • PHP

専門家に質問してみよう