CGIでテキストファイル(test.csv)を1行ずつ読み込み、

値を置換したいのですが、Perlは初めてで全然わかりません。
項目はタブ区切りで以下のようになっています。
このデータから誕生月が1桁の場合は"09"、性別は男なら"1"、女なら"2"としたいのです。
(test.csvの中身)
年月日 氏名 誕生月 誕生日 性別
060904 日本太郎 10 4 男性
060904 日本花子 9 4 女性

どなたか助けてください。

回答の条件
  • 1人2回まで
  • 登録:2006/09/04 22:11:54
  • 終了:2006/09/05 15:07:03

ベストアンサー

id:arkadien No.6

arkadien回答回数131ベストアンサー獲得回数12006/09/05 01:26:27

ポイント50pt

プログラムは人のを見て理解するのが上達の早道と思います。

割と定番サイトですが、perlの関数や書式、演算子などは、

以下がわかりやすいと思います。

http://pzxa85.hp.infoseek.co.jp/www/wwwperl.htm

ご提示の例ですと以下のようなプログラムでいけると思います。

後はデータの形式などにあわせて、

多少改良するといけると思います(動作確認済みです)。

中にコメントも入れておきましたので参考にしてください。


  • スクリプトここから ----------

######################################################################

# 前提

#

# 入力データは以下の形式とする。

# 但し、5カラム目以降も、任意のデータがあった場合でもそのまま格納できる。

#

# 060904 日本太郎 10 4 男性

# 060904 日本花子 9 4 女性

#

# 1データ一行で、区切り文字はタブ1つのみ

# 性別は 4 カラム目にあるとする(0から数えて4カラム目)

# 0カラム目:年月日

# 1カラム目:氏名

# 2カラム目:誕生月

# 3カラム目:誕生日

# 4カラム目:性別

######################################################################

######################################################################

# 以下、条件に合わせて書き換える

# 入力ファイル名を以下に指定。

$in_file = "in.txt";

# 入力ファイル名を以下に指定。

$out_file = "out.txt";

# 性別が4カラム目にある場合は4を指定。

$num = 4;

######################################################################

# 前提が変わらない場合はこれ以降の変更は不要。

#

# 入力ファイルをオープン

open(IN, "$in_file") || die "Can't open file [$in_file].";

# 置換した後のデータを入れる為の変数を初期化

$out_data="";

# ループ処理で全ての行にわたって処理

while (<IN>) {

# 行末に改行文字があれば削除

chomp;

# 1行のデータをタブで区切られた項目ごとに変数に格納

# 以下の例では、

# $data[0] に0カラム目

# $data[1] に1カラム目

# というように格納される

@data = split(/\t/);

# カラム毎に処理を行う

# $#data は配列の要素の数を返す

for ($i = 0 ; $i <= $#data ; $i++) {

# 0カラム目で無ければ区切り文字のタブを挿入

# \t はタブを挿入

# .= は左辺の変数に右辺の文字列を追加していく。

if ($i != 0) {

$out_data .= "\t";

}

# 性別の指定カラムの場合は性別を判定。

if ($i == $num) {

# 男性であれば1、女性なら2を代入、

# どちらでもなければそのままの値を挿入

if ($data[$i] eq "男性") {

$out_data .= 1;

} elsif ($data[$i] eq "女性") {

$out_data .= 2;

} else {

$out_data .= $data[$i];

}

} else {

# 性別の指定カラムでなければそのまま代入

$out_data .= $data[$i];

}

# 最後のカラムの場合は改行を入力

if ($i == $#data) {

$out_data .= "\n";

}

}

}

# オープンした入力ファイルをクローズ

close(IN);

# 変更したデータを出力する為にファイルをオープン

# 既存でファイルがあれば上書きされる

open(OUT, ">$out_file") || die "Can't open file [$out_file].";

# 置換データを書き込む

printf(OUT "%s", $out_data);

# オープンした入力ファイルをクローズ

close(OUT);

# 正常終了として0を返してプログラム終了

exit(0);

  • スクリプトここまで ----------
id:Sugipon

サンプルありがとうございます。

以下のように書いたところ、"女性"の条件に入らなくてはならない場合に

"男性"の条件に入ってしまいます。

なぜでしょうか?

if ($data[$i] == "男性") {

$data[$i] = '1';

print "AAA";

}elsif ($data[$i] == "女性") {

$data[$i] = '2';

print "BBB";

}else {

$data[$i] = $data[$i];

print "CCC";

}

2006/09/05 12:27:41

その他の回答(6件)

id:spyglass No.1

spyglass回答回数455ベストアンサー獲得回数292006/09/04 22:40:45

ポイント5pt

こんばんは。

ある程度のスキルは必要かと思いますが以下のサイトでcgiを使用出来るサーバをレンタルしてください。

無料でレンタル出来るかと思います。

http://www.kooss.com/hp/

次に変換するソースを作ってアップします。

ソースは以下を参考にしてみてください。

http://perl.misty.ne.jp/index.html

基本的には「11ファイルの処理」と「08文字の扱い」と「03処理の制御1」の処理が参考になるかと思います。

id:Sugipon

すでに動作するサーバはあり、簡単にはテストをしていますが、記述方法がいまいち不明です。。

サンプルページがあるとうれしいのですが。

2006/09/04 22:46:41
id:cubick No.2

cubick回答回数125ベストアンサー獲得回数362006/09/04 22:44:29

ポイント10pt

$record に1行毎のデータが入っているとして…

# フォーマットは以下の通り
# $record = "年月日\t氏名\t誕生月\t誕生日\t性別";

# タブ区切りでフィールドを分割
($date,$name,$month,$day,$sex) = split(/\t/,$record);

# 1桁(=10未満)なら0を補完
$month = "0" . $month if($month < 10);

# 性別の判定
if($sex eq "男性") {
	$sex = "1";
elsif($sed eq "女性") {
	$sex = "2";
}

あとはループで処理すればOKかと思います。

id:spyglass No.3

spyglass回答回数455ベストアンサー獲得回数292006/09/04 22:53:05

どの辺りが不明なのかもう少し教えて頂けますか。

1、ファイルのオープン・クローズは分からないのか?

2、文字列の中から任意の文字の変換が分からないのか?

まったく同じサンプルのページは無いかと思われますので教えてください。

id:yoshi_12 No.4

yoshi_12回答回数18ベストアンサー獲得回数22006/09/05 01:01:56

ポイント50pt

作ってる間に、似たようなコードを提供してくださった方がいたみたいなんですが

せっかくなので掲載させていただきます。

※動作確認はしましたが、念のためバックアップをとってから試してください。

※私はperlを極めてるわけではないので、他の方がもっとシンプルなコードを提供してくださるかもしれません。


open (FH,"+< test.csv"); #読み書きモードでファイルをオープン

@line=<FH>; #ファイルを行ごとの配列に格納

chomp(@line); #改行コード切捨て

$line_amount=$#line+1; #行数計算


#各行を列単位に分割

for($i=0;$i<$line_amount;$i++){

($nengappi[$i],$shimei[$i],$tanjyoutuki[$i],$tanjyoubi[$i],$seibetu[$i])=split(/\t/,$line[$i]);

}


#誕生月の処理

for($i=0;$i<$line_amount;$i++){

$tanjyoutuki[$i]=sprintf("%02d",$tanjyoutuki[$i]);

}


#性別の処理

for($i=0;$i<$line_amount;$i++){

if($seibetu[$i] eq "男性") {

$seibetu[$i]=1;

}

elsif($seibetu[$i] eq "女性"){

$seibetu[$i]=2;

}

}


truncate(FH, 0); #ファイルをゼロクリア

seek(FH,0,0); #ファイルポインタを先頭に移動


#処理したデータを書き込み

for($i=0;$i<$line_amount;$i++){

print FH $nengappi[$i]."\t".$shimei[$i]."\t".$tanjyoutuki[$i]."\t".$tanjyoubi[$i]."\t".$seibetu[$i]."\n";

}

close (FH);


質問者さんはperlを勉強したいのでしょうか?

それとも、今回、たまたまこのようなシステムが必要になっただけで習得は望んでいないのでしょうか?


前者の場合は、ソースコードをまるまる渡すという行為はよくなかったかもしれませんね。

前者かもしれないので、perlの基礎講座のようなサイトをいくつかのせておきます。

http://www.site-cooler.com/kwl/perl/

http://www.kent-web.com/perl/index.html

http://www.poizun.jp/content/cgi/reference/

id:Sugipon

ありがとうございます。

どちらかというと勉強したいというより、仕事で急に必要になったものですから。。。

2006/09/05 09:37:03
id:pmakino No.5

まきのっぴ回答回数355ベストアンサー獲得回数282006/09/05 01:04:59

ポイント10pt

こんな感じでしょうか?

#!/usr/bin/perl

print "Content-Type: text/plain\n\n"; # HTTPレスポンスヘッダ

my $file = 'test.csv'; # 読み込むファイル名

open(my $fd, $file); # ファイルを開く

print scalar <$fd>; # 1行目はそのまま出力

foreach (<$fd>) { # 以下1行ずつ読み込み

    chop; # 行末の改行削除

    my @cols = split("\t", $_); # タブで分割して配列へ

    $cols[2] = sprintf('%02d', $cols[2]); # 2桁目は0フィル2桁整数変換

    $cols[4] = 1 if $cols[4] eq '男性'; # 4桁目は男性なら1

    $cols[4] = 2 if $cols[4] eq '女性'; # 4桁目は女性なら2

    print join("\t", @cols), "\n"; # タブで結合して改行をつけて出力

}

close($fd); # ファイルを閉じる

実際には文字コード違いへの配慮や余計な空白の除去や諸々の例外処理を考える必要がありますが、その辺ばっさり省略しています。

蛇足ですが、CSV は Comma Separated Value (カンマで区切られた値) という意味ですので、タブ区切りテキストなら TSV (Tab Separated Value) と呼ぶのが適切かと思います。(Excel の場合拡張子は .txt になりますが)

id:arkadien No.6

arkadien回答回数131ベストアンサー獲得回数12006/09/05 01:26:27ここでベストアンサー

ポイント50pt

プログラムは人のを見て理解するのが上達の早道と思います。

割と定番サイトですが、perlの関数や書式、演算子などは、

以下がわかりやすいと思います。

http://pzxa85.hp.infoseek.co.jp/www/wwwperl.htm

ご提示の例ですと以下のようなプログラムでいけると思います。

後はデータの形式などにあわせて、

多少改良するといけると思います(動作確認済みです)。

中にコメントも入れておきましたので参考にしてください。


  • スクリプトここから ----------

######################################################################

# 前提

#

# 入力データは以下の形式とする。

# 但し、5カラム目以降も、任意のデータがあった場合でもそのまま格納できる。

#

# 060904 日本太郎 10 4 男性

# 060904 日本花子 9 4 女性

#

# 1データ一行で、区切り文字はタブ1つのみ

# 性別は 4 カラム目にあるとする(0から数えて4カラム目)

# 0カラム目:年月日

# 1カラム目:氏名

# 2カラム目:誕生月

# 3カラム目:誕生日

# 4カラム目:性別

######################################################################

######################################################################

# 以下、条件に合わせて書き換える

# 入力ファイル名を以下に指定。

$in_file = "in.txt";

# 入力ファイル名を以下に指定。

$out_file = "out.txt";

# 性別が4カラム目にある場合は4を指定。

$num = 4;

######################################################################

# 前提が変わらない場合はこれ以降の変更は不要。

#

# 入力ファイルをオープン

open(IN, "$in_file") || die "Can't open file [$in_file].";

# 置換した後のデータを入れる為の変数を初期化

$out_data="";

# ループ処理で全ての行にわたって処理

while (<IN>) {

# 行末に改行文字があれば削除

chomp;

# 1行のデータをタブで区切られた項目ごとに変数に格納

# 以下の例では、

# $data[0] に0カラム目

# $data[1] に1カラム目

# というように格納される

@data = split(/\t/);

# カラム毎に処理を行う

# $#data は配列の要素の数を返す

for ($i = 0 ; $i <= $#data ; $i++) {

# 0カラム目で無ければ区切り文字のタブを挿入

# \t はタブを挿入

# .= は左辺の変数に右辺の文字列を追加していく。

if ($i != 0) {

$out_data .= "\t";

}

# 性別の指定カラムの場合は性別を判定。

if ($i == $num) {

# 男性であれば1、女性なら2を代入、

# どちらでもなければそのままの値を挿入

if ($data[$i] eq "男性") {

$out_data .= 1;

} elsif ($data[$i] eq "女性") {

$out_data .= 2;

} else {

$out_data .= $data[$i];

}

} else {

# 性別の指定カラムでなければそのまま代入

$out_data .= $data[$i];

}

# 最後のカラムの場合は改行を入力

if ($i == $#data) {

$out_data .= "\n";

}

}

}

# オープンした入力ファイルをクローズ

close(IN);

# 変更したデータを出力する為にファイルをオープン

# 既存でファイルがあれば上書きされる

open(OUT, ">$out_file") || die "Can't open file [$out_file].";

# 置換データを書き込む

printf(OUT "%s", $out_data);

# オープンした入力ファイルをクローズ

close(OUT);

# 正常終了として0を返してプログラム終了

exit(0);

  • スクリプトここまで ----------
id:Sugipon

サンプルありがとうございます。

以下のように書いたところ、"女性"の条件に入らなくてはならない場合に

"男性"の条件に入ってしまいます。

なぜでしょうか?

if ($data[$i] == "男性") {

$data[$i] = '1';

print "AAA";

}elsif ($data[$i] == "女性") {

$data[$i] = '2';

print "BBB";

}else {

$data[$i] = $data[$i];

print "CCC";

}

2006/09/05 12:27:41
id:m-nisi No.7

m-nisi回答回数159ベストアンサー獲得回数32006/09/05 01:40:55

ポイント15pt

#!/usr/bin/perl

# ファイル読み込み

open(IN,"test.csv");

while (<IN>) {

# タブ区切りのデータを分割

($date,$name,$month,$day,$sex) = split(/\t/,$_);

# $monthが10以下なら0を頭に付ける処理

if ($month < 10) {

$month = '0' . $month;

}

# 性別によって数字を代入する処理

if ($sex eq "男性") {

$sex = 1;

} elsif ($sex eq "女性" {

$sex = 2;

} else {

$sex = 3;

}

# $date:年月日

# $name:氏名

# $month:2桁の誕生月

# $day:誕生日

# $sex:1 or 2

# この後何らかの処理を入れてください。

}

close(IN);

ところで、タブ区切りのデータは拡張子をtsvとするのが一般的です。

  • id:arkadien
    補足です。

    == は数値比較演算子
    eq は文字列比較演算子です。

    == にした場合、数値として比較しようとしているのですが、
    実際は数値ではなく文字列データが入っているので、
    意図した動作にならない結果となっています。
    文字列を比較したい場合は文字列演算子を使う必要があります。

    http://pzxa85.hp.infoseek.co.jp/www/wwwperl1.htm#CompNumOperand

    単純に確認してみると、以下のスクリプトを実行すると
    $a="男性";
    $b="女性";
    printf("[%d][%d]\n", $a, $b);

    実行結果は
    [0][0]
    となると思います。
    perlとして文字列で入っている変数を数値として見ると、
    0の数値が入っていると判断されてしまい、
    最初の判断文で一致しているとみなされている、
    ということがおきています。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません