Perl: 時刻が表記された文字列の認識
任意の正規表現パターンと、そのパターンに沿って記述された時刻表記を与えられたとき、
その文字列を読み取り午前0:00からの経過秒数を取得したい。
ただし Perl 5.6 でも動作するものとしたいため、名前付きバッファ(Named Captures)は使えない。
車輪の再発明のような気もするが実装。
実行例
"%H:%M:%S", "01:02:03" ⇒ 1時2分3秒 = 1 * 3600 + 2 * 60 + 3 = 3723
"^\d{8}%H%M%S", "20120119010203456789" ⇒ 1時2分3秒 = 1 * 3600 + 2 * 60 + 3 = 3723
"%H : %M (:? : %S )?, "01:02" ⇒ 1時2分 = 1 * 3600 + 2 * 60 = 3720
"%S [ ] %H – %M", "03 01-02" ⇒ 1時2分3秒 = 1 * 3600 + 2 * 60 + 3 = 3723
コード
i番目に出現したフォーマット識別子に対して、単位あたりの秒数 Wiを設定してゆく。
読み込んだ数値をviとすれば、求める値はΣwi × vi である。
#!/usr/bin/env perl use strict; use warnings; #----------------------------------------------------------------------------- # Time Reader class #----------------------------------------------------------------------------- package TimeReader; # Define format specifiers. # hash to a list of regular expression pattern and how many seconds in a spec. my %_SPEC_FOR = ( '%H' => [ '\\d{2}', 60 * 60 ], '%M' => [ '\\d{2}', 60 ], '%S' => [ '\\d{2}', 1 ], ); ############################################################################## # Constructor: new() # sub new { my ($class, $format) = @_; # format pattern my @weights; SEARCH_SPEC: for my $i (0 .. length($format) - 2) { my $str = substr($format, $i, 2); next SEARCH_SPEC if !exists $_SPEC_FOR{$str}; push @weights, $_SPEC_FOR{$str}->[1]; } while (my ($key, $value) = each %_SPEC_FOR) { $format =~ s/ $key / ( $value->[0] ) /xms; } # Create instance. my $new_object = bless {}, $class; $new_object->{reg_exp} = $format; $new_object->{weights} = \@weights; return $new_object; } ############################################################################## # Method read() # # Read text, then returns elapsed seconds from 0:00 am. # sub read { my ($self, $text) = @_; # text includes time representation my @ret = $text =~ m/ $self->{reg_exp} /xms or return; my $seconds = 0; for my $i (0 .. $#{ $self->{weights} }) { if ($ret[$i]) { $seconds += $ret[$i] * $self->{weights}[$i]; } } return $seconds; } package main; print TimeReader->new('%H:%M:%S')->read('01:02:03'), "\n"; print TimeReader->new('^\d{8}%H%M%S')->read('20120119010203456789'), "\n"; print TimeReader->new('%H : %M (:? : %S )?')->read('01:02'), "\n"; print TimeReader->new('%S [ ] %H - %M')->read('03 01-02'), "\n"; # unexpected result? print TimeReader->new('%H:%H')->read('01:02'), "\n"; print TimeReader->new('%H*')->read('0102'), "\n";
制約事項
・捕捉すべき同種のフォーマット識別子が複数回マッチしないようにすること
(a) パターンの中に捕捉すべき同種のフォーマット識別子が複数回使われた場合、
捕捉文字列に置き換わるのは最初の一回のみである。
"%H:%H", "01:02" ⇒ ( \d{2} ) :%H として捕捉 ⇒ マッチしないため undef を返す
(b) 補足すべきフォーマット識別子に対して2回以上の量指定子を利用した場合、
最後にマッチした部分の値が評価される。
"%H*", "0102" ⇒ ( \d{2} )* として捕捉 ⇒ 最後にマッチするのは「02」 ⇒ 2時として評価 ⇒ 2 * 3600 = 7200
・フォーマット識別子の定義は2文字であること(通常は '%' と他1文字とする)
0 件のコメント:
コメントを投稿