[Perl]foreach if 抽出

このQ&Aのポイント
  • [Perl]foreach if 抽出 メールヘッダに対して指定の条件を満たすデータを抽出する方法
  • Perlのforeachとif文を使用してメールヘッダから特定のデータを抽出する方法について教えてください。
  • Perlのコードを使用してメールヘッダから特定の条件を満たすデータを抽出する方法を教えてください。
回答を見る
  • ベストアンサー

[Perl]foreach if 抽出

質問させていただきます。 メールヘッダに対して以下のプログラムを実行します。 ソースコード ~略 my $file_data_str = join("", @file_data); my @received_datas = split(/Received:/, $file_data_str); my @received_datas = reverse(@received_datas); foreach my $sorted(@received_datas){ if($sorted =~ /by\s\w+.abc.jp/){ $sorted =~ /(\[\d+\.\d+\.\d+\.\d+\])/; print $sorted; print "\n"; } } } exit; を実行すると以下のようにに実行結果が出てきます。 from 158.217.207.30 (114.114.96.20 [114.114.96.20]) by ************abc.jp from ****************.jp (***************.jp [158.217.208.15]) by ***********abc.jp 私は [114.114.96.20]だけを取りだしのですがどうしたらいいでしょうか? foreach my $sorted(@received_datas){ if($sorted =~ /by\s\w.abc.jp/){ $sorted =~ /(\[\d+\.\d+\.\d+\.\d+\])/; ここをどうにかすればいけると思うのですが詰まってしまって… 申し訳ないですが助言をお願いします。

  • Perl
  • 回答数3
  • ありがとう数1

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

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

「一番最初にマッチした値を知りたい」のなら, マッチした時点でループから脱出すればいいじゃん.

blitzcrank
質問者

補足

それに気がつかなかった私はほんとおろかでした…;;

その他の回答 (2)

  • hirotn
  • ベストアンサー率59% (147/246)
回答No.3

ifでマッチした後でも$1は有効だったかと思います。 foreach $sorted (@received_datas){ if(($sorted =~ /by\s\w+.abc.jp/)and ($sorted =~ /\[(\d+?\.\d+?\.\d+?\.\d+?)\]/)){ $ipadd = $1; print "ip $ipadd\n"; last; #<-ループを抜ける } } \d.+? は最短一致(最も短く数字だけを抜き出す) \d.+は最長一致(最も長く数字だけを抜き出す) の違いになります。\.があるので今回の場合はなくてもいいです。 \wはハイフンは入らないですが大丈夫ですね?

blitzcrank
質問者

お礼

最終的にhirotn様が書いてくれたような同じようなプログラムになりました。本当に助かりました。ありがとうございます。 \wに関しては今回は大丈夫でした。ご忠告ありがとうございます。

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

この状態だと、 $sorted =~ /(\[\d+\.\d+\.\d+\.\d+\])/; は何も効果ありません。 この式は、 $sorted を対象として、 /(\[\d+\.\d+\.\d+\.\d+\])/ にマッチするかどうかの評価をします。 しかし、$sortedは検索対象に使っただけで変換はしません。そのため、print $sorted;では、元々の文章がそのまま出力されます。 正規表現中()で抽出していますが、その抽出した$1を使っていません。 $sorted自体を変更する(foreachの性質上、@recieved_datas自体も書き変わる)なら、 s/// で()の中だけ残すとか。 $sorted =~ s/.*(\[\d+\.\d+\.\d+\.\d+\]).*/$1/ ; # 前後を削除 抜き出したものだけ使いたいなら、内容が変化する前なら$1 を使うとか、$1を別な変数に代入するとか。 print $1 . "\n" ; # $1をそのまま使う $IPaddress = $1 ; # 別な変数に代入する

blitzcrank
質問者

補足

ご回答ありがとうございます。 質問した後、ちょこちょこいじりつつ、kmee様の回答を参考にしました。 if(($sorted =~ /by\s\w+.abc.jp/) and  ($sorted =~ /(\[\d+\.\d+\.\d+\.\d+\])/)){ $& =~ /(\d+\.\d+\.\d+\.\d+)/; $ipadd = $1; print $ipadd; print "\n"; } } } exit; 最終的に”[]”このかっこがない状態で出力したいので $& =~ /(\d+\.\d+\.\d+\.\d+)/; をいれています。 一応結果として$&にはIPアドレスはでるのですが、初めの質問に書いたメールヘッダのそれぞれのReceivedフィールドのIPアドレスが抽出されています。(以下のように) 114.114.96.20 #ここだけほしい 158.217.208.15 上の一つだけのIPアドレスを出力するにはどうしたらいいでしょうか。 申し訳ありませんが、続けて助言お願いできませんか。

関連するQ&A

  • [Perl]抽出、出力について

    質問させていただきます。 メールヘッダに対して以下のプログラムを実行します。 ~略 $file_data_str = join("", @file_data); @received_datas = split(/Received:/, $file_data_str); @received_datas = reverse(@received_datas); foreach $sorted(@received_datas){ if(($sorted =~ /by\s\w+.abc.jp/) and ($sorted =~ /(\[\d+\.\d+\.\d+\.\d+\])/)){ $& =~ /(\d+\.\d+\.\d+\.\d+)/; $ipadd = $1; print $ipadd; print "\n"; } } } exit; すると、by\s\w+.abc.jpを含むReceived行のIPアドレスが全て抽出され、複数出力されます。(以下実行結果) 114.114.96.20 158.217.208.15 私はこの初めの"114.114.96.20"のみを出力したいのですが、どうしたらいいでしょうか? 助言のほどをお願いいたします。

    • ベストアンサー
    • Perl
  • [Perl]抽出について

    再度質問させていただきます。 本当は質問などせずに自力で解決したいのですがなかなかうまくいかなくて困っています… 前回質問させていただいたとき(http://okwave.jp/qa/q7855819.html)には問題なかったのですが、事情がかわってしまい、ifの中の条件を変更しなければならなくなりました。今回はIPアドレスではなく時間抽出です。 以下のメールヘッダに対して、後述するプログラムを実行します。 Received: from ************.jp (**********.jp [158.217.43.2]) by **********.abc.jp ; Wed, 29 Feb 2012 12:17:31 Received: from ************** (localhost [127.0.0.1]) by localhost (Postfix) with SMTP ; Wed, 29 Feb 2012 12:16:23 Received: from **************.jp (***********.jp [158.217.208.11]) by ************.abc.jp ; Wed, 29 Feb 2012 12:15:54 #ここがほしい Received: from **************.jp (***********.jp [158.217.208.11]) by ************.xyz.jp ; Wed, 29 Feb 2012 12:15:14   From: ************** To: *********@***********abc.jp Subject: = ***** Date:Wed, 01 Mar 2012 21:06:24   #ここがとれてしまう . . . 本文 ~略 $file_data_str = join("", @file_data); @received_datas = split(/Received:/, $file_data_str); @received_datas = reverse(@received_datas); foreach $sorted(@received_datas){ if(($sorted =~ /abc.jp/) and ($sorted =~ /(\w+,\s+\d+\s+\w+\s+\d+\s+\d+:\d+:\d+)/)){   $new_file_name = str2time($1);   $min1 = $new_file_name / 300;   $min1_cut = int( $min1 );   print $min1_cut; last; } } } exit; まず、Received:で区切って、reverseしているのではじめの要素は、元のメールヘッダの最後のReceivedフィールドから本文まであることになります。更にIfで指定してる条件のアドレスがTo:にも出現するのでそこも判定されます。なのでプログラムを実行すると、To:のabc.jpとDateの時間にひっかかってしまい、Dateの時間情報を抽出してしまいます。 あくまで取りたいのはReceivedフィールドの中の時間情報なのですが、何かいいやり方はありませんでしょうか?ちなみに やり方としては、初めの要素を消す、shiftとか使えるかな、と考えたりもしましたがうまくいかず…、Receivedフィールドのみで判定させたいです。 ご教授願えたらと思います。よろしくお願いします。

  • Perlのキャプチャ

    プログラム #!/usr/bin/perl my $str = "abc,def,ghi"; ($a,$b,$c) = $str =~ /(.*),(.*),(.*)/; print $a,"\n"; print $b,"\n"; print $c,"\n"; 実行結果 abc def ghi このようなプログラムで ($a,$b,$c) = $str =~ /(.*),(.*),(.*)/;は それぞれ$a,$b,$cにabc,def,ghiが入ります. キャプチャしたすべてを変数に入れるならこの方法ですが, defだけが欲しいのに変数を3つ用意する必要がないと思います. 間違った表記ですが, ($b) = $str =~ /(.*),(.*),(.*)/$2/; 2つ目にキャプチャした$2が変数$bに入るような表記は出来ないのでしょうか?

  • perl 初心者です。 わかりやすくお願いします。

    Perlを始めたのですが、本や色いろんなサイトを見てもよく分かりません。 Aと言うデータを読み込んで最大最小・平均・標準偏差を求めたいです。 (1)どこが間違っているのか1部づつでもいいので詳しく教えてください。 (2)一つ一つの意味がちゃんとはつかめていないと感じるので流れを教えてください。 #!/usr/bin/perl # 12345 STDIN use strict; use warnings; open ( FILEHANDLE , " < A " ) ; my @Str=<STDIN>; foreach my $Row (@Str ){ print $Row; } my $Minimum=$ARGV[0]; my $Maximum=$Minimum; my $Sum=$Minimum; my $temp=0; my $i=1; while ( $i < $Num_arg){ $temp=$ARGV[$i]; if ( $Minimum > $temp ) { $Minimum = $temp; }elsif ( $Maximum < $temp ){ $Maximum = $temp; } $Sum = $Sum + $temp; $i++; } my $Average = $Sum / $Num_arg; my $w = foreach my $w(0..$#Numbers){ ($Num_arg - $Average) ** / Num_arg; } my $Standarddivitation = sqrt ($w); print "Average value = $Average \n"; print "Maximum Value = $Maximum \n"; print "Minimum Value = $Minimum \n"; print "Standard devitation = $Standarddevitation;

  • perlがうごきません

    <html> <head> <title>form3</title> </head> <body> <form action="form3.cgi" method="post" name="form3"> <input type="text"name="form3"> <input type="submit"value="送信"> </form> </body> </html> ::::::::::::::::::::::::::::::: #! /usr/bin/perl if($ENV{"REQUEST_METHOD"} eq "GET"){ $str=$ENV{QUERY_STRING"}; }else{ read(STDIN, $str, $ENV{"CONTENT_LENGTH"}); } $str=~ tr/+/ /; $str=~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("c", hex($1))/eg; foreach(split(/&/, $str)){ my ($key, $value)=split(/=/, $_); $data{$key}=$value; } print "Content-type:text/html\n"; print "\n"; print "<html>\n"; print "<head><title>form3</title></head>\n"; print "<body>$data{'form3'}</body>\n"; print "</html>\n"; exit; :::::::::::::::::::::::::::::::: どうしてもうごきません。 どこかスクリプトがまちがっているのでしょうか。 パールのパスはあっています。

    • ベストアンサー
    • Perl
  • Perlでのforeach文の挙動がわかりません

    Perlに詳しい方教えてください。 WinXP(SP3)+ActivePerl-5.10.0.1004の環境で、重複データのチェックプログラムを 作成していますが、foreach文の挙動がよく分かりませんので教えてください。 ※Perlは独学です。 <入力ファイル> あいうえお あいうえお かきくけこ あいうえお <出力期待値その1> あいうえお,重複しています あいうえお,重複しています かきくけこ あいうえお,重複しています <出力期待値その2> あいうえお,重複しています あいうえお,重複しています かきくけこ あいうえお <プログラムの要約> open(IN,$input_file); # 入力ファイルを読み込み @BASE = <IN>; close(IN); @BASE_2 = @BASE ; # 配列のコピーを作成 $count_1 = '0' ; foreach $data_1 (@BASE) {   $count_1++ ; # 何行目を処理しているかのカウンター   $flag_1 = '0' ; # フラグの初期化   $count_2 = '0' ;   foreach $data_2 (@BASE) { # ←★ここの記述方法の質問です★     $count_2++ ;     if ( $count_1 == $count_2 ) { next; } # 自分自身の行とは比較しない     if ( $data_1 eq $data_2 ) { $flag_1 = '1' ; } # 一致したらフラグを立てる   }      if ( $flag_1 == '0' ) { # フラグが立たなかったらそのまま新しい配列へ追加     push(@kekka,$data_1);   } else { # フラグが立ったらコメントを追加して新しい配列へ     $data_1 =~ s/\n//g;     $data_3 = $data_1 . ",重複しています\n" ;     push(@kekka,$data_3);   } } $kekka_2 = join("",@kekka) ; # 配列のデリミタ対策 open(OUT,"> $output_file"); print OUT "$kekka_2"; close(OUT); exit; <質問> 上記プログラムの★マークの箇所で、 foreach $data_2 (@BASE_2) としますと、<出力期待値その1>が得られます そして、 foreach $data_2 (@BASE) としますと、<出力期待値その2>が得られます プログラムの途中で @BASE_2 = @BASE としていますので、一見しますと 同じ処理をしているように感じるのですけれども、実際には出力結果が異なり ますので、何かが違うのだと思います。 その違いを教えて戴きたいです。 ※投稿確認画面でTABが消えてしまいますのでTABは全角スペースに変更して  おります。  見えづらい場合には申し訳ありません

    • ベストアンサー
    • Perl
  • perlでCSVをソートする方法について

    perl初心者です。いつもありがとうございます。 perlでcsvファイル(1行のカラム数は200)、総行数は約3万行のファイルを37番目のカラム(-25以上25未満の数値データ)で降順ソートしその値によって行数がだいたい均等になるよう3分割し、2番目のカラムに文字でも数字でもよいのですがその4つのグループごとにフラグ(例えば1,2,3)を入れたいと思ってます。グループ化については境目の37番カラムの値は重複している場合が多いと思うのですがその場合は下(別に上でもかまいません)に入れるものとします。 ソートロジックは過去の質問を参照して理解しましたがグループ化しフラグを入れるルーチンがうまく作れません。下記のように作ったのですがこの先同じことを何度もやらなくてはならないので先に進めません。どなたかお助けください。最終的にやりたいことはカラム37でグループ化→カラム2にフラグを立てる、次にカラム2とカラム38(-25から0までの数値)でソートし同様に同じ行数になるようにグループ化→カラム3にフラグを立てる、さらにカラム2とカラム3とカラム39(-25以上25未満の数値データ)でソートし・・・同様に繰り返し最終的に1グループが100件(行)~150件(行)になるようにしたいのです。つまり約3万件のデータを3*4*2*4*2=192分割(5列の値で分類)したい、そしてどのような範囲で分割したかという情報も得たいのです。 use strict; use warnings; use utf8; use Encode; binmode STDOUT, ':encoding(utf-8)'; my $dir = './data'; # 処理するディレクトリ my $motoFile = 'customer.txt'; # もとファイル open my $fh, '<:encoding(cp932)', "$dir/$motoFile" or die 'ファイルが開けません。',"$!"; my %sorted; while (my $line = <$fh>) { my $key = (split /,/, $line)[37]; push @{$sorted{$key}}, $line; if (@{$sorted{$key}} == 1000) { open OUT, '>>:encoding(cp932)', "$dir/$key.tmp" or die "Can't open: $!"; print OUT @{$sorted{$key}}; close OUT; @{$sorted{$key}} = (); } } open OUT, '>:encoding(cp932)', "$dir/out.txt" or die "Can't open: $!"; foreach my $key (sort { $b <=> $a } keys %sorted) { if (-e "$key.tmp") { open IN, '<:encoding(cp932)', "$dir/$key.tmp" or die "Can't open: $!"; print OUT while <IN>; close IN; } print OUT @{$sorted{$key}} if @{$sorted{$key}}; } close OUT; #↓↓↓↓ここからフラグを作成するルーチン # 行数を調べ3つに分けるルーチン my @colum37; open IN, '<:encoding(cp932)', "$dir/out.txt" or die 'ファイルが開けません。',"$!"; my @in = <IN>; close IN; my $gyousuu = scalar(@in); my $amari = $gyousuu % 3; if ($amari == 0) { my $groupGyousuu = ($gyousuu-$amari)/3; print "総行数は$gyousuu","で、1グループの行数は$groupGyousuu","ほど、余りは$amari\n"; # あまりが0の時、group1は@inの0行 ~$groupGyousuu-1行まで #         group2は@inの$groupGyousuu行 ~$groupGyousuu*2-1行まで #         group3は@inの$groupGyousuu*2行~$groupGyousuu*3-1行まで foreach my $num (1..2) { push @colum37, (split /,/, $in[$groupGyousuu*$num])[37]; # これは境目の先頭の37番目 } print "@colum37\n"; #これでここまでは完成、分けるべき値がこの配列に入っている。 open OUT, '>:encoding(cp932)', "$dir/out.txt" or die "Can't open: $!"; foreach my $line (@in) { my @line = split /,/,$line; if ($line[37]>=$colum37[0]) { $line[1] = 1; }elsif ($line[37]>=$colum37[1] and $line[37]<$colum37[0]) { $line[1] = 2; }elsif ($line[37]<$colum37[1]) { $line[1] = 3; } $line = join (',',@line); print OUT $line; } close OUT; } elsif ($amari == 1) { この後未作成

    • ベストアンサー
    • Perl
  • perl サブルーチンでのファイル出力結果おかしい

    以下のコードを実行するとカレントディレクトリの配下にある すべてのファイルのリストがコンソールとファイルに出力される はずですが、コンソールに表示されているファイルの一部しか ファイルに出力されていません。 どうも、最後に do_file()を呼び出したときのファイルしか リストされていないようなのですがなぜでしょうか。 どのようにすればよいのでしょうか。 よろしくお願いします。 (Windows7, ActivePerl(v5.16.3)) ----test.pl--------------------------------------------- &do_dir('.'); sub do_dir{  open(FILE2,'>list.txt') or die "$!";  my $dirname=shift;  my $delim='/';  opendir(DIR,$dirname) or die "$!";  foreach $entry (readdir(DIR)){   next if($entry eq '.');   next if($entry eq '..');   if ($dirname=~/[\\\/]$/) {    my $delim='';   }   my $filename="$dirname$delim$entry";   if(-d $filename){    &do_dir($filename);   } else {    &do_file($filename);   }  }  close(DIR);  close(FILE2); } sub do_file{  my $filename=shift;  return unless ($filename=~/\.*$/);  print "$filename\n";  print FILE2 "$filename\n"; }

    • ベストアンサー
    • Perl
  • Perlの文字列置換について

    質問です。文字列の置換を行いたいのですが、まずはソースから・・・ use File::Basename; use File::Copy; use File::Find; use File::Path; my @filelist = (); $inputfile='C:\Users\test.txt'; $outputfile='C:\Users\test_new.txt'; open (IN, $inputfile) or die "$!"; open (OUT, ">$outputfile") or die "$!"; while(<IN>) { $str = $_; if ($str =~ /html:text/) { $str =~ s/html:text/s:textfield/g; $str =~ s/property/name/; $str =~ s/styleClass/cssClass/; elsif($str =~ /html:textarea/) { $str =~ s/html:textarea/s:textarea/g; $str =~ s/property/name/; $str =~ s/style/cssStyle/; $str =~ s/styleClass/cssClass/; } print OUT $str; } close (IN); close(OUT); sub wanted{ push(@filelist, $File::Find::dir."/$_") unless ($_ =~ /^\.$/); } Perlプログラムで下記のソースを一括置換します。 <html:text property="xxx" styleClass="yyy"> <html:textarea property="abc" styleClass="def" style="ghi"> 目標は以下のように置換したいです <s:textfield name="xxx" cssClass="yyy"> <s:textarea name="abc" cssClass="def" cssStyle="ghi"> しかし、上記のプログラムを実行すると <s:textfield name="xxx" cssClass="yyy"> <s:textfieldarea name="abc" cssClass="def" style="ghi"> となってしまいます。 どこをどう直せばよいのかわからないので誰か教えてください。 お願い致します!!

    • ベストアンサー
    • Perl
  • perl-cgiのリネームについて

    Perl-CGIで ABCという、ディレクトリの中のファイルの名前を、ランダムな名前に変換したいのですがうまくいきません。 これを動作させるたびになぜか、どんどんファイルが減っていってしまいます。 どなたか、教えていただけないでしょうか? 宜しくお願い致します。 #!/usr/local/bin/perl print "Content-Type: text/plain\n\n"; $| = 1; my ($sec,$min,$hour,$mday,$mon,$year,$wno) = localtime(time); my ($nowtime) = sprintf("%02d_%02d_%02d_%02d_%02d_",$year+1900,$mon+1,$mday,$hour,$min,$sec); #ディレクトリのファイル個数を記録する $dir = "./ABC/"; # ← ディレクトリを変数にセットする opendir DIR, $dir; @files = grep { !m/^(\.|\.\.)$/g } readdir DIR; # ← 「.」 「..」 以外のファイルを取得 close DIR; srand; for (my $i = @files; --$i; ) { my $j = int rand ($i + 1); next if $i == $j; @files[$i, $j] = @files[$j, $i]; } $num = 0; use File::Copy; foreach(@files){ $getpath = "$dir"."$_"; if( copy($getpath, "$dir".$nowtime.$num++.'.dat') eq 1){ $num++; unlink($getpath);}else{print "Copy Error"; exit;} }

    • ベストアンサー
    • CGI

専門家に質問してみよう