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 である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #!/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 件のコメント:
コメントを投稿