[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19 入出力


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.1 ポート

Builtin Class: <port>

Portは、Schemeにおいて抽象化された入出力のインタフェースを提供します。 Gaucheはportにいくつかの拡張を行い、いろいろなアプリケーションに対応できるようにしました。

標準のSchemeでは、portはキャラクタを一文字づつ読み込む(一文字先読み可)、 もしくは書き出すだけのもので、他の入出力ルーチンはその上に構築されています。

Gaucheではさらに次のような操作がportに対して可能になっています。

バイナリI/O

キャラクタ毎でなく、オクテット毎のI/Oが可能です(Gaucheではマルチバイト文字を 扱うので、この2つが異なることに注意して下さい)。大抵のポートでは キャラクタI/OとバイナリI/Oを混ぜて使うことができます。

最も基本的なバイナリI/Oプリミティブはread-bytewrite-byteです。 また、より高機能なpackunpackといった手続きが binary.pack - バイナリデータのパック で提供されています。

ブロックI/O

ポートから指定した数のバイト列を読んだり、ポートへ書いたりできます。 ポートがブロックI/Oを行うデバイスに接続されている場合、ある程度大きなブロック毎に 読み書きすると効率の良いデータ転送ができます。

変換

ポートはまた、データストリームを変換するのにも使えます。例えばgauche.charconv モジュールでは、文字コード間の変換を行うポートを提供しています (詳しくはgauche.charconv - 文字コード変換を参照)。

追加機能

また、特殊な機能を実現するポートもあります。 コーディング認識ポート(コーディング認識ポート参照)は ファイル中の特殊なコメントを認識して、そのファイルがどの文字エンコーディング で書かれているかを検出します。 仮想ポート(gauche.vport - 仮想ポート参照)はSchemeでふるまいをプログラムできる ポートを提供します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.2 ポートとスレッド

GaucheがスレッドサポートをONにしてコンパイルされている場合、 組み込みのポート操作関数はポートをロックして、 複数のスレッドからの同一のポートへのアクセスがポートの内部状態を壊さないように しています。 (SRFI-18によって要求されている動作です)。 ここで「組み込みのポート操作関数」はGaucheにより提供される、 ポートを引数に取り何らかのI/O動作や問い合わせを行う手続きで、 read/writeread-char/write-charport->string等を含みます。 但し、call-with-*with-*系関数は、 与えられた手続きを呼ぶ際にはポートをロックしません。 その手続きが別のスレッドにポートを渡すかもしれず、Gaucheにはそれを知ることが できないからです。

従って、マルチスレッド環境でポートへのアクセス競合により ポートの内部状態を壊してしまうんじゃないか、などとあまり神経質に なる必要はありません。但し、このロック機構はあくまで予想外の アクセス競合によってポートがおかしな状態になってしまうことを 防ぐための安全ネットであって、一般的な排他制御機構として使われる ことは想定していないのに注意して下さい。このロックの実装は、 ポートへのアクセス競合は例外的な場合のみであると仮定し、 通常のアクセスにおけるオーバヘッドを避けるために、スピンロックを 使用します。もし、意図的にポートアクセスが競合するようなコードを書く場合は、 明示的に排他制御をしてください。

Function: with-port-locking port thunk

portをロックし、thunkを実行します。 ロックはthunkのダイナミックエクステントの期間有効です。

portがロックされている期間での組み込みのポートアクセス関数の 呼び出しは排他制御をバイパスするため、性能向上が見込まれます。

ロックの有効期間はthunkのダイナミックエクステントなので、 thunk内からwith-port-lockingの外で捕捉された 継続を呼んだ場合、ロックは解放されます。その後、thunk内で 捕捉された継続が呼ばれた場合、再びロックが獲得されます。

with-port-lockingはネスト可能です。ロックは最も外側の with-port-lockingの期間中有効となります。

この手続きはポート組込みのロック機構を利用します。つまり、ポートアクセスが 競合した場合はbusy waitになるということです。この手続きはあくまで 頻繁なロックによるオーバヘッドを回避するためのものです。 もし本当に競合が予測される場合は明示的に排他制御を行ってください。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.3 ポート共通の操作

Function: port? obj
Function: input-port? obj
Function: output-port? obj

[R5RS] obj がそれぞれポート、入力ポート、出力ポートなら真を返します。 port?はR5RSの"Standard Procedures"の項には 載っていませんが、"Disjointness of Types"の項に挙げられています。

Function: port-closed? port

objがポートであり、既に閉じられていた場合に真を返します。 一度閉じたポートは再び開くことはできません。

Function: current-input-port
Function: current-output-port

[R5RS] 現在の入力ポートと出力ポートをそれぞれ返します。

Function: current-error-port

現在のエラーポートを返します。

Function: standard-input-port
Function: standard-output-port
Function: standard-error-port

プログラム開始時点の入力、出力、エラーポートをそれぞれ返します。

Function: with-input-from-port port thunk
Function: with-output-to-port port thunk
Function: with-error-to-port port thunk

それぞれ入力、出力、エラーポートをportにセットした状態でthunkを呼び出します。

Function: with-ports iport oport eport thunk

上の3つの動作を同時に行う手続きです。 入力、出力、エラーの各ポートをそれぞれiport, oport, eportに セットしてthunkを呼び出します。変更する必要がないポートの引数には #fを渡すことができます。

Function: close-input-port port
Function: close-output-port port

[R5RS] それぞれ、入力ポートと出力ポートを閉じます。

Function: port-type port

portのタイプを、シンボルfilestringprocの いずれかで返します。

Function: port-name port

portの名前を返します。ポートがファイルに関連付けられている場合は、ポートの名前は ファイル名です。そうでない場合、ポートを説明する文字列が返されます。

Function: port-buffering port
Function: (setter port-buffering) port buffering-mode

ファイルポート((port-type port)fileを返すもの) に対して、そのバッファリングモードを読みだし、もしくは変更します。 入力ポートではバッファリングモードは :full:modest:noneのいずれかです。 出力ポートでは :full:line:noneのいずれかです。 バッファリングモードの詳細な説明は、ファイルポート を参照してください。

port-bufferingがファイルポート以外のポートに対して呼ばれた場合は #fを返します。port-bufferingのsetterが ファイルポート以外のポートに対して呼ばれた場合はエラーとなります。

Function: port-current-line port

portの現在の行番号を返します。行番号は、ファイルに関連付けられたポートで かつシーケンシャルなキャラクタI/Oを行っている場合のみ有効です。それ以外の場合は -1を返します。

Function: port-file-number port

portがファイルに関連付けられている場合、そのファイルディスクリプタ番号を 返します。それ以外の場合は#fを返します。

Function: port-seek port offset &optional whence

portがランダムアクセス可能なポートの場合、 この手続きはportのread/writeポインタをoffsetwhenceの値によって 設定し、新たなread/writeポインタの値(データの先頭からのバイトオフセット)を 返します。portがランダムアクセス可能でない場合は#fが返されます。 現在のバージョンでは、ファイルポートおよび入力文字列ポートがランダムアクセス可能です。 出力文字列ポートは現在のポインタの値を問い合わせる動作だけが可能です。

ポートのポインタはバイト数で表現され、文字数とは異なることに注意して下さい。

portが出力ファイルポートの場合は、データの終端を超えた位置までseek することが可能です。その場合の動作はPOSIXのlseek(2)に準じます。 入力ファイルポートや入力文字列ポートではデータの終端以降にseekすることはできません。

whence引数は、offsetの基準を指定する小さな整数です。 以下の定数が定義されています。

SEEK_SET

offsetはデータ先頭からのバイト数を指定します。 whenceが省略された場合のデフォルトの動作です。

SEEK_CUR

offsetは現在のread/writeポインタからの相対バイト数を指定します。 offsetが0であれば、ポインタを動かさずに現在のポート位置を知ることができます。

SEEK_END

offsetはデータの終端からの相対バイト数を指定します。

Function: port-tell port

portの現在のread/writeポインタの値をバイト数で返します。 portがランダムアクセス可能でない場合は#fが返されます。 これは以下の呼び出しと等価です。

 
(port-seek port 0 SEEK_CUR)

名前に関するメモ: port-seekは他の処理系で seekfile-positioninput-port-position/ output-port-position等と呼ばれています。 port-telltellftellset-file-position!等と 呼ばれています。いくつかの処理系はport-positionという手続きを 持っていますが、port-seekとは別の機能を実現しています。 file-positionはCommonLisp由来の名前ですが、 fileポート以外のものも扱うため採用しませんでした。 また、seektellはPOSIXの名前由来であり、 Gaucheの名前付け規則を使ってsys-seeksys-tellとしても よさそうですが、portの操作はシステムコールレベルよりも抽象度が高いため これも採用しませんでした。結局、新しい名前を採用することにしました。

Function: copy-port src dst &keyword (unit 0) (size #f)

srcからEOFまでデータを読みだし、dstへ書き出します。

キーワード引数unitは0以上の整数か、シンボルbyteもしくはchar でなければなりません。これはデータをコピーする単位を指定します。 整数ならば、その大きさ(0の場合はシステム規定の大きさ)のバッファが確保され、 ブロックI/Oを使って転送が行われます。通常のファイルをコピーする場合などはこれが 速いでしょう。もしunitがシンボルbyteであれば、バイト毎 に読みだし/書き込みが行われます。unitがシンボルcharであれば、 キャラクタ毎に読みだし/書き込みが行われます。

キーワード引数sizeに非負の整数が与えられた場合、それはコピーされるデータの 最大量を指定します。unitがシンボルcharの場合はsizeは コピーされる文字数を、そうでない場合はバイト数を指定します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.4 ファイルポート

Function: open-input-file filename &keyword if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: open-output-file filename &keyword if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R5RS+] ファイルfilenameを入力または出力用にオープンし、 入力ポートまたは出力ポートを作成して返します。

キーワード引数により、動作を細かく指定できます。

:if-exists

このキーワード引数はopen-output-fileのみに指定でき、 filenameが既に存在した場合の動作を指定します。次の値のいずれかを与えることができます。

:supersede

既存のファイルが長さ0に縮められます。これが既定の動作です。

:append

既存のファイルにこれから書き出す内容が追加されます。

:overwrite

既存のファイルにこれから書き出す内容が上書きされます。 書き出されるデータが既存のファイルのデータよりも短い場合、 残りの部分はそのまま残されます。

:error

エラーが報告されます。

#f

何もせず、#fを返します。

:if-does-not-exist

このキーワード引数はfilenameが存在しない場合の動作を指定します。

:error

エラーを報告します。これがopen-input-fileの既定の動作です。

:create

ファイルが作成されます。これがopen-output-fileの既定の動作です。 ファイルの存在のチェックと作成はアトミックに行われます。 このオプションに加え、if-existsオプションに:error#fを 指定することで、排他的にファイルを作成することができます。 open-input-fileに対してはこの値を指定することはできません。

#f

何もせず、#fを返します。

:buffering

この引数はバッファリングモードを指定します。以下の値が設定できます。 ポートのバッファリングモードは手続きport-buffering (ポート共通の操作参照)によって 読みだし/変更可能です。

:full

出来る限りデータをバッファリングします。これがデフォルトのモードです。

:none

バッファリングを行いません。出力ポートにデータが書き出されるか、 入力ポートからデータが読み込まれる度に、下位にあるシステムコールが呼ばれます。 プロセスの標準エラーポートはこのモードでオープンされています。

:line

このモードは出力ポートにのみ有効です。書き出されたデータはバッファに 貯められますが、改行文字が書かれたらフラッシュされます。 このモードは対話的な出力ポートなどに便利です。 プロセスの標準出力ポートはこのモードでオープンされています。 (これは、Cのstdioライブラリの「ラインバッファリング」とちょっと違うことに 注意してください。stdioでは同じファイルディスクリプタから入力が行われる時も バッファはフラッシュされますが、Gaucheではそうはなりません)。

:modest

このモードは入力ポートにのみ有効です。ほとんど:fullバッファリングモードと 同じですが、read-blockはポートに要求されたデータより少ないデータしか 無かった場合、要求された量がたまるまで待つのではなく、今あるデータだけを 返します。このモードはポートがパイプやネットワークに接続されている場合に 便利です。

:element-type

この引数はファイルのタイプを指定します。

:character

ファイルはキャラクタモード(テキストモード)でオープンされます。

:binary

ファイルはバイナリモードでオープンされます。

現在のバージョンでは、この引数は無視され、全てのファイルはバイナリモードで オープンされます。いずれにせよUnixプラットフォームでは違いはありません。

:encoding

この引数はファイルの文字エンコーディングを指定します。引数は文字列かシンボルで、 文字エンコーディングスキーム(CES)の名前でなければなりません。 open-input-fileでは、ここにワイルドカードCES (例: *jp) を 渡して、入力ファイルのエンコーディングを推測させることもできます (文字エンコーディングの自動判定参照)。

この引数が与えられた場合、Gaucheは自動的にgauche.charconvモジュールを ロードし、ポートの入出力時に文字コード変換を行います。 CESについて詳しくはサポートされる文字エンコーディングを参照してください。

:conversion-buffer-size

この引数は、文字エンコーディング変換に使うバッファサイズを指定するために encoding引数と共に使うことができます。渡された値はそのまま 文字コード変換ポートのコンストラクタのbuffer-size引数に渡されます (変換ポート参照)。

この引数を指定する必要は滅多にありませんが、入力ファイルの文字エンコーディングを 推測しなければならない場合、大きめのバッファサイズの方が精度が上がります。 推測ルーチンがより多くのデータを見て文字エンコーディングを決定できるからです。

if-existsif-does-not-existフラグの組合せにより、 色々な動作を実現できます。

 
(open-output-file "foo" :if-exists :error)
 ⇒ ;"foo"を排他的にオープンするかエラーを報告する

(open-output-file "foo" :if-exists #f)
 ⇒ ;"foo"を排他的にオープンするか#fを返す

(open-output-file "foo" :if-exists :append
                        :if-does-not-exist :error)
 ⇒ ;"foo"が既に存在する場合に限り、それを追加モードでオープン

ファイルをオープンせずにその存在をチェックするには、 sys-accessfile-exists?を使って下さい (ファイルの状態参照)。

移植性に関する註:Schemeシステムによっては、filenameのところに シェルコマンドを指定して、サブプロセスの標準入出力と通信できるようにするものが あります。他のスクリプティング言語(例:Perl)にも同様の機能があります。 Gaucheでは、open-input-fileopen-output-fileは あくまでファイル (OSがファイルとして扱うもの) のみに対して使えます。 サブプロセスと通信するためには、「プロセスポート」という機能が提供されています。 Process portsを参照して下さい。

Function: call-with-input-file string proc &keyword if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: call-with-output-file string proc &keyword if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R5RS] stringで示されるファイルを入力または出力用にオープンし、 作成されたポートを引数として手続きprocを呼び出します。 procが正常終了するか、proc内で捕捉されないエラーが起きた場合に ファイルはクローズされます。

キーワード引数は open-input-file及びopen-output-fileのものと同じ意味を持ちます。 if-existsif-does-not-exist#fを指定した場合、 ファイルがオープンされなかった場合はprocにポートではなく#fが渡される ことに注意して下さい。

procが返す値を返します。

Function: with-input-from-file string thunk &keyword if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: with-output-to-file string thunk &keyword if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R5RS] stringで示されるファイルを入力または出力用にオープンし、オープンされた ポートを現在の入力または出力ポートに設定して、thunkを呼び出します。 thunkが戻るか、thunk内で捕捉されないエラーが生じた際にファイルは閉じられます。

thunkが返す値を返します。

キーワード引数は open-input-file及びopen-output-fileのものと同じ意味を持ちます。 但しif-existsif-does-not-exist#fが指定され、 ファイルがオープンできなかった場合は、thunkは呼ばれずに 直ちに#fが返されます。

ポートを閉じるセマンティクスについて: R5RSはcall-with-input-file等の説明において、次のように述べています。 「procが戻って来なかった場合、今後ポートが読み書きに一切使われないことが 証明できない限りは、ポートは自動的には閉じられない」。

Gaucheの実装は若干この条件には反しています。捕捉されないエラーがprocから 発せられたというだけでは、そのポートが今後一切使われないかどうかはわかりません。 しかし実際には、そのようなエラーが発せられた後でポートに対して意味のある操作をするのは 非現実的です。ポートがどのような状態にあるかわからないわけですから。 現実的なプログラムでは、ポートに対して意味のある操作をしつづけたいのなら、 procの中で明示的にエラーをハンドルすべきでしょう。

call-with-input-fileの外で捕捉された継続をproc内で呼んだ場合には ポートは閉じられないことに注意して下さい。後でprocへと制御が戻ってくるかも しれないからです (コルーチン等)。また、 低レベルの例外メカニズム(例外の処理 参照)を利用した場合、エラー時にポートを閉じるのはプログラマの責任になります。

Function: open-input-fd-port fd &keyword buffering name owner?
Function: open-output-fd-port fd &keyword buffering name owner?

与えられたファイルディスクリプタにアクセスする入力または出力ポートを 作成して返します。bufferingopen-input-file の項で 説明されたポートのバッファリングモードを指定します。デフォルトは:fullです。 nameport-nameによって返されるポートの名前を指定します。 owner? は、このポートを閉じた時にfdもクローズすべきかどうかを 指定するブーリアン値です。

Function: port-fd-dup! toport fromport

システムのdup2(2)のインタフェースです。 アトミックにtoportのファイルディスクリプタをクローズし、fromportの ファイルディスクリプタを複製したものをtoportに設定します。 toportfromportはいずれもファイルポートでなければなりません。

ファイルディスクリプタが「複製」されると、ふたつのディスクリプタ番号が異なっていても それらはシステムのオープンファイルテーブルの同じエントリを指します。 例えば、現在の(システムレベルでの)ファイル上の読み書き位置は共有されます。 port-fd-dup!の後で、port-seekfromportに 対して呼び出せば、その変更はtoportの読み書き位置にも影響を与えるでしょうし、 その逆もまたあります。ただし、共有されるのはシステムレベルの情報のみで、 toportfromportがバッファリングされている場合、バッファの内容は 共有されません。

この手続きは、主にファイルディスクリプタを明示的に制御する必要のあるプログラム のために用意されています。例えばデーモンプロセスがその入出力を‘/dev/null’などの 無難なデバイスに切り替えたり、シェルプロセスが子プロセスをexecする前に そのファイルディスクリプタをセットアップしたりするような場合です。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.5 文字列ポート

文字列ポートは、メモリ上のデータと関連付けられたポートです。

Function: open-input-string string

[SRFI-6] stringを内容とする入力文字列ポートを作って返します。 文字列に逐次的にアクセスする場合、インデックスをインクリメントしながら string-refを呼び出すより効率の良い方法です。

 
(define p (open-input-string "文字 列"))
(read p) ⇒ 文字
(read-char p) ⇒ #\space
(read-char p) ⇒ #\列
(read-char p) ⇒ #<eof>
(read-char p) ⇒ #<eof>
Function: get-remaining-input-string port

portは入力文字列ポートでなければなりません。 入力ポートに残っている文字列を返します。 portの内部ポインタは動かされないので、portに対するreadは 影響を受けません。portが既にEOFに達していた場合は、空文字列が返されます。

 
(define p (open-input-string "abc\ndef"))
(read-line p)                  ⇒ "abc"
(get-remaining-input-string p) ⇒ "def"
(read-char p)                  ⇒ #\d
(read-line p)                  ⇒ "ef"
(get-remaining-input-string p) ⇒ ""
Function: open-output-string

[SRFI-6] 出力文字列ポートを作成して返します。このポートに書き出された文字列は 内部のバッファにたくわえられ、get-output-string で取り出すことが できます。 これは、順番に文字列を構成する方法として、あらかじめ文字列をアロケートして string-set!で埋めて行くよりもずっと効率の良い方法です。

Function: get-output-string port

[SRFI-6] 出力文字列ポートportを取り、それまでそのポートに蓄積された 文字列を返します。バイトデータがそのポートに書き出されていた場合、 この手続きはまず内部バッファをスキャンし、結果が完全な文字列で表現できるかどうかを 調べます。もし表現できなければ、不完全な文字列が返されます。

これはportの操作には影響をあたえません。get-ouptut-stringを 呼んだ後でも、portに内容を蓄積しつづけることができます。

Function: call-with-input-string string proc
Function: call-with-output-string proc
Function: with-input-from-string string thunk
Function: with-output-to-string thunk

これらのユーティリティ関数は次に定義されるような動作をします。 インタフェースはファイルポートを扱う類似の関数と揃えてあります。

 
(define (call-with-output-string proc)
  (let ((out (open-output-string)))
    (proc out)
    (get-output-string out)))

(define (call-with-input-string str proc)
  (let ((in (open-input-string str)))
    (proc in)))

(define (with-output-to-string thunk)
  (let ((out (open-output-string)))
    (with-output-to-port out thunk)
    (get-output-string out)))

(define (with-input-from-string str thunk)
  (with-input-from-port (open-input-string str) thunk))
Function: call-with-string-io str proc
Function: with-string-io str thunk
 
(define (call-with-string-io str proc)
  (let ((out (open-output-string))
        (in  (open-input-string str)))
    (proc in out)
    (get-output-string out)))

(define (with-string-io str thunk)
  (with-output-to-string
    (lambda ()
      (with-input-from-string str
        thunk))))
Function: write-to-string obj &optional writer
Function: read-from-string string &optional start end

文字列ポートを使う定型句をユーティリティ関数にしました。

 
(write-to-string obj writer)
  ≡
  (with-output-to-string (lambda () (writer obj)))

(read-from-string string)
  ≡
  (with-input-from-string string read)

writerの既定値はwriteです。start, endは 省略されればそれぞれ0と文字列の長さになります。

移植性への註:Common Lispに同名の関数があります。必須引数の動作は同じですが、 省略可能な引数は異なります。 STkにはread-from-stringがありますが、省略可能な引数は取りません。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.6 コーディング認識ポート

コーディング認識ポートは特殊な手続的入力ポートで、loadが プログラムソースコードを読む際に使われています。このポートは ;; -*- coding: utf-8 -*-のような、プログラムソースの 文字エンコーディングを指定する特殊なコメントを認識し、適切な 文字エンコーディング変換を行います。 特殊なコメントでソースの文字エンコーディングを指定することについては、 マルチバイトスクリプトを参照して下さい。

Function: open-coding-aware-port iport

入力ポートを引数としてとりコーディング認識入力ポートを返します。 基本的には iport からの入力データをリーダにわたしているだけです。 しかし、iport からの入力データの最初の2行以内に、特別な呪文コメント が現れた場合、コーディング認識ポートは、その後に読み込まれるデータについて 必要な文字エンコーディング変換を行います。

引数として渡されたポート、iport は生成されたコーディング認識 ポートによって所有されます。つまり、コーディング認識ポートがクローズ されると、iport もクローズされます。iport から読み込まれた 内容はコーディング認識ポート内でバッファリングさます、したがって、 別のコードで iport から読み出しを行うべきではありません。

デフォルトでは、Gauche の load はプログラムソースを読むのに コーディング認識ポートを使います。したがって、文字エンコーディングを 示す特別な呪文コメントは、Gauche のソースプログラムでは有効になります (Schemeファイルのロード参照)。ただし、この機構自身は load とは 独立しており、このポートを別の目的で利用できます。特にコーディングの 呪文コメントがある Scheme のソースプログラムを処理する関数を書くときに 便利です。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.7 入力

入力に関する手続きで、省略可能な引数iportは入力ポートでなければなりません。 省略された場合が現在の入力ポートが使われます。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.7.1 データの読み込み

Function: read &optional iport

[R5RS] iportからS式をひとつ読み込んで返します。 GaucheはR5RSに定義されている構文要素に加え、字句構造に 定義されている拡張構文要素を認識します。

iportが既にEOFに達していた場合は、EOFオブジェクトが返されます。

この手続きはS式を構成する最後の文字までを読み、その後の文字はポートに 残します。これは、S式に続く空白文字も読み込むCommonLispのreadの 振る舞いとは異なります。

Function: read-with-shared-structure &optional iport
Function: read/ss &optional iport

[SRFI-38] これらの手続きは、SRFI-38 で定義されていて、共有構造を表す記法 (#n=, #n#)を認識できます。Gauche の組み込み read は この SRFI-38 の記法を認識します。それゆえ、これらの手続きは、read と同じで、SRFI-38 との互換性のために用意されています。

Function: read-char &optional iport

[R5RS] iportから1文字読み込んで返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。 iportにあるバイトストリームが正しい文字を構成しない場合、 ふるまいは未定義です。(将来はポート側に、不正な文字に対する対応を決める オプションを設ける予定です)。

Function: peek-char &optional iport

[R5RS] iportから1文字読み込んで返します。文字はそのままiportに留まります。 iportが既にEOFに達していた場合はeofオブジェクトを返します。 iportにあるバイトストリームが正しい文字を構成しない場合、 ふるまいは未定義です。(将来はポート側に、不正な文字に対する対応を決める オプションを設ける予定です)。

Function: read-byte &optional iport

入力ポートiportから1バイト読み込み、0から255までの整数値として返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

Function: peek-byte &optional iport

入力ポートiportの先頭の1バイトを見て、それを0から255までの整数値として返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

Function: read-line &optional iport allow-byte-string?

入力ポートから、行末もしくはEOFまで読み込んで文字列として返します。 よく使われる行末 (LF only, CRLF, and CR only) を認識します。 戻り値にはこれらの行末文字は含まれません。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

iportから、内部文字エンコーディングでは文字を構成し得ないバイトシーケンスが 読まれた場合、デフォルトではread-lineはエラーを通知します。 しかし、省略可能な引数allow-byte-string?に真の値が与えられた場合は、 read-lineはエラーを通知せず、かわりにバイト文字列 (不完全な文字列) を 返します。この動作は、特に文字エンコーディングが不明なソースから読み込む際に 便利です。例えばXMLドキュメントを読み込む際、最初の行のcharsetパラメータを チェックしてから適切な文字エンコーディング変換ポートを使うといった用途などです。

Function: read-block nbytes &optional iport

nbytesバイトのデータをiportから読み込み、 不完全な文字列として返します。iportに十分なデータが無い場合、 返される文字列はnbytesより短いかもしれません。 nbytesが0の場合は、常に空文字列が返されます。

iportが既にEOFに達していた場合はEOFオブジェクトが返されます。

iportがファイルポートだった場合、read-blockは ポートのバッファリングモードによってふるまいが異なります (バッファリングモードの詳細についてはファイルポートを参照して下さい)。

read-blockは呼ばれるたびに新たな文字列をアロケートします。 アロケーションを避け、あらかじめ用意された固定長のバッファにデータを読み込みたい 場合は、gauche.uvectorモジュールのread-block! を使って下さい (ユニフォームベクタのブロック入出力参照)。 Read-block!はuniform vectorをバッファとして用います。

データブロックをポートに書き出すには、データが文字列で表現されている 場合は単純にdisplayが使えます。データがuniform vectorで表現されている 場合はgauche.uvectorモジュールのwrite-blockが 使えます (ユニフォームベクタのブロック入出力参照)。

Function: eof-object

[R6RS] EOFオブジェクトを返します。

Function: eof-object? obj

[R5RS] objがEOFオブジェクトなら#tを返します。

Function: char-ready? port

[R5RS] portから文字が読み出せる状態ならば#tを返します。

今のところ、この手続きはportから少なくとも1バイト読み出せる状態なら#t を返します。そのバイトがマルチバイト文字を構成する場合、char-ready?を返した ポートから文字全てを読み込もうとすると、ブロックする可能性があります。 (通常の使用状況ではそのようなことは起きないでしょうが、理論的には起こり得ます。 慎重を期したい場合はread-blockでバイトシーケンスとして読み込んだ後、 入力文字列ポート等を使って文字毎に読むようにして下さい。)

Function: byte-ready? port

If one byte (octet) is ready to be read from port, returns #t.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.7.2 読み込み時コンストラクタ

SRFI-10で定義されている読み込み時コンストラクタは、ユーザ定義の構造の 外部表現を作るための簡単な方法を提供します。

Reader Syntax: #,(tag arg …)

[SRFI-10] Gaucheはtag (シンボル)をコンストラクタ手続きに関連付ける グローバルなテーブルを管理しています。

リーダーがこの構文に出会ったとき、arg …を読み込み、 tagに関連付けられた読み込みコンストラクタを探し、 arg …を引数としてそのコンストラクタを呼び出し、 その構文を読み込んだ結果としてそのコンストラクタが返した値を挿入します。

この構文はリーダー内部で処理されることに注意して下さい。評価器は argを見ず、リーダーが返したオブジェクトしか見ません。

Function: define-reader-ctor tag procedure

[SRFI-10] 読み込みコンストラクタproceduretagに関連付けます。

例:

 
(define-reader-ctor 'pi (lambda () (* (atan 1) 4)))

#,(pi) ⇒ 3.141592653589793

'(#,(pi)) ⇒ (3.141592653589793)

(define-reader-ctor 'hash
  (lambda (type . pairs)
    (let ((tab (make-hash-table type)))
      (for-each (lambda (pair)
                  (hash-table-put! tab (car pair) (cdr pair)))
                pairs)
      tab)))

(define table
 #,(hash eq? (foo . bar) (duh . dah) (bum . bom)))

table ⇒ #<hash-table eq? 0x80f9398>
(hash-table-get table 'duh) ⇒ dah

write-objectメソッド(出力参照)と組み合わせて、 読み戻ることが可能なフォームで書かれたユーザ定義のクラスを 作ることが簡単になります。

 
(define-class <point> ()
  ((x :init-value 0 :init-keyword :x)
   (y :init-value 0 :init-keyword :y)))

(define-method write-object ((p <point>) out)
  (format out "#,(<point> ~s ~s)" (ref p 'x) (ref p 'y)))

(define-reader-ctor '<point>
  (lambda (x y) (make <point> :x x :y y)))

注意: define-reader-ctorの効果の範囲はSRFI-10には 規定されておらず、SRFI-10をサポートする実装においても互換性の問題を 起こすことがあるかもしれません。 (実際に、define-reader-ctorの存在そのものが実装の選択に 任されています。)

Gaucheでは、現時点においては、define-reader-ctorはそのフォームが コンパイルされ評価された時点で効力を持ちます。 Gaucheはトップレベルのフォームを順番にコンパイル・評価するので、 define-reader-ctorで指定されたtagは、その指定の直後から 使えます。 しかし、define-reader-ctorの呼び出しとtagの使用が beginフォームで囲まれている場合は、beginフォーム全体は 評価される前に一度にコンパイルされるため、うまく動作しません。

他の実装では、define-reader-ctorの呼び出しが効力を持つようにする 前にファイル全体を読み込むことを要求するかも知れません。 その場合は、define-reader-ctorと定義されたtagの使用を 同じファイルに置く事は実質的に不可能です。 可能ならば、define-reader-ctorの呼び出しと、tagの使用は 異なるファイルに分離されることが望まれます。

現在のdefine-reader-ctorに関するもう1つの問題は、それが Gaucheシステムのグローバルテーブルを変更してしまうことで、それゆえに モジュール性が良くありません。 複数人によって書かれたコードは同じタグを使っているかも知れず、 期待されない結果を引き起こすかも知れません。 作者にはまだ明確なアイデアがありませんが、将来のバージョンでは、 Gaucheにはtagのスコープをカプセル化する方法が導入されるかも しれません。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.7.3 入力ユーティリティ手続き

Function: port->string port
Function: port->list reader port
Function: port->string-list port
Function: port->sexp-list port

便利な入力手続きです。APIはScshとSTkから取りました。

port->stringportをEOFまで読み込み、 読んだものを文字列として返します。

port->listは手続きreaderportに繰り返し適用し、 結果をリストに蓄積します。readerがEOFを返したら 蓄積されたリストを返します。

(port->string-list port)(port->list read-line port) であり、 (port->sexp-list port)(port->list read port) です。

Function: port-fold fn knil reader
Function: port-fold-right fn knil reader
Function: port-for-each fn reader
Function: port-map fn reader

readerによって読まれる入力に対する便利な繰り返し手続きです。 基本的に、readerが引数無しでEOFを返すまで繰り返し呼ばれ、 それが返した値に対してfnが呼ばれます。 readerはいずれEOFを返す手続きなら、入力ポートと関係している必要はありません。

readerが要素 {X0, X1, …, Xn} を返したとすると、 port-fold は次の値を返します。

 
(fn Xn (fn Xn-1 … (fn X0 knil)))

一方、port-fold-rightは次の値を返します。

 
(fn X0 (fn X1 … (fn Xn knil)))

すなわち、(port-fold cons '() read)は全ての入力の逆リストを 返し、(port-fold-right cons '() read)(port->list read port)と同じものを返します。

port-for-eachport-mapfnを読まれた要素に 次々と適用します。前者はfnの結果を捨てますが、後者はfnの 結果をリストにして返します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.19.8 出力

以下の手続きで、省略可能な引数portは出力ポートでなければなりません。 省略された場合が現在の出力ポートが使われます。

Function: write obj &optional port
Function: display obj &optional port

[R5RS] オブジェクトobjの表示可能な表現を生成し、出力ポートに書き出します。 writeは可能な限り、objの標準的な外部表現を使い、 生成された出力がreadで再び読み込めるようにします。 displayはより人間にとって可読性の高い出力を生成します。

writedisplayが表示中にユーザ定義クラスのオブジェクトに 出会った場合は、ジェネリックファンクションwrite-objectを呼び出します。

objが循環する構造を持っていた場合、これらの手続きは停止しないかも しれません。write/ssを参照して下さい。

Function: write-with-shared-structure obj &optional port
Function: write/ss obj &optional port
Function: write* obj &optional port

[SRFI-38] writeと同じような出力を生成しますが、これらの手続きはさらに 共有される部分構造や循環構造を認識し、#n=, #n#構文を使って それらを表現します。

 
(write/ss 
  (let ((x (list 'a)))
    (list x x)))
 ⇒ ;; writes (#0=(a) #0#)

(write/ss 
  (let ((x (list 'a)))
    (set-cdr! x x)
    x))
 ⇒ ;; writes #0=(a . #0#)

read手続きもこの構文を認識するので、読み込めば もとの構造と同型の構造が得られます。

3つの手続きは等価です。 Gaucheは、STklosから取ったwrite*という名前を長く使ってきましたが、 srfi-38によってwrite-with-shared-structurewrite/ssが 定義されました。今後は後者の名前を使った方がポータブルでしょう。

註: ユーザ定義のwrite-objectメソッドはこれらの手続きに対して 透過的に動作します。

Function: print expr …

expr … をdisplayを使って現在の出力ポートに表示し、 最後に改行を書き出します。

Method: write-object (obj <object>) port

このメソッドをつかって、オブジェクトをどのように印字するかをカスタマイズ できます。

Function: newline &optional port

[R5RS] portに改行文字を書き出します。

Function: flush &optional port
Function: flush-all-ports

それぞれ、port、および全てのポートにバッファされているデータを 全て書き出します。

手続き"flush"はScheme実装によって様々な名前で呼ばれています: force-output (Scsh, SCM)、 flush-output (Gambit)、flush-output-port (Bigloo) 等。 flushの名前はSTkとSTklosから取りました。

Function: write-char char &optional port

[R5RS] 文字charをポートに出力します。

Function: write-byte byte &optional port

出力ポートに1バイトのデータbyteを書き出します。 byteは0から255の間の正確な整数でなければなりません。

Function: format port string arg …
Function: format string arg …

[SRFI-28+] string の指示に従い、arg …をフォーマットします。 この手続きはCommonLispのformatのサブセットに、Gauche独自の拡張を 加えたものです。また、これはSRFI-28 "Basic format strings" のスーパーセットに なっています (SRFI-28)。

portは出力先を指定します。それが出力ポートであれば、フォーマットされた 結果はそのポートに書き出されます。port#tであれば、結果は 現在の出力ポートに書き出されます。port#fであれば、結果は 文字列としてformatから返されます。 portは省略することもできます。その場合は、port#f を指定したのと同じ動作をします(SRFI-28のformat)。

stringはフォーマット指示子を含んだ文字列です。 フォーマット指示子はチルダ`~'から始まり、特定の文字で終了する文字の並びで、 それぞれのフォーマット指示子が対応するargを取りフォーマットします。 string内のフォーマット指示子以外の文字列はそのまま出力されます。

 
(format #f "the answer is ~s" 42)
  ⇒ "the answer is 42"

フォーマット指示子は一つ以上のコンマで区切られたパラメータを取ることもできます。 パラメータは整数か文字です。文字の場合、クオート文字に続けてその文字を置きます。 パラメータが省略された場合は既定値が使われます。パラメータの意味はフォーマット指示子毎に 異なります。

さらに、フォーマット指示子は2種類のフラグ、`@' と `:' を 取ることができます。これらの組合せでフォーマットの動作が変わります。フラグは (もしあれば)パラメータの後、指示子の文字の直前に置かれなければなりません。

パラメータの位置に文字 `v' か `V' を置くこともできます。 その場合、パラメータの値が引数リストから取られます。対応する引数は整数か 文字、または#fでなければなりません。#fの場合はそのパラメータが 省略されたのと同じになります。

いくつかの例です。

~10,2s

パラメータ10と2を伴う、フォーマット指示子~s

~12,,,'*A

第1パラメータに数値12、第4パラメータに文字`*'を取るフォーマット指示子~a。 第2と第3のパラメータは省略されています。

~10@d

フォーマット指示子~d。パラメータ10と`@'フラグがついています。

~v,vx

フォーマット指示子~x。第1パラメータと第2パラメータは引数リストから取られます。

以下にサポートされているフォーマット指示子を示します。フォーマット指示子の文字自体は 大文字であっても小文字であっても構いません。特に断りのない限り両者は同じ動作をします。

~mincol,colinc,minpad,padchar,maxcolA

ASCII出力。対応する引数がdisplayを使ってフォーマットされます。 整数がmincolに与えられた場合、それは出力される最小の文字数を指定します。 引数のフォーマット結果がmincolより短ければ、空白が右に追加されます(つまり、 左詰めになります)。

colincminpad、そしてpadcharは更に細かいパディング方法を 指定します。padcharに文字が与えられた場合、それが空白文字の代わりにパディング文字と して使われます。minpadに0以上の整数が与えられた場合、少なくともその数だけの パディング文字が追加されます。colincが指定された場合、 追加されるパディング文字の数がcolincの倍数に調整されます。

アトマーク `@' フラグが与えられた場合、結果は右詰めになります。

maxcolパラメータは与えられていれば書かれる文字数の上限を指定します。 フォーマット後の文字列の長さがmaxcolを超えた場合、maxcol文字だけが 書かれます。コロン `:' フラグが同時に与えられていれば、 maxcol - 4 文字が書かれた後、文字列“ ...”が書かれます。

 
(format #f "|~a|" "oops")
  ⇒ "|oops|"
(format #f "|~10a|" "oops")
  ⇒ "|oops      |"
(format #f "|~10@a|" "oops")
  ⇒ "|      oops|"
(format #f "|~10,,,'*@a|" "oops")
  ⇒ "|******oops|"
(format #f "|~10,,,'☆a|" "oops")
  ⇒ "|oops☆☆☆☆☆☆|"

(format #f "|~,,,,10a|" '(abc def ghi jkl))
  ⇒ "|(abc def gh|" 
(format #f "|~,,,,10:a|" '(abc def ghi jkl))
  ⇒ "|(abc de ...|" 
~mincol,colinc,minpad,padchar,maxcolS

S式出力。対応する引数がwriteを使ってフォーマットされます。 パラメータの意味は~A指示子と同じです。

 
(format #f "|~s|" "oops")
  ⇒ "|\"oops\"|"
(format #f "|~10s|" "oops")
  ⇒ "|\"oops\"    |"
(format #f "|~10@s|" "oops")
  ⇒ "|    \"oops\"|"
(format #f "|~10,,,'*@s|" "oops")
  ⇒ "|****\"oops\"|"
(format #f "|~10,,,'★s|" "oops")
  ⇒ "|\"oops\"★★★★|"
~mincol,padchar,commachar,intervalD

10進出力。対応する引数が10進数表記でフォーマットされます。もし引数が数値でなければ、 全てのパラメータは(`v'パラメータの処理後に)無視され、 引数は~Aでフォーマットされます。

もしmincolに整数が与えられたら、それが最小の文字数を指定します。 結果の文字数がそれより少なければ、文字padcharが左に追加されます(右詰めになります)。 padcharが省略された場合は空白文字が使われます。

 
(format #f "|~d|" 12345)
  ⇒ "|12345|"
(format #f "|~10d|" 12345)
  ⇒ "|     12345|"
(format #f "|~10,'0d|" 12345)
  ⇒ "|0000012345|"

アトマーク `@' フラグが与えられた場合、正の引数に対して `+' が 先頭につけられます。

コロンフラグ `:' が与えられた場合、結果の文字はinterval文字毎に まとめられ、間に文字commacharが挿入されます。デフォルトでは3文字毎にコンマが 挿入されます。

 
(format #f "|~:d|" 12345)
  ⇒ "|12,345|"
(format #f "|~,,'_,4:d|" -12345678)
  ⇒ "|-1234_5678|"
~mincol,padchar,commachar,intervalB

2進出力。対応する引数が2進数の整数としてフォーマットされます。 パラメータの意味は~Dと同じです。

~mincol,padchar,commachar,intervalO

8進出力。対応する引数が8進数の整数としてフォーマットされます。 パラメータの意味は~Dと同じです。

~mincol,padchar,commachar,intervalX
~mincol,padchar,commachar,intervalx

16進出力。対応する引数が16進数の整数としてフォーマットされます。 フォーマット指示文字に `X' が与えられた場合は `ABCDEF' が桁文字として 使われ、 `x' が与えられた場合は `abcdef' が桁文字として使われます。 パラメータの意味は~Dと同じです。

 
(format #f "~8,'0x" 259847592)
  ⇒ "0f7cf5a8"
(format #f "~8,'0X" 259847592)
  ⇒ "0F7CF5A8"
~count*

引数のカウンタをcountだけ後方にずらします。つまり、count個の引数が 無視されることになります。countのデフォルト値は1です。 コロンフラグが与えられた場合は引数カウンタを前方に動かします。 例えば~:*は次のディレクティブが直前に使った引数を再び使うようにします。 アトマークフラグが与えられた場合は、countが引数の絶対位置を示します。 0が最初の引数です。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated by Shiro Kawai on November, 22 2009 using texi2html 1.78.