vec

文字列の特定の範囲をビットで操作する

構文

解説

vec は、文字列 EXPR の中の要素位置 OFFSET から BITS の数だけのビット数を符号なし整数として取得またはセットします。

BITS は 1 から 32 (システムがサポートしていれば 64 まで) の範囲の 2 のべき乗でなければいけません。 つまり、BITS は 1, 2, 4, 8, 16, 32(, 64) のいずれかでなければいけません。 ここで言う「要素」とは BITS の数で表されるビット列 1 つを指します。 もし BITS が 8 なら、1 要素は 1 バイト (1 文字) を指します。 BITS が 16 なら、 1 要素は 2 バイト (2 文字) を指します。

例えば、先頭から 4 文字を超えて次の 1 文字を指したいなら、BITS は 8 で OFFSET は 4 です。 先頭から 4 文字を超えて次の 2 文字を指したいなら、BITS は 16 で OFFSET は 2 です。

一見、vec はバイナリデータを扱う関数のように見えますが、あくまでも文字列操作関数です。 文字列をビット指向で読み取ったり編集したりするための関数と考えると良いでしょう。

まずは文字列から特定の範囲のビット列を取得する例を見てみましょう。 次のコードは文字列 "Perl" から最初の 8 ビットを取り出しています。

my $bits = vec( 'Perl', 0, 8 );
print $bits, "\n";                     # 80 (10 進数の数値)
print sprintf( "%X", $bits ), "\n";    # "50" (16 進数の文字列)

文字列 "Perl" の最初の 8 ビットは "P" に相当します。 この文字は、バイナリデータ (ASCII コード) として見れば、16 進数なら 0x50 であり、10 進数なら 80 です。 このように、vec は、読み取りで使う場合、特定の範囲のビット列を整数値で返します。

vec は基本的に日本語のようなマルチバイト文字を扱うのは厳しいでしょう。 次のコードは文字列 "あいうえお" の最初の 32 ビットを読み取っています。 ただし、スクリプトの文字セットは UTF-8 で、utf8 モードではありません (use utf8; を使っていません)。

my $bits = vec( 'あいうえお', 0, 32 );
print $bits, "\n";                     # 3816915683 (10 進数の数値)
print sprintf( "%X", $bits ), "\n";    # "E38182E3" (16 進数の文字列)

前述の通り、BITS は 2 のべき乗でなければいけません。 UTF-8 の「ひらがな」の 1 文字は 3 バイトですので、3 の倍数を扱いたいところですが、それは叶いません。 そのため、ここでは BITS に 4 を指定しました。 得られた値は 32 ビット分の整数であり、冒頭 3 バイトは 16 進数文字列で "E38182" となります。 これは確かに「あ」を表しています。しかし、このように、マルチバイト文字を扱おうとすると、 文字の区切りが適切でなく、非常に不便なことが分かります。

utf8 モード (use utf8; が指定された環境) の場合、 vec は Perl 内部文字列を 1 バイト 1 文字に変換したうえで処理します。 つまり vec は ASCII 文字であれば utf8 モードであろうがなかろうが期待通りに処理します。 ところが、utf8 モードで日本語のようなマルチバイトの文字列を vec で扱おうとすると、vec は例外を発生しますので注意してください。

では次に書き込みについて見ていきましょう。 次のコードは、空の文字列がセットされた変数 $str に、数値を使って "Perl" と書き込みます。

my $str = '';
vec( $str, 0, 32 ) = 0x5065726C;
print $str, "\n";    # Perl

上記のコードに引き続いて、"Pe" (0x5065) と "rl" (0x726C) を後ろに追加してみましょう。 さらに引き続いて、"Perl" (0x5065726C) を後ろに追加します。

vec( $str, 2, 16 ) = 0x5065;        # Pe を追加
print $str, "\n";                   # PerlPe

vec( $str, 3, 16 ) = 0x726C;        # rl を追加
print $str, "\n";                   # PerlPerl

vec( $str, 2, 32 ) = 0x5065726C;    # Perl を追加
print $str, "\n";                   # PerlPerlPerl

OFFSETBITS の関係性に注目してください。 1 行目と 4 行目では、BITS は 16 であり、2 バイト分の文字列を追加しようとしています。 この場合、OFFSET は 2 バイトを単位とした位置を指定する必要がありますので、 2 と 3 が文字列最後の位置を指すことになります。

7 行目では BITS は 32 であり、4 バイト分の文字列を追加しようとしています。 この場合、OFFEST は 4 バイトを単位とした位置を指定する必要がありますので、 2 が文字列最後の位置を指すことになります。