正規表現でCSVファイルから行を取り出す準備
正規表現を使ってCSVファイルから必要な行を取り出してみましょう。第2章で学習したCSVデータに発売日などの情報を追加したデータを使います。
ID,書名,著者名,価格,発売日 1,Perlテキスト処理プログラミング入門,木本裕紀,2900,2021-01-03 2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03 3,データベース入門,田中太郎,1900,2019-03-06 4,正規表現テクニック,田中太郎,3000,2019-01-08 5,本音で語るWeb開発,望月正太郎,5000,2018-02-19 6,気になるperl,竜崎新次郎,5000,2017-05-23
これを「book_data.csv」として保存してください。文字コードはUTF-8で保存してください。
まず、第2章で学習した、プログラムを少し改造します。CSVファイルを入力して、CSVデータをそのまま出力するプログラム「output_csv.pl」を書きます。
use strict; use warnings; use utf8; use Encode 'encode', 'decode'; # DATAセクションから行を一行ずつ読み込む while (my $line = <>) { # 一行目は読み飛ばす($.はファイルの行番号。nextで次のループの先頭へ) if ($. == 1) { next } # PerlのUTF-8を内部文字列へデコード $line = decode('UTF-8', $line); # 改行を削除 chomp $line; # カンマで分割して各変数へ my ($id, $name, $author, $price, $issued_date) = split(/,/, $line); # 出力行を作成 my $output_line = join(',', $id, $name, $author, $price, $issued_date); # Perlの内部文字列をUTF-8にエンコードして出力 print encode('UTF-8', $output_line) . "\n"; }
次のように入力ファイルと出力ファイルを指定して実行します。
perl output_csv.pl book_data.csv > raw_output.csv
出力結果です。ヘッダを除いて、入力されたデータとまったく同じデータが出力されていれば、OKです。
1,Perlテキスト処理プログラミング入門,木本裕紀,2900,2021-01-03 2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03 3,データベース入門,田中太郎,1900,2019-03-06 4,正規表現テクニック,田中太郎,3000,2019-01-08 5,本音で語るWeb開発,望月正太郎,5000,2018-02-19 6,気になるperl,竜崎新次郎,5000,2017-05-23
正規表現を使った検索を実践していきましょう。
検索条件を指定してみよう
さまざまな検索条件を指定してみましょう。
書名にPerlが含まれている
Perlが書名に含まれている行だけを出力してみましょう。
以下の正規表現を使います。「=~」は、パターンマッチ演算子です。「/」と「/」の間に正規表現を書きます。
if ($name =~ /Perl/) { # マッチした場合の処理 }
以下がプログラムの全体「contain_perl.pl」です。マッチした場合のみ行を出力します。
use strict; use warnings; use utf8; use Encode 'encode', 'decode'; # DATAセクションから行を一行ずつ読み込む while (my $line = <>) { # 一行目は読み飛ばす($.はファイルの行番号。nextで次のループの先頭へ) if ($. == 1) { next } # PerlのUTF-8を内部文字列へデコード $line = decode('UTF-8', $line); # 改行を削除 chomp $line; # カンマで分割して各変数へ my ($id, $name, $author, $price, $issued_date) = split(/,/, $line); if ($name =~ /Perl/) { # 出力行を作成 my $output_line = join(',', $id, $name, $author, $price, $issued_date); # Perlの内部文字列をUTF-8にエンコードして出力 print encode('UTF-8', $output_line) . "\n"; } }
実行してみましょう。
perl contain_perl.pl book_data.csv
出力結果では、書名に「Perl」を含む行だけが出力されます。
1,Perlテキスト処理プログラミング入門,木本裕紀,2900,2021-01-03 2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03
書名がPerlで始まる
書名がPerlで始まるという正規表現に変えてみましょう。「^」は先頭を意味する正規表現文字です。サンプルプログラムは「contain_perl_head.pl」です。
if ($name =~ /^Perl/) { # 出力処理 }
実行してみましょう。
perl contain_perl_head.pl book_data.csv
出力結果です。書名がPerlで始まる行だけが出力されています。
1,Perlテキスト処理プログラミング入門,木本裕紀,2900,2021-01-03
書名がPerlで終わる
書名がPerlで終わるという正規表現に変えてみましょう。「$」は終端を意味する正規表現文字です。サンプルプログラムは「contain_perl_tail.pl」です。
if ($name =~ /Perl$/) { # 出力処理 }
実行してみましょう。
perl contain_perl_tail.pl book_data.csv
出力結果です。
2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03
Perlの大文字と小文字を区別せずに検索
Perlの大文字と区別を区別せずに検索してみましょう。「Perl」「PERL」「perl」などにマッチさせます。
大文字と小文字を区別せずに検索するには正規表現の「i」オプションを使用します。サンプルプログラムは「contain_perl_nocase.pl」です。
if ($name =~ /perl/i) { # 出力処理 }
実行してみましょう。
perl contain_perl_nocase.pl book_data.csv
出力結果です。「Perl」を含む行、「perl」を含む行が取り出せています。
1,Perlテキスト処理プログラミング入門,木本裕紀,2900,2021-01-03 2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03 6,気になるperl,竜崎新次郎,5000,2017-05-23
書名にPerlまたはWebを含む
書名にPerlまたはWebを含むという正規表現を書いてみましょう。またはを表現する「|」を使用し、「()」で囲みます。サンプルプログラムは「contain_perl_or_web.pl」です。
if ($name =~ /(Perl|Web)/) { # 出力処理 }
実行してみましょう。
perl contain_perl_or_web.pl book_data.csv
出力結果です。書籍にPerlとWebが含まれている行が出力されます。
1,Perlテキスト処理プログラミング入門,木本裕紀,2900,2021-01-03 2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03 5,本音で語るWeb開発,望月正太郎,5000,2018-02-19
書名が「Web(なんでもよい一文字以上)Perl」を含む
書名が「Web(なんでもよい一文字以上)Perl」という正規表現を書いてみましょう。サンプルプログラムは「contain_web_any_perl.pl」です。
if ($name =~ /Web.+Perl/) { # 出力処理 }
「.」で「改行文字を除いたすべての文字」を表現できます。「+」で「直前の文字の1文字以上」を表現できます。
実行してみましょう。
perl contain_web_any_perl.pl book_data.csv
出力結果です。
2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03
もし、WebとPerlの間が0文字でも良い場合は「+」の代わりに「*」を使うことができます。「*」で「直前の文字の0文字以上」を表現できます。
if ($name =~ /Web.*Perl/) { # 出力処理 }
価格が1000円以上、2000円より小さい
これは、正規表現ではありませんが、価格のの大小で検索するサンプルを書いてみます。サンプルプログラムは「price_range.pl」です。
if ($price >= 1000 && $price < 2000) { # 出力処理 }
実行してみましょう。
perl price_range.pl book_data.csv
出力結果です。価格が1000円以上、2000円より小さい行が出力されています。サンプルプログラムは「price_range.pl」です。
3,データベース入門,田中太郎,1900,2019-03-06
2018年から2019年の間に発売された
2018年から2019年の間に発売されたという条件を書いてみましょう。サンプルプログラムは「issued_date_range_ymd.pl」です。
# 正規表現で発売日の年の部分を取得 my $issued_year; if ($issued_date =~ /^(\d{4})/) { $issued_year = $1; } # 出版年を取得できた場合のみ処理 if (defined $issued_year) { # 出版年が2018年~2019年まで if ($issued_year >= 2018 && $issued_year <= 2019) { # 出力処理 } }
実行してみましょう。
perl issued_date_range_ymd.pl book_data.csv
出力結果です。2018年から2019年の間に発売された書籍のみ出力されます。
3,データベース入門,田中太郎,1900,2019-03-06 4,正規表現テクニック,田中太郎,3000,2019-01-08 5,本音で語るWeb開発,望月正太郎,5000,2018-02-19
年を取得する正規表現の解説
年を取得する正規表現の解説です。
# 正規表現で発売日の年の部分を取得 my $issued_year; if ($issued_date =~ /^(\d{4})/) { $issued_year = $1; }
「^」は先頭を表現する正規表現文字。「\d」は、数字を表現する正規表現の文字クラス。「{4}」は、4文字続くという意味の正規表現の量指定子です。
丸カッコ「()」を使って、キャプチャすることができます。キャプチャされた文字は「$1」という変数で取得できます。
次の処理を見てみましょう。年が取得できた場合のみ処理を行いたいのでdefined関数で、年が取得できていることを確認しています。その後は、比較演算子「>=」「<=」と論理演算子「&&」を使って「2018年以上、かつ、2019年以下」という条件を作成しています。
# 出版年を取得できた場合のみ処理 if (defined $issued_year) { # 出版年が2018年~2019年まで if ($issued_year >= 2018 && $issued_year <= 2019) { # 出力処理 } }
年月日を取得する正規表現
上記の例では、年を取っているだけですが、年月日を取得する正規表現を書いてみましょう。キャプチャの丸かっこが複数存在する場合は「$2」「$3」でキャプチャを取得できます。サンプルプログラムは「issued_date_range_ymd.pl」です。サンプルプログラムは「issued_date_range_ymd.pl」です。
my $issued_year; my $issued_month; my $issued_mday; if ($issued_date =~ /^(\d{4})-(\d{2})-(\d{2})/) { $issued_year = $1; $issued_month = $2; $issued_mday = $3; }
実行してみましょう。出力結果は、先ほどのサンプルと同じです。
perl issued_date_range_ymd.pl book_data.csv
出力結果です。2018年から2019年の間に発売された書籍のみ出力されます。
3,データベース入門,田中太郎,1900,2019-03-06 4,正規表現テクニック,田中太郎,3000,2019-01-08 5,本音で語るWeb開発,望月正太郎,5000,2018-02-19
文字列比較で日付の範囲を指定する方法
また単純に以下のように文字列比較演算子「ge」「le」を使って文字列比較をしてもかまいません。サンプルプログラムは「issued_date_range_string.pl」です。
# 2018年1月1日から2019年12月31の間に発売された if ($issued_date ge '2018-01-01' && $issued_date le '2019-12-31') { # 出力処理 }
実行してみましょう。
perl issued_date_range_string.pl book_data.csv
出力結果です。2018年から2019年の間に発売された書籍のみ出力されます。
3,データベース入門,田中太郎,1900,2019-03-06 4,正規表現テクニック,田中太郎,3000,2019-01-08 5,本音で語るWeb開発,望月正太郎,5000,2018-02-19
次の章では、HTMLを題材にして正規表現の検索と置換をさらに実践していきます。