Perlテキスト処理・正規表現入門

Perlで始めるCSVのテキスト処理

イントロダクション

「大量データの処理をすると、エクセルが重くて開けない。。」

昔、CSV ファイルを上司から渡されて、エクセルで処理しようとした時の自分の感想です。そのときは、CSV ファイルを3つくらいに分割して処理させました。

しかし、かなり後になって、Perl を使うと、重くて処理できない大容量のファイルでもパソコンをフリーズさせることなく処理できることが分かりました。

このトピックでは、仕事でパソコンを使っているけどプログラミングに詳しくない人を対象に、文書データを便利に処理するための方法を紹介します。

サイズの大きいファイルでもフリーズしないやり方

過去の事務作業で上司から渡されたのは、経理データでした。 今年は法令監査に対応しないといけないので、過去10年分のデータを集計しないといけなかったのです。

データは、CSV 形式でした。CSV というのは、データとデータがカンマ(,)で区切られたものです。

Perl を使ってデータを読み込もう

エクセルで読み込むとパソコンがフリーズするので、どうにかしたいですよね。

そこで、Perl を使ってみましょう。大きなデータも読み込めるし、エクセルと比べてサクサク処理が出来ます。

今回は、まず CSV ファイルを読み込んでみます。

読み込むデータ(data.csv)

year,month,sales,profit
2019,1,1000,800
2019,2,2000,900
2019,3,3000,1000

データを読み込むプログラム(csv_read.pl)

#! /usr/bin/env perl
use strict;
use warnings;

while (my $line = <>) {
  print $line;
}


さあ、以下のようにコマンドを入力し、プログラムを実行します。ドルマーク($)は、コマンドラインであることを示す記号なので、実際には打ち込まないでください。

$ perl csv_read.pl data.csv

実行結果

year,month,sales,profit
2019,1,1000,800
2019,2,2000,900
2019,3,3000,1000$

少し不格好ですが、data.csv のデータがすべて出力されましたね。一歩前進です!

次は、この不格好な出力を何とかしてみましょう。

出力を見やすくしよう

CSV のデータがすべて出力されたのは良いのですが、不格好ですね。

まず、2019,3,3000,1000$という表示になっていますので、ドルマーク($)を同じ行に表示させないようにしましょう。

プログラムを次のように書き換えます。

書き換えたプログラム(csv_read_new.pl)

#! /usr/bin/env perl
use strict;
use warnings;

while (my $line = <>) {
+  chomp $line;
  print $line;
+  print "\n";
}


左側にプラス記号(+)を付けた行が、追加した行です。プラス記号は目印なので、実際のプログラムには書かないでください。

実行結果

year,month,sales,profit
2019,1,1000,800
2019,2,2000,900
2019,3,3000,1000
$

ドルマークが改行されて表示されます。これで見栄えが良くなりました!

プログラムに興味のある方への補足

chomp $line;は、改行を除去する命令文です。

このプログラムは、CSV ファイルを1行ずつ読み込みますが、行には改行の情報も存在しています(正確には、改行コードというデータが行末に存在しています)。

改行の情報はプログラムで処理するには余計なことが多いので、chomp $lineで取り除きます。

print "\n";は、改行情報を表示するための文です。見栄えを整えるために、取り除いた改行の情報をプログラムで出力しています。

このように、改行の情報はプログラムで追加したほうが分かりやすくなります

数字を集計しよう

CSV が見やすくなったところで、数字を集計しましょう。集計対象のデータを再掲します。

読み込むデータ(data.csv)

year,month,sales,profit
2019,1,1000,800
2019,2,2000,900
2019,3,3000,1000

このデータを使って、2019 年の売上げ(sales)はいくらなのか出しましょう。こんな感じですね。

year,sales
2019,6000


まず、コードをこう書きます。

データ処理コード(proc_data.pl)

#! /usr/bin/env perl
use strict;
use warnings;

while (my $line = <>) {
  chomp $line;
  my @row = split /,/, $line;

  print "@row\n";
}

実行結果

year month sales profit
2019 1 1000 800
2019 2 2000 900
2019 3 3000 1000

いや、何も変わってないじゃん!と思われますが、カンマが無くなってますね。

ここでコードを書き換えてみます(左端に-を付けたのが消した箇所、+を付けたのが書き換えた箇所です)。

データ処理コード(proc_data.pl)

#! /usr/bin/env perl
use strict;
use warnings;

while (my $line = <>) {
  chomp $line;
  my @row = split /,/, $line;

-  print "@row\n";
+  print "$row[0]\n";
}

実行結果

year
2019
2019
2019

year の列しか出なくなりましたね!

データを集計するときには、このように、列の単位でデータを扱えたほうが便利です。

列の単位で数字を集計しよう

さて、実際に 2019 年の売上げを集計してみましょう。

読み込むデータ(data.csv)

year,month,sales,profit
2019,1,1000,800
2019,2,2000,900
2019,3,3000,1000

欲しい出力結果

year,sales
2019,6000

データ処理コード(proc_data.pl)

#! /usr/bin/env perl
use strict;
use warnings;

my $sales = 0;
while (my $line = <>) {
  chomp $line;
  my @row = split /,/, $line;

  $sales += $row[2];
  print "$row[0], $row[2]\n";
  print "$row[0], $sales\n";
}

コード実行

$ perl proc_data.pl data.csv

出力結果

Argument "sales" isn't numeric in addition (+) at proc_data.pl line 11, <> line 1.
year, sales
year, 0
2019, 1000
2019, 1000
2019, 2000
2019, 3000
2019, 3000
2019, 6000

なんとか結果は出せましたが、要らない情報がたくさんあります!

次は、これらの情報を削除していきましょう。

要らない情報を消そう

余計な出力を削除していきましょう。

読み込むデータ(data.csv)

year,month,sales,profit
2019,1,1000,800
2019,2,2000,900
2019,3,3000,1000

欲しい出力結果

year,sales
2019,6000

データ処理コード(proc_data.pl)

#! /usr/bin/env perl
use strict;
use warnings;

my $sales = 0;
my $is_header = 1;
my $year = '';
while (my $line = <>) {
  chomp $line;
  my @row = split /,/, $line;

  if ($is_header) {
    print "$row[0], $row[2]\n";
    $is_header = 0;
    next;
  }

  $year = $row[0];
  $sales += $row[2];
}
print "$year, $sales\n";

コード実行

$ perl proc_data.pl data.csv

出力結果

year,sales
2019,6000


欲しい結果と同じになりましたね!

しかし、データ処理コードが複雑になりました。頭を整理しましょう。

次回は複雑になった箇所を解説します。

目次