perlform - Perl のフォーマット
Perl は、あなたが単純なレポートやチャートを作るのを助けてくれるような 機構を持っています。 これを容易にするために、Perl は出力するページを 見栄えよくするためのプログラムの作成を助けてくれます。 現在のページにおいて何行出力したとか、ページヘッダを出力するタイミングなどの ことを保持しつづけることができます。 キーワードは FORTRAN から借りました: format() が宣言のためのもので、 write() が実行のためのものです。 perlfunc 中のそれぞれのエントリを参照してください。 幸運にも、そのレイアウトは非常に読みやすく、 BASIC の PRINT USING 文のようなものです。 貧者のための nroff(1) と考えてください。
パッケージやサブルーチンと同様に、フォーマットも実行されるのではなく 宣言されるものなので、プログラムの任意の場所に置くことができます (ただし、通常は全てまとめておくのが良いでしょう)。 これらは、Perl の他のすべての「型」とは分離された名前空間を持っています。 これはつまり、"Foo" という名前の関数を持っているとき、それは "Foo" と 名前が付けられているフォーマットとは違うものなのだということです。 しかしながら、与えられたファイルハンドルに結び付けられたフォーマットの デフォルトの名前は、そのファイルハンドルと同じ名前になります。 したがって、STDOUT のデフォルトのフォーマットの名前は "STDOUT" であり、 TEMP というファイルハンドルに対するデフォルトフォーマットの 名前は "TEMP" となります。 これらは同じもののように見えますが、そうではないのです。
出力レコードフォーマットは以下のように宣言されます:
format NAME =
FORMLIST
.
名前が省略された場合、フォーマット "STDOUT" が定義されます。 1 桁目に単一の "." を置くと、フォーマットを終了します。 FORMLIST は、それぞれが以下の 3 つのいずれかである行の並びから構成されます。
第 1 カラムに `#' が置かれることによって示されるコメント。
出力行のフォーマットを与える「ピクチャー」行。
直前のピクチャー行に値を押し込むための引数行。
ピクチャー行は、リテラルなテキストが混ざった出力フィールド定義を含みます。 これらの行はどのような変数展開も行われません。 フィールド定義は文字の集合で行われ、フィールドの開始と、希望する幅への 拡張を行います。 以下はフィールド定義のための文字の完全な集合です:
@ start of regular field
^ start of special field
< pad character for left justification
| pad character for centering
> pad character for right justification
# pad character for a right-justified numeric field
0 instead of first #: pad number with leading zeroes
. decimal point within a numeric field
... terminate a text field, show "..." as truncation evidence
@* variable width field for a multi-line value
^* variable width field for next line of a multi-line value
~ suppress line with all fields empty
~~ repeat line until all fields are exhausted
ピクチャー行の書くフィールドは "@" (at) か "^" (caret) で開始され、 それぞれ「通常」か「特殊」フィールドを呼び出すことを示します。 パッド文字の選択はフィールドがテキストか数値化によって決定されます。 チルダ演算子はフィールドの一部ではありません。 様々な可能性に冠する詳細について見ていきましょう。
フィールドの長さは、左寄せ、右寄せ、センタリングをそれぞれ指定する "<", ">", "|" といった文字で埋められた非数値フィールドで 与えられます。 通常のフィールドでは、値(最初の改行まで)が取られ、選択された行揃えと 余分な文字の切り詰めに従って表示されます。 テキストフィールドが "..." で終わっている場合、もし値が切り詰められると 3 つのドットが表示されます。 特殊テキストフィールドは基本的に複数行テキストブロック詰め込みに使われます; 詳細については "Using Fill Mode" を参照してください。
Example:
format STDOUT =
@<<<<<< @|||||| @>>>>>>
"left", "middle", "right"
.
Output:
left middle right
パッディング文字として "#" を使うと、数値フィールドを右詰めすることを 指定します。 オプションの "." は小数点の位置を定義します。 最初の "#" の代わりに "0" (zero) を使うと、フォーマットされた数値は もし必要であれば 0 でパッディングします。 もし結果の値がフィールドで指定された幅を超えた場合、 オーバーフローしたことを示すために "#" で埋められます。
Example:
format STDOUT =
@### @.### @##.### @### @### ^####
42, 3.1415, undef, 0, 10000, undef
.
Output:
42 3.142 0.000 0 ####
フィールド "@*" は複数行で切り詰められない値を表示するために使われます; これはそれ自身 1 行で表示されるべきです(しかし必要ではありません)。 最後の改行は切り落とされますが、その他の全ての文字はそのまま出力されます。
"@*" と同様、これは可変長幅フィールドです。 指定する値はスカラ値でなければなりません。 Perl はテキストの最初の行(最初の "\n" まで)をフィールドにいれ、それから 次にこの変数が参照されたときに次のテキストが表示されるように、文字列の 前の部分は切り落とされます。 変数は復元 されません 。
Example:
$text = "line 1\nline 2\nline 3";
format STDOUT =
Text: ^*
$text
~~ ^*
$text
.
Output:
Text: line 1
line 2
line 3
引き続くフォーマット行で指定された値は、ピクチャーフィールドと同じ順序に なります。 値を提供する式はカンマで分けられていなければなりません。 式は、行の処理が行われるより前にリストコンテキストで評価されます。 ですから、一つのリスト式が複数行の要素を生成することもできます。 この式はそれが中かっこで囲まれている場合には、2 行以上に 広げられることもできます。 その場合、開きかっこは一行目の最初のトークンでなければなりません。 ある式が小数部を持った数値として評価され、数値指定に対応するピクチャーは 出力に現れます(つまり、埋め込みの "." 以外の 複数の "#" を除く全ての ピクチャーです)。 そして、小数点のために使われるキャラクターは use locale
が有効ならカレントの LC_NUMERIC ロケールによって決定されます。 これはたとえば、実行時にドイツ語ロケールが指定されている環境の場合には、 デフォルトの "." ではなく "," が使われるということです。 詳しい情報は perllocale と "WARNINGS" を参照してください。
テキストフィールドでは、キャレットが詰め込みモードを有効にします。 指定する値は任意の式ではなく、テキスト文字列を保持しているスカラ変数の 名前でなくてはなりません。 Perl は次の位置のテキストをフィールドに出力してから 文字列の先頭をたたき落とす(chop)ので、次にその変数を参照したときには 残りの文字列を出力できるのです。 (これはつまり、その変数自身が write() の実行中に変更されてしまうと いうことであり、元には戻らないということです。) テキストの次の位置は粗い行分割アルゴリズムによって決定されます。 強制的に行分割を行うために復帰文字 (\r
) が使えます。 $:
という変数(English モジュールを使っていれば $FORMAT_LINE_BREAK_CHARACTERS)の内容を変更することによって、 そこで改行することのできる文字を設定できます。
通常は、同じスカラ変数に結び付けられた縦方向に積み重ねられたフィールドの 並びを使ってテキストのブロックを出力することができます。 最後のフィールドの終端に "..." を置くと、それは出力対象のテキストが 長すぎて全体を出力できないときに出力に付加されます。
キャレットを使ったフィールドは、全てのフィールドが空白の行を 生成することができます。 行の任意の場所に "~"(チルダ) を置くことによって空行を 抑制することができます。 チルダは出力では空白に変換されます。
行のどこかに 2 つの連続したチルダ文字 "~~" を置くと、その行は その行にある全てのフィールドが 出力されるまで(つまり未定義になるまで)くり返されます。 特殊(キャレット)テキストフィールドでは、これは遅かれ早かれ起こりますが、 変化のあるテキストフィールドを使う場合、指定する式は同じ値を毎回永遠に 返さないようにした方がよいでしょう! (これが起きる単純な例は shift(@f)
です。) このような行に通常の数値フィールドを使ってはいけません; なぜなら 決して空にならないからです。
ページの先頭の処理は、デフォルトではカレントのファイルハンドルに "_TOP" を付けた名前のフォーマットによって取り扱われます。 これは、各ページの先頭で呼び出されます。 "write" in perlfunc を参照してください。
例:
# a report on the /etc/passwd file
format STDOUT_TOP =
Passwd File
Name Login Office Uid Gid Home
------------------------------------------------------------------
.
format STDOUT =
@<<<<<<<<<<<<<<<<<< @||||||| @<<<<<<@>>>> @>>>> @<<<<<<<<<<<<<<<<<
$name, $login, $office,$uid,$gid, $home
.
# a report from a bug report form
format STDOUT_TOP =
Bug Reports
@<<<<<<<<<<<<<<<<<<<<<<< @||| @>>>>>>>>>>>>>>>>>>>>>>>
$system, $%, $date
------------------------------------------------------------------
.
format STDOUT =
Subject: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$subject
Index: @<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$index, $description
Priority: @<<<<<<<<<< Date: @<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$priority, $date, $description
From: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$from, $description
Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$programmer, $description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<...
$description
.
同じ出力チャネルにおいて print() と write() を混ぜて使うことは可能ですが、 $-
($FORMAT_LINES_LEFT
) を自分で扱う必要があるでしょう。
カレントのフォーマット名は $~
($FORMAT_NAME
) という変数に格納され、 カレントの先頭のフォーマットの名前は $^
($FORMAT_TOP_NAME
) に 格納されています。 カレントの出力ページ数は $%
($FORMAT_PAGE_NUMBER
) にあり、 ページ当たりの行数は $=
($FORMAT_LINES_PER_PAGE
) にあります。 そのハンドルが出力時に自動フラッシュするかどうかは $|
($OUTPUT_AUTOFLUSH
) にあります。 各ページの先頭の前(1 ページ目を除く)に出力される文字列は $^L
($FORMAT_FORMFEED
) に格納されています。 これらの変数はファイルハンドルごとに設定されるものなので、 異なるファイルハンドルに効果を及ぼすには、そのファイルハンドルへ select() を行う必要があります:
select((select(OUTF),
$~ = "My_Other_Format",
$^ = "My_Top_Format"
)[0]);
ちょっと見づらいですね。 でもこれは一般的なイディオムなんです。 ですからこれを見たときにもそんなに驚かないでください。 少なくとも、以前のファイルハンドルを保持するために一時変数を 使うことができます(一般的にはこちらのほうが良いやりかたです。 なぜなら、読みやすくなるばかりでなく、式の途中でデバッガの シングルステップが使えるからです):
$ofh = select(OUTF);
$~ = "My_Other_Format";
$^ = "My_Top_Format";
select($ofh);
English モジュールを使っていれば、変数名もわかりやすいものにできます:
use English '-no_match_vars';
$ofh = select(OUTF);
$FORMAT_NAME = "My_Other_Format";
$FORMAT_TOP_NAME = "My_Top_Format";
select($ofh);
しかし、これでもまだ妙な select() があります。 FileHandle モジュールを使いましょう。 そうすれば、これらの特殊変数の代わりに小文字のメソッド名を 使ってアクセスできるようになります。
use FileHandle;
format_name OUTF "My_Other_Format";
format_top_name OUTF "My_Top_Format";
ずいぶん良くなりましたね!
値の行には任意の式(^ フィールドではなく @ フィールドで)を含めることが できるので、sprintf() や自分で作ったようなその他の関数を使って より整った処理を行うことができます。 例を挙げてみましょう:
format Ident =
@<<<<<<<<<<<<<<<
&commify($n)
.
本当のキャレットやアットマークをフィールドに挿入するは以下のようにします:
format Ident =
I have an @ here.
"@"
.
行全体を中央寄せするには以下のようにします:
format Ident =
@|||||||||||||||||||||||||||||||||||||||||||||||
"Some text line"
.
「ページの幅に関係なくフィールドをページの右端に浮かせておく」ような 組み込みの方法はありません。 フィールドがどこに置かれるのかを指定する必要があります。 本当にお薦めはできないのですが、カレントのカラムの数に基づいて その場でフォーマットを生成することが可能なので、 そうしてから eval() するという手が使えます:
$format = "format STDOUT = \n"
. '^' . '<' x $cols . "\n"
. '$entry' . "\n"
. "\t^" . "<" x ($cols-8) . "~~\n"
. '$entry' . "\n"
. ".\n";
print $format if $Debugging;
eval $format;
die $@ if $@;
これは以下のようなフォーマットを生成するでしょう:
format STDOUT =
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$entry
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
$entry
.
以下の例は、fmt(1) のようなことをするちょっとしたプログラムです:
format =
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~
$_
.
$/ = '';
while (<>) {
s/\s*\n\s*/ /g;
write;
}
$FORMAT_TOP_NAME はカレントのヘッダフォーマットの名前を 保持していているのですが、フッタに対して同じことを自動的に行う適切な 仕掛けはありません。 評価を行ってみるまではフォーマットがどれくらい 大きなものになるのか知ることができないということが、大きな問題です。 これは TODO リストにあります。
一つの戦略: あなたが固定サイズのフッタを使うのであれば、write() する前に $FORMAT_LINES_LEFT をチェックすればフッタを適切に表示できます。
別の戦略: open(MYSELF, "|-")
("open FILEHANDLE" in perlfunc を参照)を使って 自分自身に対するパイプをオープンして常に STDOUT ではなく MYSELF に write() するようにし、子プロセスではその STDIN からの入力を、ヘッダと フッタを再構成するために処理します。 これは非常にお手軽というわけではありませんが、やればできます。
フォーマット機構に対する低水準アクセスのために、formline() を使ったり、 直接 $^A
($ACCUMULATOR 変数) にアクセスすることができます。
例えば:
$str = formline <<'END', 1,2,3;
@<<< @||| @>>>
END
print "Wow, I just stored `$^A' in the accumulator!\n";
また、printf() に対する sprintf() と同じことを write() に対して行う サブルーチン swrite() を作成するために以下のようにできます:
use Carp;
sub swrite {
croak "usage: swrite PICTURE ARGS" unless @_;
my $format = shift;
$^A = "";
formline($format,@_);
return $^A;
}
$string = swrite(<<'END', 1, 2, 3);
Check me out
@<<< @||| @>>>
END
print $string;
フォーマットを終了する独立したドットは、間違って設定されている インターネットメーラー(そして経験によれば、そういった間違った設定は 通例のものであって、例外ではありません)を通して渡されるメールメッセージを 早まって終わらせてしまう可能性もあります。 ですから、メイルを通じてフォーマットコードを送るときには、 フォーマットを終端するドットがレフトマージンに乗らないように インデントすべきです; これにより SMTP が途中で切ってしまうことを防ぎます。
("my" を使って宣言された)レキシカル変数は、フォーマットがその レキシカル変数のスコープの内側で宣言されていない限りは、フォーマットの 内側では不可視になります(バージョン 5.001 までは全ての場所で不可視でした)。
プログラムの環境が LC_NUMERIC ロケールを指定して、フォーマットを 宣言したときに use locale
が有効の場合でも、 指定した小数点文字がフォーマットされた出力に現れます。 フォーマットされた出力は write() が呼び出された時点では use locale
プラグマによって制御することはできません。 ロケールの処理に関する議論は perllocale を参照してください。
固定テキストフィールドに表示される文字列中の各制御文字は空白に 置換されます。 (しかし、詰め込みモードでは \r
は特別な意味を持つことを 忘れないでください。) これは、制御文字が出力メディアによっては「消える」時に揃えが乱れることを 防ぎます。