tr/// 用オプション /c

昨日勉強したtr/// 演算子用のオプションを勉強します。まずは「/c」。c は complement (補集合)の略です。「補集合」とは、数学の集合に出てくる概念で、簡単に言えば「ある集合に含まれないモノの集合」を意味します*1
tr/// 演算子には「変換する文字のリスト」を渡すわけですが、「/c」オプションを指定すると意味が反転して、このリストに「含まれない」文字が変換の対象となります。補集合が変換の対象になるわけですね。(^_^)
このオプションを使うと、対象となった文字は全て置換リストの最後の文字に変換されます。
訂正:このオプションを使うと、検索リストには「リストに含まれない文字」(補集合)が \0 (NULL:文字コード 0)から順番に並べられた事になるようです。詳しくはH.Iさんのコメントをご覧ください。(ツッコミありがとうございます♪ > H.I さん)

use strict;
 
my $text = "There's more than one way to do it.";
print "$text\n";
 
$text =~ tr/oe/ /c;
print "$text\n";

例えばこの場合、「o」「e」以外の文字が全て空白に置き換えられます。
実行結果は以下の通りです。

There's more than one way to do it.
  e e    o e      o e      o  o    

検索リストに含まれていなければ「どんな文字でも」置換されてしまいます。上の例でも「'」(アポストロフィ)や「.」(ピリオド)も置換の対象になっていますね。
もちろん、日本語だって置換されてしまいます。

use strict;
 
my $text = "日本語のtextです。";
print "$text\n";
 
$text =~ tr/a-z/ /c;
print "$text\n";

実行結果は以下のようになりました。

日本語のtextです。
        text      

上の実行結果は、ソースファイルを「Shift-JIS」で保存して実行した場合です。日本語1文字がスペース2文字に置き換えられていますね。つまり、「日本語1文字が2文字」として扱われています。これは、日本語1文字が2バイトで表現される事によるものです。
そこで、Unicode ならどうかな? と「UTF-8」で保存して実行してみましたが、やはり日本語1文字がスペース2つに置き換えられてしまいます。「UTF-16」だとエラーになってしまいました。
ラクダ本を読んでみると、「utf8」プラグマを使えばいいようです。

use strict;
use utf8;
 
my $text = "日本語のtextです。";
print "$text\n";
 
$text =~ tr/a-z/ /c;
print "$text\n";

と書いて「UTF-8」で保存しました。DOS プロンプトでは UTF-8 の日本語は文字化けしてしまうので、テキストファイルに出力するように実行してみました。

D:\dev\perl>perl tr.pl > tr.txt
Wide character in print at tr.pl line 5.

なんだか警告がでていますね。(^_^;) でも出力自体は問題なくできました。UTF-8 を扱えるテキストエディタで開いてみると……

日本語のtextです。
    text   

きちんと、日本語が1文字として扱われていますね。(^_^)
警告の原因は日本語が含まれる文字列を print した事でしょうか。この Unicode の扱いについてはもうちょっと勉強が必要そうです。

*1:厳密な定義はWikipediaなどをご覧ください。