AWK を使っていあのですが、perl への移行を目指して勉強しています。
(1) 行数が3の倍数
(2) 列数は分からない(スペース区切り。固定列数)
(3) # はコメント行
というデータがあります。
このデータを perl に読み込ませて、
三行ずつ足して出力する
ようなプログラムをつくっています。
例えば、6行4列のデータ test.dat
# comment
1 2 3 5
3 2 1 6
2 2 2 7
4 5 6 7
6 5 4 6
5 5 5 5
を cat test.dat | sum3row.pl のように perl のプログラム sum3row.pl に読みこませて、三行ずつ足して
# comment
6 6 6 18
18 18 18 18
という出力を得たいのです。
次の点で困ってます。
●AWK の場合、今読み込んでいる行の列数は NF という変数で分かるのですが、perl ではよく分かりません。データへのアクセス自体は $data[2] のようにすれば良いことは分かっているのですが・・。
●AWK の場合、今読み込んでいる行の番号は NR という変数で分かるのですが、perl ではよく分かりません。
すみませんが、よろしくお願いします。。
サンプルプログラムでも助かります(読んで自分で勉強しますので)。
No.5ベストアンサー
- 回答日時:
こんにちは、#2です。
皆さん短く書くのがお好きなようなので、
もう一度チャレンジです。(暇ですね~私)
map してますが、ループが一つなのでわかり易いかな、と。
単に標準入力から読み込んで、計算して、
3回計算したら、出力してカウンターとバッファをクリア。
最後に3回計算する前に読み込みが終了した場合の出力があるのがちょっとカッコ悪い気もしますが。
データ行が必ず3行セットなら、最終行は要りません。
無理すればもうちょっと短く書けるんでしょうが、このくらいが
分かりやすいかな?と思っています。
#! /usr/bin/perl
use strict;
use warnings;
my( $i , @sum );
while ( <> ) {
next if /^#/ and print;
chomp;
my @col = split /\s/;
map { $sum[$_] += $col[$_] } ( 0 .. $#col );
if ( ++ $i == 3 ) {
print join(' ' , @sum) , "\n";
( $i , @sum ) = undef;
}
}
print join(' ' , @sum) , "\n" if @sum;
みなさん、ありがとうございます。大変勉強になりました。
能力不足で、みなさんのエレガントな解を全ては理解できなかったのですが、#5さんのをベースに下記のものを作ってみました。
#! /usr/bin/perl -w
use warnings;
if (@ARGV == 0) {
$period = -1;
} elsif (@ARGV == 1) {
$period = $ARGV[0];
} else {
&usage;
}
my $i = 0;
while (<STDIN>) {
next if /^#/ and print;
chomp;
my @col = split /\s/;
map { $sum[$_] += $col[$_] } ( 0 .. $#col );
$i++;
if ( $i == $period ) {
print join(' ' , @sum) , "\n";
$i = 0;
@sum = undef;
}
}
if ($period == -1) {
print join(' ' , @sum) , "\n";
} elsif ($i > 0) {
print qq{The number of data rows is not a multiple of $period!!\n};
}
sub usage {
my $str = "usage:\n";
$str .= "cat data.txt | sumColsPeriodically.pl <n>\n";
$str .= "<n> : number of rows to sum up periodically.\n";
$str .= " (default = number of all rows).\n";
die qq{$str\n};
}
No.4
- 回答日時:
今回、NFとNRを使ってみました。
white-tigerさんの肌に合えばと思います。設定として、ファイルから読み込んだデータが@d1に一行ずつ入っていて、出力データは@outに入ります。コメントはそのまま出力されます。
@d1=("# comment",
"1 2 3 5",
"3 2 1 6",
"2 2 2 7",
"4 5 6 7",
"6 5 4 6",
"5 5 5 5");
@out=();
for($NF=0;$NF<=$#d1;){#行数は$NF
my(@tmp);#データを仮に足していく配列
for($b=1;$b<=3;++$b){#三行一セット
while($d1[$NF]=~/#/){push(@out,$d1[$NF]);++$NF;}#コメントの追加
my(@d2)=split(/\s/,$d1[$NF++]);#一行のデータを列に分割
for($NR=0;$NR<=$#d2;++$NR){列のデータを配列に足していく
$tmp[$NR]+=$d2[$NR];
}
}
push(@out,join(" ",@tmp));#一時配列のデータを一行にする
}
foreach(@out){print "$_\n";}#出力
基本的な考え方は、データを一行読むごとに、一時的に作った配列に数字を足していくのですが、その時に$NRを上手く使って、縦に足します。
そしてそれ三回繰り返し、配列のデータをスペースを区切りとして、一行のデータにまとめ(join)出力配列に入れる。
それを一セットとして、何度も続ける感じです。
コツは始めのforの$NFで行数を数えず、中で数えるところ。
myを使って、こまめに配列のデータを綺麗に削除して、次に影響が出ないようにするところ。
それと列にデータを分割する際に、$NFを後置インクリメントで、変数を中身そのままで一度処理して、その後増加させているところでしょうか。非常に微妙なラインの数字遊びと化しています。
データは綺麗にそろっているようなので、数字判定などは入れてません。
ちょっと自分的に迷わないコメントをつけていたらごちゃごちゃしました。意外に難しい処理ですね。
No.3
- 回答日時:
> 今読み込んでいる行の列数
@dataがすでにあるって前提だと、$cols = @data; とか scalar(@data) とかです。
> 今読み込んでいる行の番号
$. と言う名前の特殊変数に入ります。
ただ、今回の処理だとコメントがあるので $. % 3 == 0 が使えないですね。
一応処理も書いてみました。思ったよりスマートに書きにくい処理ですね。こんなになっちゃいました。
while(1){
my @lines = ();
while( @lines < 3 && (my $l = scalar(<>)) ){ push @lines, [split(/ /, $l)] unless $l =~ /^#/ };
@lines or last;
print join(' ',
map {my $s = 0; for my $l (@lines) { $s += $l->[$_]; } $s;} (0 .. $#{ $lines[0] })
), "?n";
}
No.2
- 回答日時:
こんにちは、
すでに回答が出ちゃってますが、
私も書いてみたので、参考までに。
#! /usr/bin/perl
use strict;
use warnings;
while ( my @lines = &get_recs(3) ) {
my @sum;
for my $line ( @lines ) {
my @cols = split /\s/ , $line;
for my $i ( 0 .. $#cols ) {
$sum[$i] += $cols[$i];
}
}
print join(' ' , @sum) , "\n";
}
exit;
sub get_recs {
my $return_rows = shift;
my @lines = ();
while ( @lines < $return_rows ) {
my $line = <>;
last unless $line;
if ( $line =~ /^#/ ) {
print $line;
next;
}
chomp $line;
push @lines , $line;
}
return @lines;
}
#1さんよりごちゃごちゃしてますが、
とりあえず、
# comment
6 6 6 18
15 15 15 18
と表示されます。
No.1
- 回答日時:
試しに書いてみました。
完璧ではないかもしれませんが、こんな感じです。
#!Perl
use strict;
while (<>) {
print, next if /^#/;
my @rec3 = ($_); # 1行目
my $rec1 = <>; # 2行目
my $rec2 = <>; # 3行目
push @rec3 => $rec1, $rec2; # ひとつに
calc3print(@rec3); # 計算するサブルーチン
}
sub calc3print {
my (@rec) = @_;
my @col;
for (@rec) {
chomp;
my @col_this = split;
my $index = 0;
$col[$index++] += $_ for @col_this;
}
print join(' ' => @col), "\n";
}
__END__
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# [C言語] コメント文字列を無視して、数値データを読み込むプログラム部分について 5 2022/10/05 11:03
- Perl perlで2次元配列をサブルーチンに値渡しで渡す 5 2022/12/17 18:49
- C言語・C++・C# pythonのファイルの並びでの読み込みとリストについて 4 2022/04/13 03:52
- Perl Windows10においての『Perl』のプログラムについて 1 2022/05/09 16:04
- Perl perlについて 2 2022/11/18 19:38
- Perl perlでリテラル値はメモリにどのように格納されているか? 1 2023/01/15 20:45
- その他(プログラミング・Web制作) プログラミング python pandas 固定長データの出力 2 2022/08/16 11:22
- PHP 配列の値の更新方法について 1 2022/08/05 09:49
- C言語・C++・C# このプログラミングの問題を教えてほしいです。 キーボードからデータ数nとn個のデータを入力し、平均値 3 2022/12/19 22:51
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
配列のサイズを動的に拡張
-
Pythonの再帰関数の動作の流れ...
-
文字列をカウントする方法
-
ファイル全てを .xlsm に変更し...
-
一定時間が経過したフォルダの削除
-
csvファイルの横方向への改行に...
-
while(<ハンドラ>) {} で行数を...
-
画像アップロード機能を追加し...
-
batファイルでrenameができませ...
-
while文がうまく動かない
-
VBAでCSVファイルの特定行を書...
-
MATLABのm-fileについて
-
ディレクトリのファイル作成を...
-
ANSI Cでファイル名、ディレク...
-
VB6.0でDB接続する際に切断時の...
-
AutoCADのスクリプト
-
C++でファイルから複数行のデー...
-
オープンしたファイルで行の連結
-
Perlで特定行から特定行までを...
-
連番のファイルを何個も開きたい
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
配列の中に重複文字列があるか...
-
C言語のバイナリモードでのfsca...
-
データの日付でソートをしたい
-
perlで複数行のデータを自由に...
-
perl-cgi 文字の長さでソートし...
-
Visual C++を用いたシリアル通信
-
バッチファイルの作り方(CSV→...
-
awkスクリプトでダブルクォーテ...
-
VBAでCSVファイルの特定行を書...
-
DOSコマンドで、標準出力を出力...
-
ExcelをCSV書き出す場合のシー...
-
openした後、closeしないでプロ...
-
close()で例外が投げられる理由
-
batファイルでrenameができませ...
-
VBAでCSVファイルを途中行まで...
-
window.open でのファイル指定方法
-
VBAで巨大なファイルの途中から...
-
Perlで特定行から特定行までを...
-
ReadLineでの読み出し行を指定する
-
エクセルVBAで素数だけを出力す...
おすすめ情報