エラー メッセージをわざと、あえて出して「うひゃひゃひゃひゃー!」と喜ぶコーナー、今日は「'!' allowed only after types %s」です。これは「%s型(複数)の後でしか!は使えません」という意味ですが、pack、unpack関数の引数において発生するエラーです。

Boxes to unpack (7734456122)
 pack、unpackというのは難しい関数です。16進数の文字列からバイナリー列(普通のデータ)に変換するのがpack、バイナリー列を16進数に変換するのがunpack。ものすごく汎用性の高い関数です。

Perlの組み込み関数 pack の翻訳 - perldoc.jp
perlpacktut - pack と unpack のチュートリアル - perldoc.jp

 汎用性が高い関数はトレードオフ的に難しく感じます。たとえば配列を操作するとき、なんでも出来る汎用関数spliceを使うよりは、push、pop、shift、unshiftを組み合わせて冗長に書いた方が分かりやすく感じます。でも、これは使ってるぼくの頭が悪いのかもしれません。
 pack/unpackに戻りますが、ありがちな例だとこういうのが考えられます。

#! /usr/local/bin/perl
#
# packExample.pl -- pack関数の使用例

use strict;
use warnings;
use utf8;
use 5.10.0;

my $hex = "30313233343536373839";
my $packed = pack 'H20', $hex;
say $packed;

 実行するとこうなります。

[sample]$ ./packExample.pl
0123456789
[sample]$

 "30313233343536373839"という文字列を、'H20'(ビッグ・エンディアンの16進数20桁)というテンプレートで解釈して、バイナリー化(パック)したものを、$packedという変数に入れて、表示しています。0x30は数字の0、0x31は数字の1ですから、01…という風に10桁の数字列に変換されます。

 では、エラーメッセージを出してみましょう。特に何の意味もなく、pack関数のテンプレートに!(感嘆符)を入れてみます。

#! /usr/local/bin/perl
#
# packExample2.pl -- pack関数の使用例(エラー)

use strict;
use warnings;
use utf8;
use 5.10.0;

my $hex = "30313233343536373839";
my $packed = pack 'H!20', $hex;
say $packed;

 再実行します。

[sample]$ ./packExample2.pl
'!' allowed only after types sSiIlLxXnNvV@. in pack at ./packExample2.pl line 11.
[sample]$

 早速出ましたね。
 最初に書いた「%s」というのは文字列(string)を示すプレースホルダーで、これがsSiIlLxXnNvV@という風に展開されています。

!は「sSiIlLxXnNvV@」というタイプの中でしか許されない

というエラーですね。
 では、これらの型はどういうもので、どういうときに使えばいいのでしょうか。以下の意味があります。

* sは符号付きのshort型変数、常に16ビット長
* Sは符号なしのshort型変数、常に16ビット長
* lは符号付きのlong型変数、常に32ビット長
* Lは符号なしのlong型変数、常に32ビット長

データ型の内部表現

 lは数字のイチではなくてアルファベット小文字のエル(longの略)ですから注意してください。
 これらはC言語のようにデータ型を厳密に指定しなければいけない言語での桁数を指定した整数型のデータ(バイナリー)を、Perlで生成できるテンプレートです。
 ところで、short型は16ビット、long型は32ビットというのは、32ビット時代の話で、最近出てきた64ビット型のマシンだと、同じshort、longでも長さが変わる場合があります。

32bitと64bitのサイズの違い(C言語):のぼメモ(仮):So-netブログ

 そこでsの代わりにs!、lの代わりにl!などと書くと、Perlがマシンの環境を読み取って、そのマシンのshort型、long型のデータを生成してくれます。これがビックリマークの使いどころです。


#! /usr/local/bin/perl
#
# unpackExample.pl -- unpackの例

use strict;
use warnings;
use utf8;
use 5.10.0;

my $bin = pack 'l', 1;
my $len = length $bin;
say "l", unpack 'H'.$len, $bin;

$bin = pack 'l!', 1;
$len = length $bin;
say "l!", unpack 'H'.$len, $bin;

 上記の例題は、1という数値を「l」と「l!」の2つのテンプレートでpackしたものを、16進数でunpackして表示するです。場合によってデータの長さが変わると思われるので、その長さをlength関数で取得してunpackのテンプレートを"H2"にしたり"H4"にしたりしています。
 今回はMacbook Pro Retinaで実行します。OSはEl Capitan 10.11.3です。最初にちゃんと64ビットOSか調べます。

[sample]$ uname -a
Darwin Chihiro-no-MacBook-Pro.local 15.3.0 Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64 x86_64
[sample]$

 この最後のX86_64というのが64ビット版だよという意味です。ではプログラムを実行します。

[sample]$ ./unpackExample.pl
l0100
l!01000000
[sample]$

 みごと「l」だと「0100」と表示されるが「l!」だと「01000000」と表示されます。こういうときに使えるのがpack、unpackの!で、適用外の型に使おうとすると、今日のお題のようなエラーになります。