アプリ版:「スタンプのみでお礼する」機能のリリースについて

ファイルの読み込みと出力
Perl初心者です。よろしくお願いします。

file.csvのようなファイルを読み込んで、
out.csvのように出力するプログラムを作成しているのですが
途中で変な改行が度々入っているためどうしてもうまくいきません。
(file.csvですが、実際は1500行以上あります。
また、最後に必ずendが入っています。
下記のcsvですがテキスト形式で表示した内容です。)

初歩的な質問で申し訳ありません。
調べる限り調べたのですが分かりませんでした。
どうかよろしくお願いします。

「file.csv」
"2010/1/1","C","こんにちは","田中","end",
"2009/10/2","B","おはよう","斉藤","end",
"2007/3/20","E","Good mor
ning","佐藤","end",
"1988/8/16","F","こんばんは","中
村","end",
"1999/1/10","A","Hello","木村","end",
"2005/9/17","D","おはようご
ざいます","斎藤","end",

「out.csv」(このように出力したいです)
C,こんにちは,田中,2010/1/1,end,
B,おはよう,斉藤,2009/10/2,end,
E,Good morning,佐藤,2007/3/20,end,
F,こんばんは,中村,1988/8/16,end,
A,Hello,木村,1999/1/10,end,
D,おはようございます,斎藤,2005/9/17,end,

「今書いているプログラムです」
#!/usr/local/bin/perl
use strict;
use Fatal qw/ open /;

my $csv_file = "file.csv";
my @csv = &readCsvFile($csv_file);

open(OUT,">out.csv");

for(my $i=0; $i<=5; $i++){
   print OUT $csv[$i][1],",";
   print OUT $csv[$i][2],",";
   print OUT $csv[$i][3],",";
   print OUT $csv[$i][0],",";
   print OUT $csv[$i][4],",";
}
close(OUT);

sub readCsvFile {
   open(DATA, $_[0]);
   while(<DATA>) {
     chomp;
     push @csv, [ split(/",\"/) ];
   }
   close(DATA);
   return @csv;
}

A 回答 (3件)

Perlの細かい文法は忘れてしまいましたので、


おおまかなアルゴリズムを書きます。

readCsvFile の中で、
<DATA>行が「end」文字列で終わってなければ、
次行を現在の行末に連結し、得られた行が「end」で終わるまで同じことを繰り返す。
得られた行が「end」で終わっていればその行を @csv に push。

これで希望の結果が得られると思います。
    • good
    • 0
この回答へのお礼

アルゴリズムの回答、ありがとうございました。
参考にさせていただきます。

お礼日時:2010/07/07 01:07

気づいた点を直してみましたので、参考にしてください。

なお、ソースデータにクォートされた二重引用符がないことが条件になります。

#!/usr/local/bin/perl
use strict;
use Fatal qw/ open /;

my $csv_file = "file.csv";
# my @csv = &readCsvFile($csv_file); # 代入は必要ない
my @csv;
&readCsvFile($csv_file);

open(OUT,">out.csv");

# for(my $i=0; $i<=5; $i++){
for(my $i=0; $i<=$#csv; $i++){ # 配列の要素数に合わせる
   print OUT $csv[$i][1],",";
   print OUT $csv[$i][2],",";
   print OUT $csv[$i][3],",";
   print OUT $csv[$i][0],",";
#   print OUT $csv[$i][4],","; # 改行が必要
   print OUT $csv[$i][4],",\n";
}
close(OUT);

sub readCsvFile {
#   open(DATA, $_[0]); # DATA には特別な意味があるので好ましくない
   open(IN, $_[0]);
   my $line = "";
   while(<IN>) {
     chomp;
     $line .= $_;
     next if $line !~ /end/;
#     push @csv, [ split(/",\"/) ];
     push @csv, [ grep { length } split(/","|",|"/, $line) ]; # "," のみでは行頭や行末の " が残る
     $line = "";
   }
   close(IN);
#  return @csv;
}
    • good
    • 0
この回答へのお礼

回答していただきありがとうございました。
無事に解決できました!

要所々にコメントをいれていただきとても分り易かったです。
こうした問題をすぐに解決できるようにもっと勉強します。
ありがとうございました。

お礼日時:2010/07/07 01:12

こんな感じでどうでしょうか?


条件としてご質問欄のようにデータは
 ・CSVはExcelのCSV形式(セル内に改行や記号が含まれる)
 ・データもプログラムもshift-jisコード(正規表現が誤動作する可能性があるのでEUCとかでやった方が・・・)
ただし「""」内のデータに「,」が含まれていると出力されたデータが再利用時に使い物にならない(別の文字に変換要)ので要注意
--------------------------------------------------------------------------------
#!/usr/local/bin/perl
use strict;

my $csv_file = "file.csv";
my @aDT = &readCsvFile($csv_file);## CSVファイルの読み込み
map { s/\r\n|\r|\n//g } @aDT;## 行内の改行を削除する

open(WR_DATA,">out.csv");
map { print WR_DATA $_."\n"; } @aDT;## 最近mapにはまってまして・・・「foreach(@aDT){ print WR_DATA $_."\n"; }」と同じです
close(WR_DATA);

exit;

sub readCsvFile {
my $sFN = shift;
my @aCSV;
open(RD_DATA, $sFN);
while(my $line = <RD_DATA>){
$line .= <RD_DATA> while ($line =~ tr/"// % 2 and !eof(RD_DATA));
$line =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/;
my @aDT = map {/^"(.*)"$/s ? scalar($_ = $1, s/""/"/g, $_) : $_}
($line =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);## ?
## 元来この@aDTに各行の要素がいったん格納されますので個別に処理したいならここに処理を記述
my $sTmp = join(",",@aDT);## 各要素をコンマ区切りで結合して一行分を生成する
push(@aCSV,$sTmp);
}
close(RD_DATA);
return @aCSV;
}
--------------------------------------------------------------------------------
経験上の突っ込みどころとしては
 ・perlは行指向の言語なので多次元配列は避けるのが吉(やっていけないわけじゃないけど・・・)
 ・局所変数はしっかり定義(関数内の@csvは要注意)
 ・ファイルハンドル名や変数は、できるだけ予約語や組み込み関数名に類似した名前はさける
    • good
    • 0
この回答へのお礼

ご回答いただき、ありがとうございました。

> CSVはExcelのCSV形式(セル内に改行や記号が含まれる)
説明不足で申し訳ございませんでした。
ExcelのCSV形式のため変に改行が入ってしまい四苦八苦していました。
ちなみにデータとプログラムですがEUCで行っています。

> ただし「""」内のデータに「,」が含まれていると出力されたデータが
> 再利用時に使い物にならない(別の文字に変換要)ので要注意
まさにその通りです!
データ内に「,」がたくさん入っているため別の文字に変換をして処理をしていました。

mapの使い方に目から鱗です!
とても参考になります!
また、経験に基づいたアドバイスもとても参考になりました。
Perlをもっと勉強しないといけないと思いました。

本当にありがとうございました。

お礼日時:2010/07/07 01:25

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