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

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

ただいまの
回答率

91.46%

  • Perl

    331questions

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

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

解決済

回答 1

投稿 2016/10/13 11:14

  • 評価
  • クリップ 0
  • VIEW 506

yoichirou

score 11

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文で条件分岐をすればいいのだと思うのですが、
ここがうまく書けません。。

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    2016/10/13 16:06

    こちらの質問が他のユーザから「やってほしいことだけを記載した丸投げの質問」という指摘を受けました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

回答 1

checkベストアンサー

0

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

my $inputfile="chimei1.csv";
open (FILE, $inputfile) or die "$!";
my @lines = <FILE>;
close(FILE);

my $cnt=0;
$inputfile="chimei2.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 my $var(@lines) {
        if ($var =~ /($pp)/ or $var =~ /($pq)/){
            if (fileno(OUT)){
                print OUT $var;
                close (OUT);
            }elsif (! fileno(OUT)){
                open (OUT, ">$outfile") or die "$!";
                print OUT $var;
            }
        }elsif (fileno(OUT)){
            print OUT $var;
        }
    }
}

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

use utf8;
use Encode 'decode';
use Encode 'encode';

my $inputfile="chimei1.csv.sjs";
open (FILE, "<:encoding(cp932)", $inputfile) or die "$!";
my @lines = <FILE>;
close(FILE);

my $cnt=0;
$inputfile="chimei2.csv.sjs";
open(my $data, "<:encoding(cp932)", $inputfile) or die "$!";
while (my $line = <$data>) {
    $line =~ s/[\r\n]+\z//;
    my @fields = split ",", $line;
    $cnt++;
    $outfile="result". $cnt .".csv.sjs";
    $fields[0] =~ s/^ *(.*?) *$/$1/;
    $fields[1] =~ s/^ *(.*?) *$/$1/;
    $pp="[,]+". $fields[0] ."[,]+";
    $pq="[,]+". $fields[1] ."[,]+";
    foreach my $var(@lines) {
        chomp($var);
        if ($var =~ /($pp)/ or $var =~ /($pq)/){
            if (fileno(OUT)){
                printf OUT "%s\r\n", $var;
                close (OUT);
            }elsif (! fileno(OUT)){
                open (OUT, ">:encoding(cp932)", $outfile) or die "$!";
                printf OUT "%s\r\n", $var;
            }
        }elsif (fileno(OUT)){
            printf OUT "%s\r\n", $var;
        }
    }
}

投稿 2016/10/13 14:20

編集 2016/10/13 21:09

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/13 15: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 16:03

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

    キャンセル

  • 2016/10/13 16:48

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

    キャンセル

  • 2016/10/13 17:08

    お手を煩わせてすみません。。いい感じになのですが、

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

    のとき、


    e,f,沖縄,f,r,
    t,u,東京,w,e,  ←あいだがまだ切り出せないようで、、ここが私も苦戦しております。
    r,u,千葉,e,q,

    キャンセル

  • 2016/10/13 17:30

    中間を入れるのを忘れておりました。

    キャンセル

  • 2016/10/13 18:27

    したから6行目の
    }elsif (fileno(OUT)){
    は不要でしょうか

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

    キャンセル

  • 2016/10/13 19:06

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

    キャンセル

  • 2016/10/13 19:33

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

    後程修正して再トライしてみます。

    キャンセル

  • 2016/10/13 21:11

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

    キャンセル

  • 2016/10/13 21:46

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

    キャンセル

  • 2016/10/14 09: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 $_;
    }
    }
    }

    キャンセル

  • 2016/10/14 09:54

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

    キャンセル

  • 2016/10/14 09:57

    わかりました!多分そこまでの量ではない(地名1が10000桁位、地名2が500桁位)なので、大丈夫?と思いますが、勉強を兼ねてトライしてみます。

    御助言ありがとうございました。

    キャンセル

  • 2016/10/19 10:16

    すごくすごく本やネットを調べて、ひとつひとつのスクリプトの意味を考えたのですが、2点、何か・何のためのスクリプトなのか、わからないことがあります。。

    ・$pp="[,]?". の所
     ppは何のための変数か(そのあとのpqも同様)

    ・fileno 関数を使う意味
     filenoを調べると、大概「ファイルハンドルに対するファイル記述子を返します; ファイルハンドルが オープンしていない場合は未定義値を返します。」のような説明があり、この意味が日本語的によくわかりません。。。
    これがあって、必要な行を抜き出せている??

    すみませんが、もしよければ御教授願えないでしょうか。。
    ラクダ本などでもよりよくわからなくなってしまいました。

    キャンセル

  • 2016/10/19 13:09

    ・$pp="[,]?". の所
    [、]+、[,]?の部分は正規表現です、[]には文字のパターンを入れてます、[a-z]とか[0-9]とかに記述が使われます]の後 +、?、*は パターンの出現回数を指定するものです。0回とか1回以上とかです。正規表現でググルと沢山ヒットします。言語により
    書き方が微妙に違う場合もあり、使う度にググルっています。

    上記の正規表現の記述をppに入れてます、当然//の中をじかに書いてもOKです。
    文字列比較の正規表現に関してはもっと効率の良い方法が有ると思いますが、ちょっと苦手なので簡単な記述にしました。

    fileno 関数を使う意味
    ファイルをオープンすると貰える番号です、ファイルディスクリプタと呼ばれるものでこの番号でオープン状態も判定できるので使っています。
    まず開始の文字列が見つかると出力フィアルをオープンしてファイル番号を取得します。
    このときにファイルはディスクのどこに書き込むかはOSが決めています。
    終了地名の文字列があればファイルをクローズします。 オープン中に読み込んだ行は中間として出力します。fileno(OUT)でオープン中を判定しています。
    「ファイルが未定義の状態」とはオープンされていないか、オープンに失敗した場合です。
    ファイルは複数に分ける必要が有りましたので、上記を毎回ファイル名を変更して行っています。
    状態遷移:(開始地名:open)->(中間:オープン中)->(終了地名close)
    自身でOPEN_FLGを定義してハンドリングされても良いと思います。

    perlは、色々と記述を省略して書けますが、もっと冗長的に書けばよかったですね。不明な点は補足いたします。

    キャンセル

  • 2016/10/19 15: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];
    }
    途中抜粋で恐縮ですが、そんなのは考えてみたりはしたのですが、、

    キャンセル

  • 2016/10/19 15:22

    すごくすごく読まれたのでしょう、突っ込みどころがきついです。そんなに熟慮してませんでした。すみません。
    []を用いたのは、考え過ぎですが地名が先頭、または最終に有っても探せる様にしたかったからです。仕様が分かっている方はそうは考えませんが・・・

    逆さに読んでいるのではなく2箇所で挟んで出力しているイメージです。なので片方しか無い場合、最終行まで出力されます。

    キャンセル

  • 2016/10/19 15: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を小分けにするか…)だなぁ、と悶々と検討しておりました。

    本当にありがとうございます!

    キャンセル

  • 2016/10/19 15:44

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

    キャンセル

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

ただいまの回答率

91.46%

関連した質問

同じタグがついた質問を見る

  • Perl

    331questions

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