質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Q&A

解決済

1回答

3594閲覧

perl でキーワードからキーワードの間の行を抽出したい

退会済みユーザー

退会済みユーザー

総合スコア0

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

0グッド

0クリップ

投稿2016/10/13 02:14

perl本当の初心者です。とても苦戦しています。

地名1.csv 地名2.csv の2つのファイルがあったとします。

==地名1.csv==
a,b,札幌,c,d,
e,f,沖縄,f,r,
t,u,東京,w,e,
r,u,千葉,e,q,
l,o,金沢,e,c,
p,l,高知,w,w,

==地名2.csv==
沖縄,千葉
高知,千葉

ここで、地名2.csvの1行に書かれた地名に挟まれた行を地名1.csvから
ごっそりと抜き出したのです。。
具体的には、実行すると、

e,f,沖縄,f,r,
t,u,東京,w,e,
r,u,千葉,e,q,

r,u,千葉,e,q,
l,o,金沢,e,c,
p,l,高知,w,w,

が抽出され、それが別ファイルに書き込めたら一番なのです。

地名2.csvを見ての通り、地名2.csvの地名の順番は
地名1.csvの順番と逆なこともあるので、
if文で条件分岐をすればいいのだと思うのですが、
ここがうまく書けません。。

どなたかお知恵を貸していただけないでしょうか。
どうぞどうぞよろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ちょっと力づくですがsampleとして見て下さい。

perl

1my $inputfile="chimei1.csv"; 2open (FILE, $inputfile) or die "$!"; 3my @lines = <FILE>; 4close(FILE); 5 6my $cnt=0; 7$inputfile="chimei2.csv"; 8open(my $data, "<", $inputfile) or die "$!"; 9while (my $line = <$data>) { 10 $line =~ s/[\r\n]+\z//; 11 my @fields = split ",", $line; 12 $cnt++; 13 $outfile="result". $cnt .".csv"; 14 $fields[0] =~ s/^ *(.*?) *$/$1/; 15 $fields[1] =~ s/^ *(.*?) *$/$1/; 16 $pp="[,]+". $fields[0] ."[,]+"; 17 $pq="[,]+". $fields[1] ."[,]+"; 18 foreach my $var(@lines) { 19 if ($var =~ /($pp)/ or $var =~ /($pq)/){ 20 if (fileno(OUT)){ 21 print OUT $var; 22 close (OUT); 23 }elsif (! fileno(OUT)){ 24 open (OUT, ">$outfile") or die "$!"; 25 print OUT $var; 26 } 27 }elsif (fileno(OUT)){ 28 print OUT $var; 29 } 30 } 31}

SJISを想定したperlを作成しました。

perl

1use utf8; 2use Encode 'decode'; 3use Encode 'encode'; 4 5my $inputfile="chimei1.csv.sjs"; 6open (FILE, "<:encoding(cp932)", $inputfile) or die "$!"; 7my @lines = <FILE>; 8close(FILE); 9 10my $cnt=0; 11$inputfile="chimei2.csv.sjs"; 12open(my $data, "<:encoding(cp932)", $inputfile) or die "$!"; 13while (my $line = <$data>) { 14 $line =~ s/[\r\n]+\z//; 15 my @fields = split ",", $line; 16 $cnt++; 17 $outfile="result". $cnt .".csv.sjs"; 18 $fields[0] =~ s/^ *(.*?) *$/$1/; 19 $fields[1] =~ s/^ *(.*?) *$/$1/; 20 $pp="[,]+". $fields[0] ."[,]+"; 21 $pq="[,]+". $fields[1] ."[,]+"; 22 foreach my $var(@lines) { 23 chomp($var); 24 if ($var =~ /($pp)/ or $var =~ /($pq)/){ 25 if (fileno(OUT)){ 26 printf OUT "%s\r\n", $var; 27 close (OUT); 28 }elsif (! fileno(OUT)){ 29 open (OUT, ">:encoding(cp932)", $outfile) or die "$!"; 30 printf OUT "%s\r\n", $var; 31 } 32 }elsif (fileno(OUT)){ 33 printf OUT "%s\r\n", $var; 34 } 35 } 36}

投稿2016/10/13 05:20

編集2016/10/13 12:09
A.Ichi

総合スコア4070

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/10/13 06:14

お力添え、いたみいります。ありがとうございます! 地名2.csvのリストの数ぶんのresult.csvができましたが、中身には result1.csv の中身 e,f,沖縄,f,r, result2.csv の中身 p,l,高知,w,w, という感じで、リストの1列目にあたる段しか切り出しがされないようです。。 また、例えば追加しますが、 ==地名1.csv== a,b,札幌,c,d, e,f,沖縄,f,r, t,u,東京,w,e, r,u,千葉,e,q, l,o,金沢,e,c, p,l,高知城,w,w, p,l,高知,w,w, みたいだったとしたら、「高知」と「高知城」共に一部検索OKというように 「高知」だけひっかけたくても、両方ひっかかってしまうようです。。
退会済みユーザー

退会済みユーザー

2016/10/13 07:03

変更2点ありがとうございます。ただ、今度はカラのリストの数ぶんのresult.csvができました(汗)変更2の影響でしょうか。小さなカンマが大きな意味を持っているようですね。。すみません、本当に初心者で。。
A.Ichi

2016/10/13 07:48

なかなかうまく行かずにすみません、Linux環境では確認していますが、動きが異なるみたいです。修正しました。
退会済みユーザー

退会済みユーザー

2016/10/13 08:08

お手を煩わせてすみません。。いい感じになのですが、 ==地名2.csv== 沖縄,千葉 のとき、 e,f,沖縄,f,r, t,u,東京,w,e,  ←あいだがまだ切り出せないようで、、ここが私も苦戦しております。 r,u,千葉,e,q,
A.Ichi

2016/10/13 08:30

中間を入れるのを忘れておりました。
退会済みユーザー

退会済みユーザー

2016/10/13 09:27

したから6行目の }elsif (fileno(OUT)){ は不要でしょうか 今度は先頭行が書き出せなくなってしまいました・・・ (中間と、最終行のみで) でも、ここまでお力添えを頂き、目から鱗の部分が沢山ありました。 もっともっと勉強します。
A.Ichi

2016/10/13 10:06

6行目は中間を出力するものなので必要と思います。$_を$varとしてみました。私の実行環境では先頭が出ますので環境の違いを見つける必要がありますね。
退会済みユーザー

退会済みユーザー

2016/10/13 10:33

6行目の所でエラーメッセージがでたので#でコメントアウトしたら実行されたので、不要かと勘違いしてしまいました 環境の違いによる結果の違いが、よくわからなくなってしまいます、、 後程修正して再トライしてみます。
A.Ichi

2016/10/13 12:11

windows環境で実行されていますでしょうか?SJISファイル版も作成してみました。
A.Ichi

2016/10/13 12:46

windowsのperl(ActivePerl)で実行してみましたが結果は良さそうです。改行はprintf "%s\r\n"の部分は\nのみで良いみたいです。
退会済みユーザー

退会済みユーザー

2016/10/14 00:37

その後色々と試行錯誤してみました。 +をやはり?に戻してみたり、、 環境等の違いはよくわかりませんが、最終的に以下の形でうまくいきました! 本当に初心者で、何をどのように伺えばよいか、 直接の会話であれば齟齬も生まれにくい所、 文字でどのように書けば伝わるのかも、まだ正直掴みかねている状態です。 でも、周囲の仲間は非常にレベルが高くて、今更的で聞きづらくて、 そんな私に、その中で課された課題が、今の私にはまだちょっとハードルが高かったのです・・・。 申し訳ありませんでした。 ひとつひとつのスクリプトの意味もまずはきちんとつかむところから 復習的に勉強していきたいと思います。 見ず知らずの私にこんなにも力になって頂き、 本当に本当にありがとうございました。勉強になりました。頑張ります。 == #!/usr/bin/perl my $inputfile = "地名1.csv"; open(FILE, $inputfile) or die "$!"; my @lines = <FILE>; close(FILE); my $cnt=0; $inputfile = "地名2.csv"; open(my $data, "<", $inputfile) or die "$!"; while(my $line = <$data>) { $line =~ s/[\r\n]+\z//; my @fields = split ",", $line; $cnt++; $outfile="result". $cnt .".csv"; $fields[0] =~ s/^ *(.*?) *$/$1/; $fields[1] =~ s/^ *(.*?) *$/$1/; $pp="[,]?". $fields[0] ."[,]?"; $pq="[,]?". $fields[1] ."[,]?"; foreach (@lines) { if ($_ =~ /($pp)/ or $_ =~ /($pq)/){ if (fileno(OUT)){ print OUT $_; close (OUT); }elsif (! fileno(OUT)){ open (OUT, ">$outfile") or die "$!"; print OUT $_; } }elsif (fileno(OUT)){ print OUT $_; } } }
A.Ichi

2016/10/14 00:54

何とか答えが出てよかったです。 入力ファイルが大量であれば配列に入れるのは、メモリオーバーが発生します。その場合はフィアル読込みに変えてください。
退会済みユーザー

退会済みユーザー

2016/10/14 00:57

わかりました!多分そこまでの量ではない(地名1が10000桁位、地名2が500桁位)なので、大丈夫?と思いますが、勉強を兼ねてトライしてみます。 御助言ありがとうございました。
退会済みユーザー

退会済みユーザー

2016/10/19 01:16

すごくすごく本やネットを調べて、ひとつひとつのスクリプトの意味を考えたのですが、2点、何か・何のためのスクリプトなのか、わからないことがあります。。 ・$pp="[,]?". の所  ppは何のための変数か(そのあとのpqも同様) ・fileno 関数を使う意味  filenoを調べると、大概「ファイルハンドルに対するファイル記述子を返します; ファイルハンドルが オープンしていない場合は未定義値を返します。」のような説明があり、この意味が日本語的によくわかりません。。。 これがあって、必要な行を抜き出せている?? すみませんが、もしよければ御教授願えないでしょうか。。 ラクダ本などでもよりよくわからなくなってしまいました。
A.Ichi

2016/10/19 04:09

・$pp="[,]?". の所 [、]+、[,]?の部分は正規表現です、[]には文字のパターンを入れてます、[a-z]とか[0-9]とかに記述が使われます]の後 +、?、*は パターンの出現回数を指定するものです。0回とか1回以上とかです。正規表現でググルと沢山ヒットします。言語により 書き方が微妙に違う場合もあり、使う度にググルっています。 上記の正規表現の記述をppに入れてます、当然//の中をじかに書いてもOKです。 文字列比較の正規表現に関してはもっと効率の良い方法が有ると思いますが、ちょっと苦手なので簡単な記述にしました。 fileno 関数を使う意味 ファイルをオープンすると貰える番号です、ファイルディスクリプタと呼ばれるものでこの番号でオープン状態も判定できるので使っています。 まず開始の文字列が見つかると出力フィアルをオープンしてファイル番号を取得します。 このときにファイルはディスクのどこに書き込むかはOSが決めています。 終了地名の文字列があればファイルをクローズします。 オープン中に読み込んだ行は中間として出力します。fileno(OUT)でオープン中を判定しています。 「ファイルが未定義の状態」とはオープンされていないか、オープンに失敗した場合です。 ファイルは複数に分ける必要が有りましたので、上記を毎回ファイル名を変更して行っています。 状態遷移:(開始地名:open)->(中間:オープン中)->(終了地名close) 自身でOPEN_FLGを定義してハンドリングされても良いと思います。 perlは、色々と記述を省略して書けますが、もっと冗長的に書けばよかったですね。不明な点は補足いたします。
退会済みユーザー

退会済みユーザー

2016/10/19 06:09

後々のフォローまで、いたみいります。。 「$pp="[,]?". $fields[0] ."[,]?";」これを#で日本語的に表すなら、ようは[、]はそのままの意味で、 「変数ppには、カンマ,がくることもありこないこともある そのあとに変数fieldsの1列目の値 そのあとにカンマ,がくることもありこないこともある」 というようなかんじでしょうか。。"[,]?"と$fields[0]のあいだのピリオドも、重要な意味があるんでしょうか。 本とか調べてもよくわからず、、、正規表現で使う意味のある文字なのか、そうでないのかの判別がすぐにできなくて、正規表現の所でいつも挫折しかかります。。 filenoについてもありがとうございました。 概念がとても難しくて、、ググっても今の私には意味不明で、、 でもそういう方法があるんだと思いました。 一番最初にこうしたい、という所で記載しましたが、 「地名2.csvの地名の順番は 地名1.csvの順番と逆なこともある」 これをfileno関数か、その後の「if ($_ =~ /($pp)/ or $_ =~ /($pq)/){」で 行っているという感じですよね?? 順序が逆であれば逆さから参照していく、、ということで if ($field1 > $field2){ $timei = $field1; $field1 = $field2; $field2 = $timei; } for ($i = $field1; $i <= $field2; $i++){ print 出力ファイル.csv $lines[$i]; } 途中抜粋で恐縮ですが、そんなのは考えてみたりはしたのですが、、
A.Ichi

2016/10/19 06:22

すごくすごく読まれたのでしょう、突っ込みどころがきついです。そんなに熟慮してませんでした。すみません。 []を用いたのは、考え過ぎですが地名が先頭、または最終に有っても探せる様にしたかったからです。仕様が分かっている方はそうは考えませんが・・・ 逆さに読んでいるのではなく2箇所で挟んで出力しているイメージです。なので片方しか無い場合、最終行まで出力されます。
退会済みユーザー

退会済みユーザー

2016/10/19 06:35

すみません(汗)たしかに、今ものすごくいろんなものを読み漁っています。。 Perl含め、そのスクリプト言語も、;を忘れただけでもエラーになったりするので(私が元来大雑把な性格なもので…)、細かいひとつひとつも、大きな意味があるのか、はたまた実はそう意味はなく別の表現でもいいのかが、いまひとつピンときていないので、突っ込んでしまった感じになり申し訳ないです。。深い意味はなく、、ということであれば、そういうものなんだ!ここではそういう表現にされたんだ、と思うことにします。 別件ですが、 「逆さに読んでいるのではなく2箇所で挟んで出力しているイメージ」で、だからか!ということがありました。 というのは、地名1.csvはもともと10000桁位あるデータなのですが、 ==地名1.csv== a,b,札幌,c,d, e,f,沖縄,f,r, t,u,東京,w,e, r,u,千葉,e,q, l,o,金沢,e,c, p,l,高知城,w,w, p,l,高知,w,w, t,j,沖縄,r,f, r,u,千葉,e,q, l,o,金沢,e,c, p,l,高知城,w,w, p,l,高知,w,w, 追加してしまいましたが、本当は一回しか登場すべきでない所が、稀に同じ地名が登場することがあり、その際は検索したい地名で一番最後に登場した所まで抜き出してしまうことがありましたので、そこが腑に落ちました!!!同名の地名をヒットさせないようにするには、その前後のカラム(a,b,等の所)まで考えなくては(はたまた、最後は人間の目でチェックするか、もっと地名1.csvを小分けにするか…)だなぁ、と悶々と検討しておりました。 本当にありがとうございます!
A.Ichi

2016/10/19 06:44

最初だけヒットさせるのであれば、pp、pqを別々にしてヒットした方を存在ないキャラに書き換える方法は有ります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問