概要
コマンド出力等にありがちな、以下の条件の固定長テキストを解析するためのサブルーチン。
- 全体としては固定長テキスト
- 先頭はヘッダ行
- カラムは実行ごとに異なる(例えばコマンド実行時の引数で変わる)
- カラムの幅は実行ごとに異なる(例えば出力されるデータにあわせて変わる)
- カラムの間は2文字の空白で区切られている
- レコードによっては、あるカラムが空のこともある
解析方法
以下の方針で解析します。
- 1行目をヘッダ行として処理します。
- ヘッダ行の「2文字以上の空白」の出現場所から、各フィールドの長さを判断します。
- 残りの行を、固定長テキストとして解析します。
- 結果は、ハッシュリファレンスの配列の形で返します。
ヘッダ行から「固定長」の長さを取得するので、事前に各フィールドの長さを定義する必要がなくなります。レコード行には、空のフィールドや、2文字以上の空白を含むフィールドがあっても大丈夫です。
サブルーチン
sub parse_multispaces_separated {
my $text = shift;
my @rows = ();
# split each lines
my @lines = grep { /\S/ } split(/\n/, $text);
my @length = map { length($_) } ($lines[0] =~ /(\S(?: ?\S)* {2,})/g);
foreach my $line (@lines) {
my @values = map { substr($line, 0, $_, '') } @length;
push(@values, $line);
s/^\s+|\s+$//g foreach (@values);
push(@rows, \@values);
}
# get column names and hash-nize
my @columns = @{shift(@rows)};
foreach my $row (@rows) {
my @values = @{ $row };
my %hash = map { $columns[$_] => $values[$_] } (0..$#values);
$row = \%hash;
}
return @rows;
}
使用例
my $text = <<EOF;
Customer Name Location email
Akai Isao Tokyo, Japan akai@example.com
Uda Eijiro Osaka, Japan eijiro.uda@example.com
Oswald Kahn Aidaho, U.S. kahn-oswald@oswald-inc.example.com
Silvia Tolen silvia@example.com
EOF
my @rows = &parse_multispaces_separated($text);
use Data::Dumper;
print Dumper(\@rows);
sub parse_multispaces_separated {
(略)
}
使用例の出力
$VAR1 = [
{
'email' => 'akai@example.com',
'Location' => 'Tokyo, Japan',
'Customer Name' => 'Akai Isao'
},
{
'email' => 'eijiro.uda@example.com',
'Location' => 'Osaka, Japan',
'Customer Name' => 'Uda Eijiro'
},
{
'email' => 'kahn-oswald@oswald-inc.example.com',
'Location' => 'Aidaho, U.S.',
'Customer Name' => 'Oswald Kahn'
},
{
'email' => 'silvia@example.com',
'Location' => '',
'Customer Name' => 'Silvia Tolen'
}
];