PERLFORM(1) USER COMMANDS PERLFORM(1) NAME perlform - Perl のフォーマット文 DESCRIPTION Perl は、単純なレポートやチャートを作成するお手伝いができま す。 これを簡単にするために、Perl では、印字された時にどの ように見えるかに近い形で、コードの中で出力ページをレイアウト できます。 Perl はまた、1 ページに何行入るか、現在何ページ めか、いつページヘッダを出力するかといったことのめんどうをみ てくれます。 使用されるキーワードは FORTRAN から持ってきた もので、宣言に format 文を用い、実行に write() を使います。 perlfunc manpage で formline と write の項を参照してください。 幸い、レイアウトは BASIC の PRINT USING 文のようなものより読 みやすいものです。 安上がりな nroff(1) と考えてください。 フォーマットは、パッケージやサブルーティンと同じく、実行文で はなく、宣言文ですから、プログラムのどこにおいてもかまいませ ん。 (しかしながら、普通は 1 か所にまとめておいた方が良いで しょう。) フォーマットは Perl の他の「型」とは別に名前空間 がとられます。 このことは、"Foo" という名前の関数を持ってい るとき、それは、"Foo" という名前のフォーマットを持っているの とは違うということです。 しかし、あるファイルハンドルに対し て付随するフォーマットのデフォルトの名前は、そのファイルハン ドルの名前と同じものです。 つまり、STDOUT に対するデフォル トのデフォルトのフォーマットは "STDOUT" であり、ファイルハン ドル TEMP に対するデフォルトのフォーマット名は "TEMP" だとい うことです。 これらは同じように見えますが、違うものです。 出力レコードのフォーマットは以下のように宣言します: format NAME = FORMLIST . NAME を指定しないと、"STDOUT" というフォーマットが定義されま す。 FORMLIST は行の並びからなり、それぞれの行は次の 3 つの どれかになります: 1. コメント。 行頭に # を置くとその行はコメントとして扱わ れます。 2. ピクチャ行。 出力する行のフォーマットを 1 行単位で示し ます。 3. 引数行。 先行するピクチャ行に埋め込む値を示します。 ピクチャ行は、ある種のフィールドが、その行に値を埋め込むのに 使われる他は、まったく見た目の通りに印字されます。 ピクチャ 行の各々のフィールドは、"@" や "^" で始まるようになっていま す。 ピクチャ行では、いかなる形でも変数の展開は行なわれませ ん。 "@" フィールド (配列の "@" と混同しないこと) は、通常 のフィードで、"^" フィールドが複数行に渡るテキストのブロック Perl manpages Last change: Release 5.0 Patchlevel 00 1 PERLFORM(1) USER COMMANDS PERLFORM(1) を作るために使われます。 フィールドの長さは、"@" や "^" の あとに "<" や ">" や "|" を必要な数だけ並べることで表わし、 それぞれ、左づめ、右づめ、センタリングを表わします。 もし、 値がフィールドをはみだす場合には、切り捨てられてしまいます。 右づめの別の形式として、数値フィールドを示す、"#" (と "." も 可) を使うこともできます。 この方法で、小数点の位置を揃える ことができます。 こういったフィールドに与えられる値に改行が 含まれていると、その改行までのテキストが印字されます。 もう 1 つ "@*" というフィールドを使うと、値の切捨てを行なわずに、 複数行にわたって印字するために使用するようにできます。 この フィールドを使う場合には、同じ行に別のフィールドを指定できま せん。 各フィールドへの値は、次の行に、ピクチャフィールドと同じ順番 で指定します。 値を示す式は、コンマで区切られている必要があ ります。 式はすべて、その行が評価される前に、リストコンテキ ストで評価されますので、1 つのリスト式が複数のリスト要素を作 るようにもできます。 式は、中括弧で括って、複数行にすること もできます。 もし、そのようにするときは、最初の行の最初のト ークンを、開き中括弧にしなければなりません。 "@" ではなく、"^" で始まるピクチャフィールドは、特別な扱いを 受けます。 "#" を使ったフィールドでは、値が未定義の場合に、 空白となります。 他のタイプのフィールドでは、"^" はある種の 充填モードになります。 値として指定できるものは、任意の式で はなく、テキスト文字列が入ったスカラ変数名でなければなりませ ん。 Perl はそのフィールドに入るだけのテキストを詰め込んで、 変数内の文字列の先頭から、その分を切り落とします。 次にその 変数が参照されるときには、残りの部分のテキストが印字できるこ とになります。 (そうです、このことは変数自身が write() の実 行中に変更され、もとには戻らないということです。) 普通は、 テキストを矩形に表示するように、フィールドを縦に積むような形 で使われます。 一番最後のフィールドを "..." という文字列で 終わらせておくと、表示するテキストがその最後のフィールドにも 収まりきらなかったときに、そのまま "..." が出力されます。 $: (English モジュール使用時は $FORMAT_LINE_BREAK_CHARACTERS) という変数に文字列を設定すると、出力する文字列の中に、この変 数内に設定した文字列中の 1 字が見つかったときに改行するよう になります。 "^" フィールドを使うと可変長のレコードが作れます。 フォーマ ットされるテキストが短ければ、行のどこかに "~" をおくことで 空行の出力をサプレスすることができます。 出力時には、"~" は 空白に変換されます。 "~" を 2 つ並べて書くと、すべてのフィ ールドに入れられる値が尽きてしまうまで、そのピクチャ行と引数 行を繰り返して使用します。 (もし、"@" タイプのフィールドを 使うならば、与える式は永久に同じ値を出すことの無いようにしな いといけません。) ページ先頭フォーマットの処理はデフォルトでは、現在のファイル ハンドル名に "_TOP" をつなげた名前のフォーマットで行なわれま Perl manpages Last change: Release 5.0 Patchlevel 00 2 PERLFORM(1) USER COMMANDS PERLFORM(1) す。 これは、各々のページの先頭で使われます。 perlfunc manpage の write() の項を参照してください。 例: # /etc/passwd ファイルについてのレポート format STDOUT_TOP = Passwd File Name Login Office Uid Gid Home ------------------------------------------------------------------ . format STDOUT = @<<<<<<<<<<<<<<<<<< @||||||| @<<<<<<@>>>> @>>>> @<<<<<<<<<<<<<<<<< $name, $login, $office,$uid,$gid, $home . # バグ報告書からのレポート 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) を自分で調整する必要がありま す。 Perl manpages Last change: Release 5.0 Patchlevel 00 3 PERLFORM(1) USER COMMANDS PERLFORM(1) フォーマット変数 その時点でのフォーマット名は、変数 $~ ($FORMAT_NAME) に入れ られており、ページ先頭フォーマットは、$^ ($FORMAT_TOP_NAME) に入れられています。 ページ番号は、$% ($FORMAT_PAGE_NUMBER) に、ページあたりの行数は $= ($FORMAT_LINES_PER_PAGE) に入っ ています。 そのファイルハンドルに対して出力を自動フラッシュ するか否かは、$| ($OUTPUT_AUTOFLUSH) に入れられています。 (先頭ページ以外の) ページの先頭の前に出力する文字列は、$^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; $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"; ずっと良いでしょう ! NOTES 引数行には、(^ フィールドに対してではなく、@ フィールドに対 して) 任意の式が許容されますから、もっと込み入った処理には、 sprintf() や独自の関数などに任せることもできます。 たとえば: Perl manpages Last change: Release 5.0 Patchlevel 00 4 PERLFORM(1) USER COMMANDS PERLFORM(1) 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 = ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~ $_ . Perl manpages Last change: Release 5.0 Patchlevel 00 5 PERLFORM(1) USER COMMANDS PERLFORM(1) $/ = ''; while (<>) { s/\s*\n\s*/ /g; write; } フッタ $FORMAT_TOP_NAME には、その時点のページ先頭フォーマット(ヘ ッダフォーマット)の名前が納められていますが、フッタに対して 自動的に同じことをするための、対応する機構は用意されていませ ん。 フォーマットを評価するまでは、どのくらいの大きさになる のかがわからないことも、大きな問題と言えます。 これは、Todo のリストに入っています。 1 つの方法をあげておきます: 固定サイズのフッタであれば、 write() を行なう前ごとに $FORMAT_LINES_LEFT をチェックして、 必要なときにフッタを print することで、フッタを付けることが できます。 もう 1 つ別の方法です: open(MESELF, "|-") を使って、自分自 身にパイプを open し (perlfunc manpage の open() の項を参照 してください)、常に STDOUT の代わりに MESELF に 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 English; use Carp; sub swrite { croak "usage: swrite PICTURE ARGS" unless @ARG; local($ACCUMULATOR); formline(@ARG); return $ACCUMULATOR; } Perl manpages Last change: Release 5.0 Patchlevel 00 6 PERLFORM(1) USER COMMANDS PERLFORM(1) $string = swrite(<<'END', 1, 2, 3); Check me out @<<< @||| @>>> END print $string; のようにします。 WARNING format の実行中には、グローバルな変数と local() で宣言された 動的スコープの変数だけが見えます。 my() で宣言された字句ス コープの変数は、format と同じスコープにあるとはみなされず、 使用することができません。 Perl manpages Last change: Release 5.0 Patchlevel 00 7