電子書籍の厳選無料作品が豊富!

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

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

A 回答 (12件中1~10件)

文字コードをutf-8であることをきちんと指定するようにしました。


また、地名2.csvの県名が地名1.csvにないとき、そこで打ち切らず、処理を続行するようにしました。
エラー時の文字化けも発生しないようにしました。こちらをまるまる入れ替えてください。
---------------------------------------------------------
use strict;
use utf8;
use Encode;
binmode(STDOUT, ":utf8");
#Usage表示
if (scalar(@ARGV) != 3){
printf("%s 地名1.csv 地名2.csv 出力ファイル名\n",$0);
exit 10;
}
my $fname1 = $ARGV[0];
my $fname2 = $ARGV[1];
my $fname3 = $ARGV[2];
our @lines = ();
our %names = ();
#ファイルを読み込み、内部テーブルへ展開
&readfile($fname1);
#出力ファイルオープン
open(OFH,">:encoding(:utf-8)",$fname3) || die "$fname3 オープンエラー<$!>";
#ファイルを読み込み、範囲内を出力
&readfile2($fname2);
#出力ファイルクローズ
close(OFH);
#ファイルを読み込み、内部テーブルへ展開
sub readfile
{
my $fname = shift(@_);
$fname = $fname;
open FH,"<:encoding(:utf-8)",$fname || die "$fname オープンエラー<$!>";
my $i = 0;
while(<FH>){
my @elm = split(/,/,$_);
$names{$elm[2]} = $i;
push(@lines,$_);
$i++;
}
close(FH);
}
#ファイルを読み込み、範囲内を出力
sub readfile2
{
my $fname = shift(@_);
$fname = $fname;
open FH,"<:encoding(:utf-8)",$fname || die "$fname オープンエラー<$!>";
my $i = 0;
while(<FH>){
$i++;
chomp($_);
my @elm = split(/,/,$_);
if (exists $names{$elm[0]}){
}else{
printf("%d行 %s 地名1.csv になし\n",$i,$elm[0]);
print $_,"\n";
next;
}
if (exists $names{$elm[1]}){
}else{
printf("%d行 %s 地名1.csv になし\n",$i,$elm[1]);
print $_,"\n";
next;
}
my $st = $names{$elm[0]};
my $en = $names{$elm[1]};
&outfile($st,$en);
}
close(FH);
}
#出力ファイルへ出力
sub outfile
{
my $st = shift(@_);
my $en = shift(@_);
#順番が逆ならひっくり返す
if ($st > $en){
my $tmp = $st;
$st = $en;
$en = $tmp;
}
for (my $i = $st; $i <= $en; $i++){
print OFH $lines[$i];
}
}
-----------------------------------------------------
以下実行結果です。
地名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,千葉2
高知2,千葉3

$ perl sample.pl timei1.csv timei2.csv out.csv
3行 高知2 地名1.csv になし
高知2,千葉2
4行 高知2 地名1.csv になし
高知2,千葉3

出力結果
$cat out.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,
    • good
    • 0

ちょっと気になったのは #7 のところのやり取りで


Use of uninitialized value $elm[2] in hash element at sample.pl line 40, <FH> line 1.
というエラーが出たところですかね. このエラーメッセージは「sample.pl の 40行目」で「$elm[2] という初期化されていない変数を使った」と言っているのですが, #5 にあるプログラムで $elm[2] を使っているのは
$names{$elm[2]} = $i;
の 1行のみであり, しかもこれは「40行目」ではありません. つまり, このときに使ったプログラムは #5 にあるものとは (perl が見たときに) 違うものであると結論付けることができます. ついでにいうとこの行は「地名1.csv」を読み込んでいるループの中にありますから, 「『地名1.csv』の読み込みで問題が起きている」という可能性があります.

とりあえず, いきなり巨大なデータに対して処理するんじゃなくって, 質問文にあるくらいのデータで試してみてどうなるかを確かめたらどうでしょうかね. ただし, あなたの使ったプログラムが (上に書いたように) #5 にあるものとはかなり違っている可能性があるので,
・どのような入力データに対して
・どのようなプログラムを動かしてみたときに
・どのような出力が得られたのか
をきちんと書くようにしてください.

あ, Windows のメモ帳だと必ず BOM が付きます.
    • good
    • 0
この回答へのお礼

地名1は桁数は6000位、地名2は500程度あるデータなのですが、
中身は大きくは変わらない感じです。
でも、ここまで沢山のお時間とお力添えを頂き、こう書くのかという目から鱗の部分もたくさんありました。

本当にありがとうございます。
私も、もっともっと勉強します。

BOMについては初めて知ったことでした(^^;

お礼日時:2016/10/13 18:30

>・BOM


>ついているかもしれません。。メモ帖で開いて別名保存で確認した所、元ファイルとバイト数が同じでした。
>でも、BOMを抜く方法がググってもすぐに出来なさそうなので、ちょっと一旦休憩します(すみませ>>ん、、)これを抜けばできそうでしょうか。

ゆっくり、休んでください。
たぶん、うまくいくと思います。
只、こちらでも、本当に、地名2.csvの県名が地名1.csvにないとき、
エラーメッセージが、1行 i≫?a?§e°・a?°が地名1.csvになし のように
文字化けして表示されるので、確認してみます。
    • good
    • 0

スクリプトの2行目(use strict;)に


use utf8;
追加して、再度実行していただけますか。
それで、状況が変わりませんでしょうか?

又、念のため確認ですが、地名1.csv、 地名2.csvは
BOMはついてないですよね。(ファイルの先頭3バイトにあり文字コードの並びを指定するバイナリデータ)
    • good
    • 0
この回答へのお礼

・use utf8を付与した所、
Wide character in print at test8.pl line 63, <FH> line 1.
1行 i≫?a?§e°・a?°が地名1.csvになし
というエラーが表示されたため、ないほうが良かった感があります。

・BOM
ついているかもしれません。。メモ帖で開いて別名保存で確認した所、元ファイルとバイト数が同じでした。
でも、BOMを抜く方法がググってもすぐに出来なさそうなので、ちょっと一旦休憩します(すみません、、)これを抜けばできそうでしょうか。

お礼日時:2016/10/13 16:39

>TeraPadでUTF-8に変換し、上書きまして、再度実行しましたが、


>==
>Use of uninitialized value $elm[2] in hash element at sample.pl line 40, <FH> line 1.
>¦(延々10000行)
>Use of uninitialized value $elm[2] in hash element at sample.pl line 40, <FH> line 10000.
>1行 **が地名1.csvになし
>==
>と表示され、カラの出力ファイルが出来ました(涙)

うーむ。いずれかのファイルの文字コードがおかしいように思えます。

more スクリプトファイル
more 地名1.csv
more 地名2.csv

をteratermで実行し、上記3ファイルがきれいに表示されるか確認していただけませんでしょうか。
teratermのterminalの漢字コードは、utf-8にして確認してください。

地名2.csvは行数が多いので、最初がOKなら途中で打ち切っても構いません。
    • good
    • 0
この回答へのお礼

親切にありがとうございます。
全てのファイルをmoreで見てみましたが、文字化け等ありません。
また、teratermで設定>全般>言語「UTF-8」となっています。。。

お礼日時:2016/10/13 15:55

>文字コードは65001:Unicode(UTF-8)です。


こちらでも、utf-8の環境で確認しました。

スクリプト(sample.pl)の文字コード UTF-8
地名1.csvの文字コード UTF-8
地名2.csvの文字コード UTF-8
teratermのterminal(receive,transmit)の文字コード UTF-8
で確認しました。
正常に動作します。
提示されたファイルで実行すると出力ファイルは、
$ cat out.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,

のようになります。

念のため、確認ですが、OSの文字コードもUTF-8で間違いないでしょうか?
    • good
    • 0
この回答へのお礼

あっ!!地名2.csvの方が932:日本語(シフトJIS)でした(汗)

TeraPadでUTF-8に変換し、上書きまして、再度実行しましたが、
==
Use of uninitialized value $elm[2] in hash element at sample.pl line 40, <FH> line 1.
¦(延々10000行)
Use of uninitialized value $elm[2] in hash element at sample.pl line 40, <FH> line 10000.
1行 **が地名1.csvになし
==
と表示され、カラの出力ファイルが出来ました(涙)

お礼日時:2016/10/13 15:00

>と、文字化け表示し、1行目のリストが読み込めなかったようです。


>windowsでteratermを利用してプログラミングしていますが、
>元々のファイルのエンコードをどうにかする必要があるのでしょうか。
>重ね重ね申し訳ありませんが、ご都合つけばお助け願えませんでしょうか。

念のため、
ファイルが存在するOSと
ファイルの文字コード(漢字コード)
を教えていただけますでしょうか?
    • good
    • 0
この回答へのお礼

ファイルのあるOSはLinux version 3.0、
文字コードは65001:Unicode(UTF-8)です。

お礼日時:2016/10/13 13:57

No4です。


>それがベストです!
ベスト案で作成しました。以下のスクリプトを作成してください。(sample.plとします)
---------------------------------------------------
use strict;
#Usage表示
if (scalar(@ARGV) != 3){
printf("%s 地名1.csv 地名2.csv 出力ファイル名\n",$0);
exit 10;
}
my $fname1 = $ARGV[0];
my $fname2 = $ARGV[1];
my $fname3 = $ARGV[2];
our @lines = ();
our %names = ();
#ファイルを読み込み、内部テーブルへ展開
&readfile($fname1);
#出力ファイルオープン
open(OFH,">",$fname3) || die "$fname3 オープンエラー<$!>";
#ファイルを読み込み、範囲内を出力
&readfile2($fname2);
#出力ファイルクローズ
close(OFH);
#ファイルを読み込み、内部テーブルへ展開
sub readfile
{
my $fname = shift(@_);
$fname = $fname;
open(FH,$fname) || die "$fname オープンエラー<$!>";
my $i = 0;
while(<FH>){
my @elm = split(/,/,$_);
$names{$elm[2]} = $i;
push(@lines,$_);
$i++;
}
close(FH);
}
#ファイルを読み込み、範囲内を出力
sub readfile2
{
my $fname = shift(@_);
$fname = $fname;
open(FH,$fname) || die "$fname オープンエラー<$!>";
my $i = 1;
while(<FH>){
chomp($_);
my @elm = split(/,/,$_);
if (exists $names{$elm[0]}){
}else{
printf("%d行 %sが地名1.csvになし\n",$i,$elm[0]);
print $_;
return;
}
if (exists $names{$elm[1]}){
}else{
printf("%d行 %sが地名1.csvになし\n",$i,$elm[1]);
print $_;
return;
}
my $st = $names{$elm[0]};
my $en = $names{$elm[1]};
&outfile($st,$en);
$i++;
}
close(FH);
}
#出力ファイルへ出力
sub outfile
{
my $st = shift(@_);
my $en = shift(@_);
#順番が逆ならひっくり返す
if ($st > $en){
my $tmp = $st;
$st = $en;
$en = $tmp;
}
for (my $i = $st; $i <= $en; $i++){
print OFH $lines[$i];
}
}
-------------------------------------------
perl sample.pl 地名1.csv 地名2.csv 出力ファイル
と入力すると、
出力ファイルに結果が出力されます。
    • good
    • 0
この回答へのお礼

私が1週間以上かけてやりきれなかったことがこんなすぐ回答頂けるとは、、心の底から感動しました(苦笑)

やってみたのですが、実行自体はうまくいき、ただ結果が、
==
1行 rcが地名1.csvになし
rc,ォ
==
と、文字化け表示し、1行目のリストが読み込めなかったようです。。
windowsでteratermを利用してプログラミングしていますが、
元々のファイルのエンコードをどうにかする必要があるのでしょうか。
重ね重ね申し訳ありませんが、ご都合つけばお助け願えませんでしょうか。

#Perlはまだ初歩の参考書を見ての座学中で、本の内容が実務に転用できず困っています、、、

お礼日時:2016/10/13 13:34

No2です。


>書き出すファイル名はOUT1.cs1、OUT2.csvのように連番でも良いですし、(後で1ファイルに別コマンド>でつなげればよいので)
>ただ、最終的には1ファイルにしたくは思っています。

ということは、
e,f,沖縄,f,r,
t,u,東京,w,e,
r,u,千葉,e,q,
r,u,千葉,e,q,
l,o,金沢,e,c,
p,l,高知,w,w,

のように空白行を入れずに、1つのファイルへ出力するのが理想でしょうか?
    • good
    • 0
この回答へのお礼

それがベストです!

お礼日時:2016/10/13 12:10

NO2です。


質問1は、取り消します。(質問の意味が理解できました)
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!