Strings of Life

PHP/Phalcon/MySQL/JavaScript/RegExp/Ruby/Perl/ActionScript

タグ:Perl

大文字と小文字の変換:
#!/usr/bin/env perl
use v5.12;
use warnings;

$_ = "I saw Barney with Fred.";

# 文字列全体を大文字・小文字にしたいときは、uc又はlcを使う
say uc; # I SAW BARNEY WITH FRED.
say lc; # i saw barney with fred

# 一部分だけを大文字・小文字にしたいときは、正規表現の置換で\U又は\Lを使う
s/(fred|barney)/\U$1/gi && say; # I saw BARNEY with FRED.
s/(fred|barney)/\L$1/gi && say; # I saw barney with fred.

# 一文字目だけを大文字・小文字にしたいときは、正規表現の置換で\u又は\lを使う
s/(fred|barney)/\u$1/gi && say; # I saw Barney with Fred.
split演算子(指定したパターンにしたがって文字列を分割):
#!/usr/bin/env perl
use v5.12;
use warnings;

my @fields = split /:/, "abc:def:g:h"; # ("abc", "def", "g", "h")
join関数(ばらばらの文字列を接着して一つの文字列にする):
#!/usr/bin/env perl
use v5.12;
use warnings;

$_ = join ":", 4, 6, 8, 10, 12;
say; # 4:6:8:10:12
m//をリストコンテキストで使う:
#!/usr/bin/env perl
use v5.12;
use warnings;

$_ = "Hello there, neighbor!";

# m//をリストコンテキストで使うと、マッチによって作られたキャプチャ変数のリストを返す
my($first, $second, $third) = /(\S+) (\S+), (\S+)/;
say "$second is my $third"; # there is my neighbor!
特殊変数$^I(指定した拡張子のバックアップを作成):
#!/usr/bin/env perl
use v5.12;
use warnings;

$^I = ".bak"; # 指定したファイル名.bak という名前のバックアップファイルを作成する

while (<>) {
    // 開いたファイルに対して何か処理をする
}

ドット(.)をあらゆる文字にマッチさせる/s:
#!/usr/bin/env perl
use v5.12;
use warnings;

$_ = "I saw Barney\ndown at the bowling alley\nwith Fred\nlast night.\n";
if (/Barney.*/) { # こっちはマッチしない
    say "matched";
}
if (/Barney.*/s) { # sを付けると、.は改行文字にもマッチするようになる
    print "That string mentions Fred after Barney!\n";
}
正規表現中に空白文字を含める/x:
#!/usr/bin/env perl
use v5.12;
use warnings;

$_ = "I saw Barney down at the bowling alley with Fred last night.";
if (m{
    Barney
    .* # 正規表現中のコメントは空白文字とみなされる
    Fred
    }x) {
    print "That string mentions Fred after Barney!\n";
}
括弧による文字列のキャプチャと、その制御:
#!/usr/bin/env perl
use v5.12;
use warnings;

my $names = 'Fred or Barney';

# ()で囲った範囲の文字列はキャプチャされ、後方参照や変数での再利用ができる
# (?: で始まる括弧では文字列はキャプチャされない
if ($names =~ m/(\w+) (?:and|or) (\w+)/) {
    say "I saw $1 and $2"; # ()でキャプチャした文字列は$1,$2...で呼び出せる
}

# Perl5.10以降では、
# (?<ラベル>で始まる括弧でキャプチャした文字列は、
# %+というハッシュに記録され、$+{ラベル}で呼び出せる
if ($names =~ m/(?<name1>\w+) (?:and|or) (?<name2>\w+)/) {
    say "I saw $+{name1} and $+{name2}";
}
名前付きキャプチャの後方参照:
#!/usr/bin/env perl
use v5.12;
use warnings;

my $names = 'Fred Flinstrone and Wilma Flinstrone';

# 名前付きキャプチャの後方参照には \g{ラベル} という記法を使う
if ($names =~ m/(?\w+) and \w+ \g{last_name}/) {
    say "I saw $+{last_name}";
}
自動マッチ変数(括弧でキャプチャしなくても自動でセットされる変数):
#!/usr/bin/env perl
use v5.12;
use warnings;

if ("Hello there, neighbor" =~ /\s\w+,/) { # 括弧でキャプチャしていない
    # &`: マッチした部分より前にあるもの
    # $&: マッチした部分
    # &': マッチした部分より後ろにあるもの
    # 自動マッチ変数3つを連結すると、元の文字列を復元できる
    say "That was ($`)($&)($')";
}
※自動マッチ変数は、次のマッチが実行されるまで値を保持する
※自動マッチ変数を使うと、プログラム全体の正規表現が少しだけ遅くなる

自動マッチ変数を一部だけ使用する/p(Perl 5.10以上):
#!/usr/bin/env perl
use v5.12;
use warnings;

if ("Hello there, neighbor" =~ /\s\w+,/p) {
    # ${^PREMATCH} : マッチした部分より前にあるもの
    # ${^MATCH} : マッチした部分
    # ${^POSTMATCH} : マッチした部分より後にあるもの
    say "That was (${^PREMATCH})(${^MATCH})(${^POSTMATCH})";
}

感想

Perlの正規表現は機能が豊富で、新しい機能もどんどん追加されている、という話は聞いていたけど、確かにその通りだった。Perlでは、初心者本でここまで詳細に正規表現を解説するのか! と驚いた。

あと、名前付きキャプチャについて、「PHPでも使えないかな」と思って試してみたら、使えた:

<?php
if (preg_match('/(?<hoge>hoge)/', 'hogefuga', $matched)) {
    echo  $matched['hoge'], PHP_EOL; // hoge
}
教訓:他言語を学ぶことで、メインで使ってる言語についても、より深く学ぶことができる。

Perlでは、use utf8;を宣言することで、ソースコードのコメント・文字列以外の部分に、UTF-8文字列を含めることができる。 UTF-8文字列には、通常プログラムを書く際に用いるASCII文字列(いわゆる半角英数字+半角記号)に加え、日本語の文字列や、絵文字まで含まれる。 以下のPerlプログラムは、正常に動作する。
#!/usr/bin/perl
use v5.12;
use warnings;
use utf8;
use Encode;

my $neko = 'cat';
my $猫 = 'cat';
$🐱 = 'cat';
# my $🐱 = 'cat'; だとCan't use global $🐱 in "my" というエラーになる

say $neko;
say $猫;
say encode('utf-8', $🐱);
# say $🐱; だと Wide character in print というエラーになる
ちなみに、PHPの場合:
<?php
$🐱 = 'cat';
echo $🐱, PHP_EOL;
PHPだと、設定でUTF-8を有効にしていれば、特に問題なく動く。
Perlの方は、バグなのか仕様なのか分からないけど、絵文字を使用すると予測不能な動きをする。

後方参照(キャプチャした部分文字列を正規表現パターンの中で再利用する):
#!/usr/bin/env perl
use v5.12;
use warnings;

# 同じ文字列が連続するパターンがあれば「matched」、なければ「not matched」を表示する
sub is_matched {
    my $_ = shift;
    if (/([0-9][0-9])\1/) { # 2組の数字が連続しているパターンにマッチ(組み合わせは何でもよい)
        say "matched";
    } else {
        say "not matched";
    }
}

is_matched "121"; # not matched
is_matched "3434"; # matched
is_matched "555555"; # matched
Perlの文字クラスショートカットがマッチするのは、ASCII文字だけではない
#!/usr/bin/env perl
use v5.12;
use warnings;
use utf8;

# my $_ = '一'; # 漢字の「1」 ← こっちはマッチしない
my $_ = '1'; # 全角数字の「1」

if (/\d/) { # 数字のショートカット
    say "matched \\d"; # "matched"!!!
}

if (/[0-9]/) { # こっちにはマッチしない
    say "matched [0-9]";
}

  • Perlでは、キーと値のペアを格納したデータ構造のことをハッシュ(hash)と呼ぶ
  • ハッシュのキーはユニークな文字列である
  • Perlのハッシュは、要素の順序を保存しない
keys関数とvalues関数(全てのキー/値を取り出す):
#!/usr/bin/env perl
use v5.12;
use warnings;

my %hash = ('a' => 1, 'b' => 2, 'c' => 3);
my @k = keys %hash;
my @v = values %hash;

say "@k"; # a b c (順不同)
say "@v"; # 1 2 3 (順不同)
each関数(ハッシュ全体に繰り返しを行う):
#!/usr/bin/env perl
use v5.12;
use warnings;

my %hash = ('a' => 1, 'b' => 2, 'c' => 3);

while ((my $key, my $value) = each %hash) {
	say "$key => $value";
}
exists関数(あるキーがハッシュの中に存在するか確認):
#!/usr/bin/env perl
use v5.12;
use warnings;

my %books = ('hoge' => 'fuga');

if (exists $books{'hoge'}) {
    say $books{'hoge'};
}
delete関数(ハッシュから指定されたキーを削除):
#!/usr/bin/env perl
use v5.12;
use warnings;

my %hash = ('key' => 'val');
delete $hash{'key'};

if (exists $hash{'key'}) {
    say 'not deleted';
} else {
    say 'deleted';
}

このページのトップヘ