第9章 文字列とディレクトリの操作

9-1. コメント

プログラムを記述する際に、覚え書きという位置づけ等で、(プログラムには解釈されない形で)メモ情報を残しておきたいことがあります。
それを「コメント」と呼びます。
Perlでは、シャープ ( # ) から行末までを「コメント」として扱われ、プログラムの実行上無視されます。これを「コメントアウト」といいます。
1$data = 'I am a boy';
2 
3# データを記述
4print "$data\n";
上記のコードの場合、3行目がすべてコメントとして扱われます。
コメントは、文の途中に記述しても構いません。その場合は、# から、行末までがコメントになります。
1$date = 'I am a boy';
2 
3print "$data\n"; # データを記述
また、応用的な記述方法として、複数行をコメントアウトする場合には、次のように =pod から =cut を記述することで、その範囲がすべてコメント扱われます。
1=pod
2これを用いると、
3複数行に渡って
4コメント扱いされます
5=cut
6 
7$data = 'I am a boy';
8print "$data\n";
ただし、実は上記の方法は正規なやり方ではありません。 複数行に渡ってコメントアウトしたい場合でも、本当は # を用いて行数分コメントアウトするのが基本です。
=pod =cutについては、プログラミング中やテスト中などに、「一時的に」複数行をコメントアウトしたい場合などにはよく使われる手法です。
pagetop

9-2. ヒアドキュメント

複数行に渡る文字列を記述したり、データを代入するときに用いられるのが、ヒアドキュメントです。
ヒアドキュメントは、<< + [識別文字] から [識別文字] という書式で記述します。
たとえば、以下のような文を記述するケースがあったとします。
1print "How are you?\n";
2print "Fine, Thank you. Are you?\n";
3print "I am fine, too.\n";
行ごとに print文を書かないといけないので、少し面倒です。
これをヒアドキュメントを用いて書くと、次のようになります。
1print <<"EOM";
2How are you?
3Fine, Thank you. Are you?
4I am fine, too.
5EOM
便利な書き方ですね。
上記の場合 [識別文字] を「EOM」にしていますが、この文字は任意で構いません。 ただし、通常は大文字にします。
また、[識別文字]を囲むダブルクォーテーションを省略しても構いません。 その場合は同じ意味(文字列をダブルクォーテーションで囲む)になります。
1print <<EOM;
2How are you?
3Fine, Thank you. Are you?
4I am fine, too.
5EOM
さらに、[識別文字]をシングルクォーテーションで囲むと、囲まれた文字列については、変数展開がされなくなります。
文字列をシングルクォーテーションで囲んだ場合と同じ扱いになります。
1print <<'EOM';
2それはいくらですか?
3これは$100です。
4EOM
1>  それはいくらですか?
2>  これは$100です。
ヒアドキュメントは、print文だけではありません。 たとえば、次のようにスカラー変数への代入時などにも使用されます。
1$str = <<"EOM";
2それは何ですか?
3これは檸檬です。
4EOM
5 
6print $str;
1>  それは何ですか?
2>  これは檸檬です。
pagetop

9-3. 文字列の検索

文字列を検索する関数として、index関数と rindex関数があります。
1. index関数
構文
index ( 全体文字列, 検索文字列, 開始位置 )
index ( 全体文字列, 検索文字列 )
2. rindex関数
構文
rindex ( 全体文字列, 検索文字列, 開始位置 )
rindex ( 全体文字列, 検索文字列 )
コード例-1 コード例-1
01$str = "ABCDEF";
02$sub = "CD";
03 
04$find = index($str, $sub);
05 
06if ($find >= 0) {
07    $find++;
08    print "先頭から$find番目に見つかりました。\n";
09} else {
10    print "見つかりませんでした。\n";
11}
1>  先頭から3番目に見つかりました。
「コード例-1」では、最初に見つかった部分を表示させていますが、文字列の中で見つかるものすべてを表示させたい場合には、次の「コード例-2」のように記述します。
コード例-2 コード例-2
1$str = "apple orange banana";
2$sub = "a";
3 
4while ( ($find = index($str, $sub, $find) ) >= 0 ) {
5    $find++;
6    print "$find番目に発見。\n";
7}
1>  1番目に発見。
2>  9番目に発見。
3>  15番目に発見。
4>  17番目に発見。
5>  19番目に発見。
pagetop

9-4. 文字列の操作

文字列を操作する関数として、substr関数があります。文字列の取り出し、置き換え、削除、追加などを行うことができます。
構文
substr ( 全体文字列, 開始位置, 文字長 )
substr ( 全体文字列, 開始位置 )
文字列の取り出し 文字列の取り出し
1$word = "milktea";
2$str = substr($word, 0, 4);
3print "$str\n";
substr関数を左辺に置き、代入の対象とすることで、部分文字列の置き換えや追加などが可能となります。
文字列の置き換え 文字列の置き換え
1$word = "milktea";
2substr($word, 0, 4) = "lemon";
3print "$word\n";
1>  lemontea
文字列の削除(先頭から削除) 文字列の削除(先頭から削除)
1$word = "milktea";
2substr($word, 0, 4) = "";
3print "$word\n";
文字列の削除(末尾から削除) 文字列の削除(末尾から削除)
1$word = "milktea";
2substr($word, -3, 3) = "";
3print "$word\n";
pagetop

9-5. 文字列の整形

文字列を整形する関数として、printf関数 と sprintf関数 があります。文字列や数値を指定の書式に変換を行います。
printf関数 は整形した値をファイルハンドルに出力し、sprintf関数 は整形した値を返します。
関数 構文 内容
printf関数 printf ( 書式, リスト ) 「リスト」の値を「書式」に整形して、ファイルハンドルに出力する。
sprintf関数 sprintf ( 書式, リスト ) 「リスト」の値を「書式」に整形して、その値を返す。
構文における書式のコードは次のとおりです。
コード 意味
%c 文字
%s 文字列
%d 10進整数
%e 浮動小数点数(指数形式)
%f 浮動小数点数(固定小数点形式)
%e 浮動小数点数(コンパクト形式)
%o 8進整数
%x 16進整数
%X 16進整数(大文字使用)
コード例 コード例
1$hour = 5;
2$min = 12;
3$sec = 6;
4 
5$time = sprintf("%02d:%02d:%02d", $hour,$min,$sec);
6print "$time\n";
1>  05:12:06
pagetop

9-6. ディレクトリの読み取り

ディレクトリの中身を読み取るための関数として、opendir関数、readdir関数、closedir関数があります。
ファイルをオープンするときにopen関数でファイルハンドルを関連付けますが、それと同じように、ディレクトリをオープンするときには、ディレクトリハンドルを指定して関連付けを行います。
opendir関数でディレクトリをオープンする場合は、読み出し専用であることに注意します。 open関数のように、ファイルの追加や書き込みなどを行うことはできません。
関数 構文 内容
opendir opendir ( ディレクトリハンドル, ディレクトリ ) 「ディレクトリ」で与えられたディレクトリ名をオープンする。成功すれば真を返す。
readdir readdir ( ディレクトリハンドル ) opendirによってオープンされた「ディレクトリハンドル」から、ディレクトリエントリを読む。
closedir closedir ( ディレクトリハンドル ) opendirによってオープンされたディレクトリを閉じる。
現在のディレクトリの中身を読み取って表示させる例です。
コード例-1 コード例-1
01use strict;
02 
03opendir(DIR, ".");
04my @dir = readdir(DIR);
05closedir(DIR);
06 
07foreach (@dir) {
08    next if (/^\.{1,2}$/); # . と .. を排除
09 
10    print "$_\n";
11}
上記のコード例では、ディレクトリの中身を一気に読み込んで配列@dirに代入する例ですが、1行ずつ読み出す場合には、次のように記述することもできます。
ディレクトリの中に多くのファイルがあることが予想される場合に有効です。
コード例-2 コード例-2
1use strict;
2 
3opendir(DIR, ".");
4while( my $dir = readdir(DIR) ) {
5    next if ($dir =~ /^\.{1,2}$/); # . と .. を排除
6 
7    print "$dir\n";
8}
9closedir(DIR);
ディレクトリ内の情報を読み取るものとして、他に次のような関数/演算子も用意されています。
関数/演算子 構文 内容
glob関数 glob( 式 ) 「式」はワイルドカードを使ったファイル名。省略時は$_になる。
引数の「式」にマッチしたファイル名のリストを返す。
ダイヤモンド演算子 < 式 > 同上
glob関数とダイヤモンド演算子は使い方は同じです。 元々ダイヤモンド演算子のみであったものが、Perl5からその関数として、glob関数が登場したということのようです。
コード例-3 コード例-3
1use strict;
2 
3# ディレクトリ内の情報を読み出す
4my @dir = glob('*');
5print join("\n", @dir), "\n";
6 
7# ディレクトリ内の情報を読み出す
8my @dir = <*>;
9print join("\n", @dir), "\n";
上記のglob関数とダイヤモンド演算子の場合は、同じ結果が得られます。
ワイルドカードを使って、取得するファイルを指定することもできます。
コード例-4 コード例-4
1# ディレクトリ内の拡張子が.htmlのみのファイルを読み出す
2my @dir = glob('*.html');
3print join("\n", @dir), "\n";
4 
5# ディレクトリ内の拡張子が.htmlと.plを読み出す(複数の場合はスペースで区切る)
6my @dir = glob('*.html *.pl');
7print join("\n", @dir), "\n";
glob関数(ダイヤモンド演算子)の利用において、opendir関数との主な違いは、次のとおりです。
(1) 取得するリストで、ファイルがABC順に並ぶ。
(2) ワイルドカート ( * ) のみではドットファイルは得られない。
(3) 異なるディレクトリで取得する場合、リストはパス付きで得られる。
ドットファイルも取得したい場合には、次のようにします。
コード例-5 コード例-5
1use strict;
2 
3my @dir = glob('* .*'); # .* でドットファイルを得る
4print join("\n", @dir), "\n";
異なるディレクトリを読み出す場合のopendirとの比較です。
コード例-6 コード例-6
01use strict;
02 
03# glob関数で異なるディレクトリ読み出し
04my @dir = glob('./dir/*');
05print join("\n", @dir), "\n";
06 
07# opendir関数で異なるディレクトリ読み出し
08opendir(DIR,"./dir");
09my @dir = readdir(DIR);
10closedir(DIR);
11 
12for (@dir) {
13    next if (/^\./);
14    print "$_\n";
15}
1>  ./dir/abc.html
2>  abc.html
出力結果の1行目は、glob関数の例で、「パス付き」で取得されます。
出力結果の2行目は、opendir関数の例で、ファイル名のみが取得されます。
pagetop

9-7. ディレクトリの生成と削除

ディレクトリを生成したり、削除するための関数として、mkdir関数とrmdir関数があります。
関数 構文 内容
mkdir mkdir ( ディレクトリ名, モード ) ディレクトリ「ディレクトリ名」を作成し、そのパーミッションを数値「モード」にする。 成功すれば 1 を返し、失敗すると 0 を返す。
rmdir rmdir ( ディレクトリ名 ) 「ディレクトリ名」に指定したディレクトリが空きならば、これを削除する。 成功すれば 1 を返し、失敗すると 0 を返す。 「ディレクトリ名」を省略すると、$_ が使われる。
ディレクトリの作成 ディレクトリの作成
1mkdir("./dir", 0755) or die;
ディレクトリの削除 ディレクトリの削除
1rmdir("./dir") or die;
mkdir関数の使用時に注意する点として、たとえばパーミッションを「777」のディレクトリを作成する場合、次のような一行を記述しても実際には777で生成されません(環境にもよりますが、大抵は755になります)。
1mkdir("./dir", 0777);
この場合には、その前にumask関数でumask値を0にすると、思い通りのパーミッションになります。
1umask(0);
2mkdir("./dir", 0777);
ちなみに、umask値とは、ファイル・ディレクトリの新規作成時に適用されるマスク値のことです。 通常は、「022」に設定されているため、mkdir関数で「777」で生成しようとしても、777 - 022 = 755 となってしまいます。
そこで、umask関数で意図的にこれを「000」に変更することで、777 - 000 = 777 にするということになります。
pagetop