前回の
文字列
まず紹介するのは文字列処理です。
文字列処理には,
本節では,
特定文字の削除にはy///
を使う
不要な文字や,\n
),\t
)
たとえばメールアドレスは<neko@nyaan.
のように<
と>
で囲まれた形式で現れることがあります。SMTP<
と>
ですが,
次のコードは,s///
と,y///
とで比べた例です。
s-vs-y.
my $Email = '<neko@nyaan.jp>'; # < と > を削除したい
sub ss {
my $v = $Email;
$v =~ s/[<>]//g; # 正規表現の文字クラス
return $v;
}
sub yy {
my $v = $Email;
$v =~ y/<>//d; # 置換対象を1文字単位で列挙
return $v;
}
考察:y///
がs///
に圧勝
表1に示すとおり,y///
置換演算子が圧倒的に速い結果となりました。保守性につながる読みやすさも損なわれていませんし,y///
を使った文字単位での置換が処理時間の短縮となります。
表1 特定文字の削除の比較
比較項目 | 秒間実行回数 | 速度比 |
---|---|---|
s/// | 895,522 | 1. |
y/// | 4,137,931 | 4. |
先頭文字の確認にはindex()を使う
HTTPと同様にSMTPでもステータスコードの200番台は成功を,
次のコードは,4
か5
であることを,index()
関数を使った方法で比較したものです。
check-the-first-character.
my $Status = '550';
sub regexp { return 1 if $Status =~ /\A[45]/ }
sub index2 {
# 4が文字列の先頭なら位置が0になる
return 1 if index($Status, '4') == 0;
# 5が文字列の先頭なら位置が0になる
return 1 if index($Status, '5') == 0;
}
考察:index()
が正規表現に辛勝
index()
関数は2回も実行されていますが,index2
サブルーチンのほうがやや高速に動作します。逆に考えると,
表2 先頭1文字の確認の比較
比較項目 | 秒間実行回数 | 速度比 |
---|---|---|
\A[45] | 4,411,765 | 1. |
index( ) | 5,000,000 | 1. |
文字列の先頭/ 末尾の確認にはindex()とsubstr()を使う
メールアドレスをユーザー名やログインIDとして使っているWebサービスでは,
次のコードは,<
で始まり@
を含み>
で終わる文字列をメールアドレスとみなします。これを,index()
関数,substr()
関数の併用で比べます。
is-email-address-or-not.
my $Email = '<neko@nyaan.jp>';
sub regexp { return 1 if $Email =~ /\A<.*@.*>\z/ }
sub indexs {
# index()とsubstr()
if( index($Email, '<') == 0 && # 先頭文字が<
index($Email, '@') > -1 && # 文字列に@を含む
substr($Email, -1, 1) eq '>' ) { # 末尾文字が>
return 1
}
}
考察:index()とsubstr()組が正規表現に快勝
表3に示すとおり,index()
関数と1回のsubstr()
関数のほうが1.indexs
のコードは少し長くて読みにくいように思えます。
「速いは正義」
表3 文字列の先頭/
比較項目 | 秒間実行回数 | 速度比 |
---|---|---|
正規表現 | 2,068,966 | 1. |
index( ), | 3,658,537 | 1. |
文字列の比較にはlc()を使う
大文字と小文字が入り混じった文字列を比較するとき,lc()
関数で小文字に変換してから比較する方法と,i
修飾子を用いるか,
実例で挙げるのはメールアドレスです。ほとんどのメール関連ソフトウェアの実装は,Mailer-Daemon
やmailer-daemon
のような表記をよく見かけるでしょう。
次のコードは,mailer-daemon
i
修飾子を指定した正規表現,lc()
関数とで比べた例です。
lc-vs-i-modifier-vs-char-class.
my $Text = 'Mailer-Daemon';
sub regex1 {
# /正規表現/i(大文字/小文字の区別なし)
return 1 if $Text =~ /\Amailer-daemon\z/i;
}
sub regex2 {
# 文字クラスを使う正規表現(MとDだけ大文字を考慮)
return 1 if $Text =~ /\A[Mm]ailer-[Dd]aemon\z/;
}
sub lcfunc {
# lc()関数 (小文字にしてから比較)
return 1 if lc $Text eq 'mailer-daemon';
}
考察:lc()が正規表現に圧勝
表4に示すとおり,i
修飾子を使う正規表現と,lc()
関数で小文字に変換してから比較するlcfunc
サブルーチンは,
表4 文字列の比較の比較
比較項目 | 秒間実行回数 | 速度比 |
---|---|---|
/正規表現/i | 2,752,294 | 1. |
文字クラス | 2,857,143 | 1. |
lc( ) | 6,451,613 | 2. |
そして,index()
関数が正規表現よりも速かったのと同様,
- 注1)
- メールアドレスの
@
の左側です。
メタ文字を含む正規表現は読みやすいものを使う
.
や+
などの正規表現で特別な意味を持つメタ文字を,
次のコードでは,\.
とエスケープするか,[.]
と表現するか,/\Q...\E/
とメタ文字を無効にするエスケープシーケンスを使うか,
escape-vs-char-class-vs-qe.
my $Email = 'kijitora@neko. nyaan. jp';
sub bs { return 1 if $Email =~ /neko\.nyaan\.jp/ }
sub cc { return 1 if $Email =~ /neko[.]nyaan[.]jp/ }
sub qe { return 1 if $Email =~ /\Qneko. nyaan. jp\E/ }
考察:実力伯仲
表5に示すとおり,
表5 メタ文字のエスケープの比較
比較項目 | 秒間実行回数 | 速度比 |
---|---|---|
\エスケープ | 5,042,017 | 1. |
文字クラス | 5,217,391 | 1. |
\Q\E | 5,263,158 | 1. |
読みやすさには多少の個人差がありますが,\
でメタ文字をエスケープする表記を好まない筆者は,\Q
と\E
で挟むのが最も読みやすいと思います。