ファイルの読み込みと出力プログラム作成

このQ&Aのポイント
  • Perl初心者のため、ファイルの読み込みと出力を行うプログラムを作成しています。しかし、ファイル内の改行がうまく処理できずに困っています。
  • ファイル.csvを読み込んで、out.csvに出力するプログラムを作成中です。ファイル内には不要な改行が混ざっているため、正しく出力できません。
  • Perlの初心者で、ファイル.csvを読み込んで出力するプログラムを作成していますが、改行の処理につまずいています。お力をお借りしたく思います。
回答を見る
  • ベストアンサー

ファイルの読み込みと出力

ファイルの読み込みと出力 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; }

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

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

  • ベストアンサー
  • kumoz
  • ベストアンサー率64% (120/185)
回答No.2

気づいた点を直してみましたので、参考にしてください。なお、ソースデータにクォートされた二重引用符がないことが条件になります。 #!/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; }

Jurassic_period
質問者

お礼

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

その他の回答 (2)

  • ralf124c
  • ベストアンサー率52% (232/446)
回答No.3

こんな感じでどうでしょうか? 条件としてご質問欄のようにデータは  ・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は要注意)  ・ファイルハンドル名や変数は、できるだけ予約語や組み込み関数名に類似した名前はさける

Jurassic_period
質問者

お礼

ご回答いただき、ありがとうございました。 > CSVはExcelのCSV形式(セル内に改行や記号が含まれる) 説明不足で申し訳ございませんでした。 ExcelのCSV形式のため変に改行が入ってしまい四苦八苦していました。 ちなみにデータとプログラムですがEUCで行っています。 > ただし「""」内のデータに「,」が含まれていると出力されたデータが > 再利用時に使い物にならない(別の文字に変換要)ので要注意 まさにその通りです! データ内に「,」がたくさん入っているため別の文字に変換をして処理をしていました。 mapの使い方に目から鱗です! とても参考になります! また、経験に基づいたアドバイスもとても参考になりました。 Perlをもっと勉強しないといけないと思いました。 本当にありがとうございました。

回答No.1

Perlの細かい文法は忘れてしまいましたので、 おおまかなアルゴリズムを書きます。 readCsvFile の中で、 <DATA>行が「end」文字列で終わってなければ、 次行を現在の行末に連結し、得られた行が「end」で終わるまで同じことを繰り返す。 得られた行が「end」で終わっていればその行を @csv に push。 これで希望の結果が得られると思います。

Jurassic_period
質問者

お礼

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

関連するQ&A

  • Excelのcsv形式の読み込み

    Excelのcsv形式の読み込み 前回、http://okwave.jp/qa/q6018540.htmlで 質問させていただきましたJurassic_periodです。 お力を貸していただきました方々本当にありがとうございました。 今回ですが同じプログラムでまた壁にぶつかってしまいました。 どうかよろしくお願いいたします。 Excelのcsv形式の「file.csv」のようなデータを読み込んでいます。 「out.csv」のように出力したいのですが 未入力「""」のデータ箇所が多々ある事に気が付きました。 次データを読み込むため出力データがズレてしまい困っています。 「file.csv」 "2010/1/1","C","こんにちは","田中","end", "2009/10/2","B","おはよう","","end", "2007/3/20","E","Good mor ning","佐藤","end", "1988/8/16","","こんばんは","中 村","end", "","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, ,こんばんは,中村,1988/8/16,end, A,Hello,木村, ,end, D,おはようございます,斎藤,2005/9/17,end, 解決法として、正規表現を用いて「半角スペース」を入れようとしました。 また、直接「file.csv」の「,"",」を置換で「," ",」にしましたが 基データを操作するのは好ましくないのでプログラムで どうにか処理ができないでしょうか。 「今書いているプログラムです」 #!/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<=$#csv; $i++){ $csv[$i][0] =~ s/"//; #行頭の"を削除 $csv[$i][4] =~ s/"//; #行末の"を削除 $csv[$i][0] =~ s// /; #空データを半角スペースに置換 $csv[$i][1] =~ s// /; $csv[$i][3] =~ s// /; 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],",\n"; } close(OUT); sub readCsvFile { open(IN, $_[0]); my $line = ""; while(<IN>) { chomp; $line .= $_; next if $line !~ /end/; push @csv, [ grep { length } split(/","|",|"/, $line) ]; $line = ""; } close(IN); } どうか、よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • <Perl>参照配列の出力に失敗する。

    <Perl>参照配列の出力に失敗する。 お世話になります。 配列の出力部で以下のエラーが出力されます。 Use of uninitialized value in print at test2.pl line 12. -----コーディングは以下の通りです。----- #!C:\perl use strict; use warnings; my @l = (); #----------- #GetDataへCSVファイル名と、格納用配列を渡す #----------- my $cnt = &GetData("test.csv", \@l); print "COUNT -> ".$cnt; for(my $i=0; $i < $cnt; $i++){ print $l[$i]; } ################################################################## # 概   要:指定したCSVファイルをオープンしCSVデータを配列に取得する。 # パラメータ:ファイル名, CSVデータ格納用配列 # 戻 り 値:データ取得件数 ################################################################## sub GetData { my ($f, @bf) = @_; my $rcnt = 0; print "FILE NAME -> ".$f."\n"; if ( open(FP, "<${f}") ){ print "FILE OPEN -> success.\n"; @bf = split(/,/, <FP>); close(FP); $rcnt = @bf; print "CSV GET COUNT -> ".$rcnt."\n"; } return $rcnt; } -----実行結果は以下の通りです。----- D:\>perl test.pl FILE NAME -> test.csv FILE OPEN -> success. CSV GET COUNT -> 5 Use of uninitialized value in print at test2.pl line 12. Use of uninitialized value in print at test2.pl line 12. Use of uninitialized value in print at test2.pl line 12. Use of uninitialized value in print at test2.pl line 12. COUNT -> 5 -----CSVファイルの内容は以下の通りです。(ファイル名:test.csv)----- あいうえお,かきくけこ,さしすせそ,たちつてと,なにぬねの 配列の要素数が取れているので、配列内にデータは格納されているとは思っています。 出力方法をどのように正せばよいがご教示お願い致します。

    • ベストアンサー
    • Perl
  • 文字コードの変換(Shift-JISからUTF8)

    文字コードがShift-JISのCSVファイルを読み込み、UTF-8のテキストファイルに出力するのに プログラムの中で変更しようとしているのですが、うまくいきません。出力ファイルの文字コードを 確認するとShift-JISのままです。 どなたか教えていただけないでしょうか? ActivePerl v5.16.0を使用し、Encodeモジュールのfrom_toを使用しています。 #!/usr/bin/perl use strict; use warnings; use utf8; use Encode; my $input_file="input.csv"; my $output_file="output.txt"; open (IN, $input_file) or die "$!"; open (OUT, ">$output_file") or die "$!"; while (<IN>){ chomp ($_); my @data=split(/,/,$_); for(my $i=0;$i<@data;$i++){ $data[$i]=Encode::from_to($data[$i],'shiftjis','utf8'); #Shift-JISからUTF-8に変換 $data[$i]=~s/\s+//g; print OUT $_; } print OUT "\n"; } close (IN); close (OUT);

    • ベストアンサー
    • Perl
  • FTP接続時失敗時、接続先名をcsvに出力する

    Perl 初心者です。 FTP接続時失敗時、接続先名をcsvに出力する(書き出す)プログラムを作成しております。 ※複数FTPサーバがあり順番に接続していき、接続失敗したところをcsvに一覧にして出力したいです。もし、csvファイルがない場合、C:/logの下にERROR.csvを作成する必要もあります。 今現在、私が作成しているプログラムが以下になります。 #!C:/Perl/bin/perl use Net::FTP; use strict; my $i; my $j; my @ftp_data = ( ['ftp','192.168.0.0'], ['ftp2','192.168.0.10'], ); sub error{ my $file = "ERROR.csv"; if(!-d"C:/log/$file"){ open(TFILE,"+>> $file") or die($!); print OUT "$ftp_data[$i][0]"; close(TFILE); } else{ open(OUT, ">> $file"); # 追加オープン print OUT "$ftp_data[$i][0]"; close(TFILE); } exit; } for ($i = 0; $i <= $#ftp_data; $i++){ #FTPサーバーへの接続 my $ftp = Net::FTP->new("$ftp_data[$i][1]") or &error; #ユーザー名とパスワードを指定してログイン $ftp->login('user','password') or die $ftp->message; print "connect $ftp_data[$i][1]\n"; #接続終了 $ftp->quit; } 実行してみると何もでないでずっとループ?しているみたいなのですが、 どこが悪いのかよく分からないのです。 ご教示いただけないでしょうか?

    • ベストアンサー
    • Perl
  • ファイル出力をUTF8NではなくてUTF8で行いたい

    おせわになります。 Shift-JISで記述したPerlで、ファイル出力をしようとしています。 ------------------------------------ #!/usr/local/bin/perl use encoding 'shiftjis'; $hoge="ほげ" open(OUT,">file.txt") binmode OUT; print OUT $hoge; close(OUT); ------------------------------------ 希望はUTF8での出力なのですが、UTF8Nで出力されてしまいます。 どのようにすればいいのかご教授よろしくお願いします。

    • ベストアンサー
    • Perl
  • 標準出力しながらファイルに出力

    標準出力に表示したものを、そのままファイルに書き込むことは可能でしょうか? 例えば下記のように実現できるかと思いますが、printする度に2行書くのは厳しく思ってます。 ----------------------------- open(OUT, "> outfile.txt") ; print $aaa ; print OUT $aaa ; -----------------------------

  • CSVの条件出力について

    ディレクトリー内のCSVを1ファイルにするルーチンで、現在46カラム目から3桁が"000"なら出力としている判断を、5項目めが"000"ならに変更したいのですが、どのようにすればよいでしょうか? 現在の記述は下記の通りです。 if(open(OUT,">$path/$year$mon$mday$csv")){ for my $fname (sort @FTPlist){ if(open(IN,"$path/$fname")){ while (<IN>) { $sip = substr($_,46,3); if ($sip == "000") { print OUT "$_"; } } close(IN); } } close(OUT); }

    • ベストアンサー
    • Perl
  • perlでファイルを分割してファイル名も出力したい

    >AA A AAA aaaaaaaaaaaaaaa aaaaaaaaaaaa >BBB BB B bbbbbbbbbbbbbbb bbbbbbbbbbbbbbb bbbbbbbbbbbbbb ・ ・ こういった形式でテキストが入った大きいサイズのひとつのファイルを ・ファイル名 AA A AAA ・テキスト内 >AA A AAA aaaaaaaaaaaaaaa aaaaaaaaaaaa ・ファイル名 BBB BB B ・テキスト内 >BBB BB B bbbbbbbbbbbbbbb bbbbbbbbbbbbbbb bbbbbbbbbbbbbb となるように個別に出力させたいのですがどのようにすればよいでしょうか? #!/usr/bin/perl -w use strict; my $first; my $file; $first = 1; open(IN,"分割前ファイル"); foreach(<IN>) { if ( /^>(.*)/ ) { if ($first == 0 ) { close(OUT); } else {$first = 0; } $file = $1; $file =~ s/[\r\n]//; open(OUT,">$file"); } else { print OUT $1; print OUT $_; } } close(IN); このようなプログラムを組んだのですが、出力結果はファイル名についてはいいのですが、テキスト内が思ったようにいきません。 ・ファイル名 AA A AAA ・テキスト内 AA A AAAaaaaaaaaaaaaaaa AA A AAAaaaaaaaaaaaa このような結果になってしまいます。 どなたか対処方をわかるかたがいましたらお願いします。

    • ベストアンサー
    • Perl
  • perlでCSV形式ファイルのアップロード

    本日、同じ質問をしたものです。 前回の問題は解決したのですが、ファイルがアップロードできないで悩んでします。 CGIのプログラムは以下のソースです。 #! c:/perl/bin/perl use CGI; # CGIヘッダーの出力 print "Content-type: text/html\n\n"; my ($query,$fileName); $query = new CGI; $fileName = $query->param('fileName'); open(OUT, ">./a.csv"); binmode(OUT); while(read($fileName,$buffer,1024)) { print OUT $buffer; } close(OUT); close($fileName); #ファイルハンドルをcloseしています。 exit ; ファイルは、作られるのですが、中身が書き込まれません。 どなたか教えてください。 お願いします。

    • ベストアンサー
    • Perl
  • perlでcsvファイルから複数行を抽出したい

    プログラミング初心者です。 お知恵をお貸しください。 perlでcsvファイルから任意の複数行を、コマンドライン引数を使って抜き出したいです。 1行だけだとうまくいきますが、以下ではすべてのcsvデータが出力されてしまいます。 ----------------------getcsv.pl ここから---------------------- #!/usr/bin/perl -- use strict; use warnings; open(IN, "<adata.csv"); open (OUT, ">$bdata.csv"); while(<IN>){ if($. == $ARGV[0] || $ARGV[1] || $ARGV[2] || $ARGV[3]) { print OUT $_; } } close(IN); close (OUT); ----------------------getcsv.pl ここまで---------------------- ----------------------コマンド ここから---------------------- perl getcsv.pl 1 3 8 10 ----------------------コマンド ここまで---------------------- よろしくお願いします。

    • ベストアンサー
    • Perl

専門家に質問してみよう