Perlの文字列処理の基礎



  1. Perl



  2. here

Perlの文字列処理についての解説です。Perlはテキスト処理が得意な言語です。文字列の出力、ヒアドキュメント、検索、置換、書式指定などをわかりやすく解説します。また日本語を扱うための方法についても解説します。

文字列の基礎

まずは最初に文字列の基礎を解説します。

文字列の作り方

最初に文字列を作成してみましょう。文字列を作成するにはシングルクォートあるいはダブルクォートを使用します。

# シングルクォートで文字列を作成
my $str1 = 'Hello';

# ダブルクォートで文字列を作成
my $str2 = "Hello";
ダブルクォートで囲った場合

文字列を作成するときはシングルクォートで囲むか、ダブルクォートで囲みますが、ダブルクォート演算子で囲った場合は、エスケープシーケンス変数展開を使うことできます。

  • エスケープシーケンスが使える
  • 変数展開が使える
エスケープシーケンス

エスケープシーケンスとは、ダブルクォートで囲った文字列で使える特殊文字のことです。

たとえば改行「\n」タブ「\t」を使うことができます。改行やタブは目には見えない文字ですが、エスケープシーケンスを使って表現することができます。

# 改行
my $str1 = "Hello\n";

# タブ文字
my $str2 = "Cat\tDog";

よく使うエスケープシーケンスは改行とタブなので、この二つは必ず覚えておきましょう。

「\」という記号はエスケープのために利用されます。「\」という記号自体を表現したい場合は「\\」とします。

# 「\」を表現 - 「\\」とする
my $str1 = "Hello \\ World"

シングルクォートやダブルクォートを使いたい場合

シングルクォートで囲った文字列の中で、シングルクォートを使いたい場合や、ダブルクォートで囲った文字列の中で、ダブルクォートを使いたい場合も、エスケープシーケンスを使います。

# シングルクォートを使う
my $str1 = 'Kimoto\'s cat';

# ダブルクォートを使う
my $str2 = "Can't open \"foo.txt\".";
変数展開

変数展開とは、変数をダブルクォートの中で囲まれた文字列の中で使用できる機能のことです。変数に含まれている文字列が展開されます。

my $str1 = "Dog";

# 変数展開 - 「I have Dog」と変数が展開される
my $str2 = "I have $str1";

変数展開はPerlのとても便利な機能なので、どんどん使いましょう。

変数展開が失敗する場合

変数展開を使っていると、ときどき変数展開が失敗して、コンパイルエラーになることがあると思います。それは、後ろに続く文字列も変数の一部だと解釈されてしまう場合です。

たとえば、変数の後ろに「アンダーバー」が続く場合や「日本語」が続く場合などです。

my $str1 = "Dog";

# $str1を展開したいが変数名が「$str1_Foo」と解釈されてしまう
my $str2 = "I have $str1_Foo";

# $str1を展開したいが変数名が「$str1あいうえお」と解釈されてしまう
my $str3 = "I have $str1あいうえお";

このような場合は変数名を明示するために「${変数名}」という記法を使うことができます。

${変数名}

先ほどの例であれば、次のように書くとうまくいきます。

my $str1 = "Dog";

my $str2 = "I have ${str1}_Foo";

my $str3 = "I have ${str1}あいうえお";
文字列の連結

次に文字列を連結してみましょう。文字列を連結するには文字列連結演算子「.」を使用します。

my $str1 = 'I have a' . 'pen';

上記の例はクォートで囲まれた文字列を連結していますが、変数を使うこともできます。

my $str3 = $str1 . $str2;

文字列連結演算子は代入演算子と組み合わせて次のように使うこともできます。

$str1 .= $str2;

これは次の書き方と同じになります。「$str1」の後ろに「$str2」を連結して「$str1」に代入するという意味です。よく利用する書き方なので覚えておきましょう。

$str1 = $str1 . $str2;
文字列の出力

文字列を画面に出力するにはprint関数を使用します。

my $message = 'Hello';
print $message;

文字列処理の小技

ときどき使われる文字列処理の小技をいくつか紹介しておきます。

文字列リスト演算子

Perlでは、文字列のリストを簡単に作るための文字列リスト演算子があります。

文字列リスト演算子を使うと、クォートとカンマを省略して文字列のリストを作成することができます。

# 通常の方法
my @strs1 = ('Cat', 'Dog', 'Mouse');

# 文字列リスト演算子を使う方法
my @strs2 = qw(Cat Dog Mouse);

この演算子が特に便利なのは、利用したい単語が、テキストファイルに何行も続いている場合です。

Cat
Dog
Mouse

文字列リスト演算子を使うとコピペするだけで、Perlスクリプトの中で使うことができます。

my @strs = qw(
  Cat
  Dog
  Mouse
);

文字列リスト演算子の詳しい解説は以下を参考にしてください。

改行を取り除く

テキストファイルを読み込んだ時に末尾の改行を取り除きたい場合があります。

Perlでは改行を取り除くためのchomp関数があるのですが、この関数は環境依存です。

Windows系OSで実行した場合はWindowsの改行である「CR+LF」を取り除き、Unix/Linux/Macで実行した場合は「LF」を取り除きます。

確実に改行を取り除くためには、以下の正規表現を使うのがよいでしょう。

$str =~ s/\x0D?\x0A?$//;

正規表現についてはこの後で簡単に解説します。

指定した文字で配列の要素を連結する

文字列処理ではCSVデータ(カンマで区切られたデータ)を作成したり、TSVデータ(タブで区切られたデータ)を作成することが頻繁にあります。

このような場合は、配列にデータを準備して、それを特定の文字(カンマやタブ)で連結するということを行います。

join関数を使うとこれが簡単にできます。

my @animals = ('Cat', 'Dog', 'Mouse');

# CSVデータ「Cat,Dog,Mouse」を作成
my $csv = join(',', @animals);

# TSVデータ「Cat  Dog  Mouse」を作成
my $tsv = join("\t", @animals);

join関数については以下の記事を参考にしてください。

指定した文字で文字列を分割する

上記では、CSVデータとTSVデータの作成方法を解説しましたが、その反対に、CSVデータやTSVデータを配列にしたい場合があると思います。

このような場合はsplit関数を使用すると、特定の文字を区切り文字に指定して配列を作成することができます。

my $csv = 'Cat,Dog,Mouse';

# カンマを指定して配列を作成。('Cat', 'Dog', 'Mouse')という配列になる
my @animals = split(/,/, $csv);

TSVデータの場合のサンプルも載せておきます。

my $tsv = "Cat\tDog\tMouse";
my @animals = split(/\t/, $csv);

split関数の第一引数には、正規表現が直接指定できます。たとえば「一つ以上の空白」という正規表現を使うことができます。

# ひとつ以上の空白で区切られたデータを配列にする
my $data = "Cat    Dog  Mouse";
my @animals = split(/ +/, $csv);

正規表現については、この後に簡単に解説します。

split関数の詳しい解説については以下の記事をご覧ください。

文字列の長さを取得する

文字列の長さを取得するにはlength関数を使用します。

# 文字列の長さを取得
my $length = length "ABCDE";

length関数の詳細な解説については以下をご覧ください。

文字列の書式指定

小数点の桁数の指定や、左を0で埋めるような書式指定を行いたい場合はsprintf関数を利用できます。

# 3桁で左を0埋め「Code is 013」となる
my $str1 = sprintf("Code is %03d", 13);

# 小数点第3位まで表示「Number is 0.146」となる
my $str2 = sprintf("Number is %.3f", 0.145677);

書式指定の詳細な解説については以下をご覧ください。

ヒアドキュメント

Perlには複数行の文字列を簡単に作成するためのヒアドキュメントという構文があります。

# ヒアドキュメント
my $message = <<EOS;
If you want to write
multiple lines,
you can use here document
EOS

ヒアドキュメントの構文の説明をします。ヒアドキュメントは「<<」という記号で始まります。その語に文字列の末尾を指定する文字列を指定します。

これは任意の文字列でよいのですが、僕は「End of String」の略で「EOS」という記号を使っています。

この後にセミコロン「;」を書くことに注意してください。

そしてこの次の行から文字列を開始します。

文字列を終わりたいときは、最後の行に「EOS」と記述します。EOSの前には、空白を置くことができないので注意してください。

もし、インデントされてい位置からヒアドキュメントを書きたい場合は次のように書きます。

        # ヒアドキュメント
        my $message = <<EOS;
If you want to write
multiple lines,
you can use here document
EOS
ダブルクォートあるいはシングルクォートで囲った文字列と同じ意味にする

ヒアドキュメントを次のように書いた場合は、ダブルクォートで囲った場合と同じで、エスケープシーケンスと特殊文字を使うことができます。

my $message = <<EOS;
...
EOS

あるいは

my $message = <<"EOS";
...
EOS

下の例は、EOSをダブルクォートで囲っていることに注意してください。ダブルクォートで囲まない場合はデフォルト、ダブルクォートで囲ったのと同じ意味になります。

シングルクォートで囲った場合と同じようにするには次のように書きます。

my $message = <<'EOS';
...
EOS

ヒアドキュメントの詳しい解説については以下の記事を参考にしてください。

正規表現

Perlには文字列の検索と置換を行うための正規表現という機能が言語に組み込まれています。

パターンマッチを使って検索を行う

たとえば、文字列の中に「dog」という文字列が含まれていることを知りたい場合は次のように書けます。

my $message = "It is my dog\n";

if ($message =~ /dog/) {
  ...
}

=~はパターンマッチ演算子と呼ばれ正規表現のパターンマッチを実行するための演算子です。

正規表現は「//」という記号で囲む必要があります。

置換を行う

正規表現を使うと置換を行うこともできます。「dog」を「cat」に書き換えた場合は次のように書きます。

my $message = "It is my dog\n";

$message =~ s/dog/cat/;

「s/置換する文字列/置換後の文字列/」という構文で置換を行うことができます。

正規表現の詳しい解説については以下の記事をご覧ください。

Perlで正しく日本語を扱う

ここまで文字列のサンプルはすべて英語でした。でも実際の文字列処理は、日本語を扱うことが大半だと思います。

初級者の方は、Perlで日本語処理を正しく行うにはどうしたらいいのかということを感じると思います。

たとえば、length関数や正規表現は、内部的な文字列に変換してから実行しないと、正しい結果を返しません。

「日本語で正規表現がうまく動かないなぁ」というときは、Perlで日本語を正しく扱えていない可能性があります。

でも少しのことを覚えれば、Perlの文字列処理は決して難しくありませんので安心してください。

Perlで正しく日本語処理を行うためには、次の三つのことを覚えましょう。

  1. ソースコードUTF-8で記述し、utf8プラグマを指定する。
  2. 外部から受け取った文字列は必ずデコードする
  3. 外部へ出力する文字列は、エンコードする
ソースコードUTF-8で記述し、utf8プラグマを指定する。

ソースコードWindowsでも、Macでも、LinuxでもUTF-8で保存しましょう。そしてutf8プラグマを指定しましょう。

# utf8プラグマを指定
use utf8;

my $message = 'こんにちは';

まずこれを約束事として覚えてください。

これでソースコードに書かれた文字列に対して、length関数や正規表現が正しく動くようになります。

# utf8プラグマを指定
use utf8;

my $message = 'こんにちは';

# length関数が正しく動く。5が返る。
my $length = length $message;

# 正規表現が正しく動く
if ($message =~ /にち/) {
  ...;
}

utf8プラグマは、ソースコード内に書かれた「UTF-8バイト文字列」を「内部文字列」に変換します。

内部文字列に変換することによって、正しく文字列処理を行うことができるようになります。

「バイト文字列」と「内部文字列」についてはこの後で解説します。

外部から受け取った文字列は必ずデコードする

デコードというのは「特定の文字コードで書かれた文字列」を「Perlの内部的な文字列」に変換することを言います。

Perlの内部的な文字列」のことを内部文字列と便宜的に呼ぶことにし、「特定の文字コードで書かれた文字列」のことをバイト文字列と呼ぶことにします。

たとえば「UTF-8」という文字コードで書かれている文字列を「UTF-8バイト文字列」と呼び、「EUC-jp」という文字コードで書かれている文字列を「EUC-jpバイト文字列」と呼びます。

もしJavaで文字列の扱いを知っているなら「文字列ストリーム」と「バイトストリーム」という対応で考えるとわかりやすいです。

デコードを行うためにはEncodeモジュールのdecode関数を使用します。

Windowsで「cp932」という文字コードを書かれたファイルを読み込む

Windowsで「cp932」という文字コードを書かれたファイルを読み込むサンプルを書いてみます。

# decode.pl
use strict;
use warnings;
use utf8;
use Encode 'decode';

# ファイルの読み込み
while (my $line = <>) {
  $line = decode('cp932', $line);
  
  if ($line =~ /田中/) {
    print "OK";
  }
}

ファイル「name.txt」のサンプルは以下のようになっています。cp932で保存してください。

田中
鈴木
山田

次のようにコマンドを実行すると一度だけOKと表示され正規表現が正しく動くことがわかります。

perl decode.pl name.txt

Windowsコマンドライン引数の文字列を読み込む

外部から受け取った文字列というのは、ファイルだけではなく、コマンドライン引数や環境変数など、プログラムの外から読み込まれるものはすべて外部です。

ですから、コマンドライン引数で日本語を受け取った場合も、必ずデコードします。

my $name = $ARGV[0];
$name = decode('cp932', $name);
外部へ出力する文字列は、エンコードする

外部から受け取った文字列はデコードするのに対し、外部へ出力する文字は必ずエンコードします。

エンコードとは「内部文字列」を「バイト文字列」に変換することを言います。

エンコードを行うにはEncodeモジュールのencode関数を使用します。

たとえば、UTF-8で出力したい場合は次のように書きます。

use utf8;
use Encode 'encode';

my $message = 'あいうえお';

# 出力する前にエンコード
print encode('UTF-8', $message);

ファイル名を指定する場合

もうひとつ覚えておいてほしいのは、open関数などでファイル名を指定する場合は、OSで利用されている文字コードエンコードする必要があります。

日本語の名前を持つファイルを開くには次のようにします。エラーメッセージに日本語ファイル名を持つ場合は、その部分もエンコードしましょう。

use utf8;
use Encode 'encode';

my $file = 'テスト.txt';

# 日本語名のファイルをopenする
open my $fh, '<', encode('cp932', $file)
  or die encode('cp932', "Can't open file \"$file\": $!");

これは少し面倒だと感じると思います。よく使う場合は、サブルーチンやモジュール化しておくのも、楽にする方法のひとつかなと思います。

Perlで日本語を扱う詳細な解説についてはEncodeモジュールの解説で行っていますので、以下の記事をご覧ください。

文字列にかかわる演算子

文字列処理のために時々使う演算をここに掲載しておきます。

文字列処理のための関数

文字列処理のためにときどき使う関数を掲載しておきます。

substr関数 指定した位置の文字を抽出,置換する
index関数 文字列を検索する
rindex関数 文字列を末尾から検索する
reverse関数 文字列の順序を反転させる
ucfirst関数 文字列の先頭の文字を大文字に変換する
lcfirst関数 文字列の先頭の文字を小文字に変換する
uc関数 小文字を大文字に変換する
lc関数 大文字を小文字に変換する
chr関数 数値をASCIIコード対応する文字に変換する
ord関数 ASCIIコードの文字を内部表現である数値に変換する

まとめ

これだけ覚えておけば、日本語を含んだ文字列処理の9割くらいは行えるようになるんじゃないかと思います。