CSVファイルをPerlで処理する方法について質問です。


CSVファイルに以下のようなデータが入っています。

2,4,a2,5,9,10,7,11,
5,3,4,12,2,18,2,12,
7,4,a6,11,5,2,6,4,
6,2,a9,34,4,2,7,2,
4,7,2,6,6,9,2,7,
4,9,a2,8,1,7,a1,5,
(こういう感じで続いていきます)

このファイルを読み込み,小文字aのすぐあとの数字と,aが含まれているデータの次のデータを出力させたいです。(説明が下手くそですみません)

つまり上のデータだったら,

2,5
6,11
9,34
2,8

というように出力させたいです。
データ処理のためにPerlをはじめたのですが,なかなか苦労しているのでお願いします。。。

回答の条件
  • 1人3回まで
  • 登録:2007/11/19 20:13:07
  • 終了:2007/11/21 01:07:02

ベストアンサー

id:lunlumo No.3

lunlumo回答回数107ベストアンサー獲得回数142007/11/20 00:41:20

ポイント40pt

 thrillseekerさんのコードだと,aの付くデータが一行に二つ以上あった場合に最初のデータしか出力しませんね。同様に簡易な処理で行うならこんな感じになるでしょうか。

#! /usr/bin/perl

while (<>) {
	print "$1\n" while ($_ =~ m/a(\d+,\d+),/g);
}

 ただこの処理では少し荒いですしtkyk3の指摘されている問題もあるので,Text::CSVなりText::CSV::Simpleなりを使ってまじめに処理した方が良いでしょう。

#! /usr/bin/perl

use	strict;
use	IO::File;
use	Text::CSV;

my	$file;
my	$csv;

if (scalar(@ARGV) != 1) {
	print "usage: $0 DATA_FILE_PATH\r\n";
	exit(0);
}

$file = new IO::File;
$file->open("<".$ARGV[0]) || die "cannot open file.";

$csv = new Text::CSV;

while (1) {
	my	$columns = $csv->getline($file);
	last if ($csv->eof());
	unless ($csv->status()) {
		print STDERR $csv->error_diag()."(".$csv->error_input().")\r\n";
		next;
	}
	for (my $i = 0; $i < scalar(@$columns) - 1; $i++) {
		if ($columns->[$i] =~ m/^a(\d+)/) {
			unless ($csv->combine($1,$columns->[$i+1])) {
				print STDERR "invalid values\r\n";
			} else {
				print $csv->string()."\r\n";
			}
			$i++;
		}
	}
}

$file->close();

1;

id:ishikennn

Text::CSVっていうのはとても便利そうですね!ありがとうございます。

2007/11/21 01:05:56

その他の回答(2件)

id:thrillseeker No.1

thrillseeker回答回数328ベストアンサー獲得回数372007/11/19 20:25:35

ポイント20pt

正規表現というものを使えばうまく処理できます。

#! /usr/bin/perl

while (<>) # 引数で与えられたファイルから1行ずつ読み込んで処理する
{
    if (/a(\d+,\d+),/) { print "$1\n"; } # 正規表現に一致したら()の部分を出力する
}
id:ishikennn

ありがとうございます。そういうやりかたがあったんですね!

ちなみに$1というのはどういう意味なんでしょうか?

2007/11/19 22:09:26
id:tkyk3 No.2

tkyk3回答回数59ベストアンサー獲得回数62007/11/19 22:56:59

ポイント20pt

$1は、()内にマッチした値が入っている特殊な変数です。

if ( /(\d+),(\d+),(\d+)/ ) 
    print "最初:$1、2番目:$2、3番目:$3";
}

括弧ごとにそれぞれ$1,$2,$3...と入っていきます。

プログラミングPerl〈VOLUME1〉

プログラミングPerl〈VOLUME1〉

  • 作者: ラリー ウォール ジョン オーワント トム クリスチャンセン
  • 出版社/メーカー: オライリー・ジャパン
  • メディア: 単行本

この本に紹介されています(ちょっと高いけど。でも、買って損はないです。)

あと、CSVデータの扱いに関しては「"」とかを考え出すと意外に処理が難しいので、

Perlクックブック〈VOLUME1〉

Perlクックブック〈VOLUME1〉

  • 作者: トム クリスチャンセン ネイザン トーキントン
  • 出版社/メーカー: オライリージャパン
  • メディア: 単行本

こちらも本屋さんでぱらっと見てみてください。

余談:

use Text::CSV::Simple;
my $csv = Text::CSV::Simple->new({ binary => 1 });
$csv->field_map(qw/id name address tel/);
my @list = $csv->read_file("address.txt");

これだけで、@listにCSVファイルを読み込んでしまい込む事が出来ます。毎日が発見です。頑張ってください!!

id:ishikennn

ちゃんとした本を買ったほうがよさそうですね。ありがとうございます。

2007/11/21 01:05:54
id:lunlumo No.3

lunlumo回答回数107ベストアンサー獲得回数142007/11/20 00:41:20ここでベストアンサー

ポイント40pt

 thrillseekerさんのコードだと,aの付くデータが一行に二つ以上あった場合に最初のデータしか出力しませんね。同様に簡易な処理で行うならこんな感じになるでしょうか。

#! /usr/bin/perl

while (<>) {
	print "$1\n" while ($_ =~ m/a(\d+,\d+),/g);
}

 ただこの処理では少し荒いですしtkyk3の指摘されている問題もあるので,Text::CSVなりText::CSV::Simpleなりを使ってまじめに処理した方が良いでしょう。

#! /usr/bin/perl

use	strict;
use	IO::File;
use	Text::CSV;

my	$file;
my	$csv;

if (scalar(@ARGV) != 1) {
	print "usage: $0 DATA_FILE_PATH\r\n";
	exit(0);
}

$file = new IO::File;
$file->open("<".$ARGV[0]) || die "cannot open file.";

$csv = new Text::CSV;

while (1) {
	my	$columns = $csv->getline($file);
	last if ($csv->eof());
	unless ($csv->status()) {
		print STDERR $csv->error_diag()."(".$csv->error_input().")\r\n";
		next;
	}
	for (my $i = 0; $i < scalar(@$columns) - 1; $i++) {
		if ($columns->[$i] =~ m/^a(\d+)/) {
			unless ($csv->combine($1,$columns->[$i+1])) {
				print STDERR "invalid values\r\n";
			} else {
				print $csv->string()."\r\n";
			}
			$i++;
		}
	}
}

$file->close();

1;

id:ishikennn

Text::CSVっていうのはとても便利そうですね!ありがとうございます。

2007/11/21 01:05:56

コメントはまだありません

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

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

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

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