ストリーム

要約:
  • ストリームとは、リソースに対する読み取りまたは書き込みが可能なデータ列です。
  • ストリームは単一方向です。
ストリームは、アプレットで読み取りまたは書き込みのいずれかか可能なデータ列です。リソースとデータを交換するための汎用インタフェースを生成します。通常は、ストリームを作成したらその出力先を気にする必要はありません。
ストリームは一方向にのみ流れます。プログラムは、入力ストリームからデータを読み取り、出力ストリームにデータを書き込みます。読み取りと書き込みの両方を行うリソースの場合 (たとえばネットワーク ソケットなど) は、そのリソースに対する入力および出力ストリームの両方を開く必要があります。

ストリーム クラス

要約:
  • ストリームは、その特徴によって分類されます。
  • 入力ストリームからはデータを読み取り、出力ストリームにはデータを書き込みます。
  • バイナリ ストリームは生バイトを扱い、テキスト ストリームはテキストを扱います。
  • バッファ ストリームは、バッファを使用してオーバーヘッドを削減します。
ストリーム クラスにはさまざまなものがあります。これらのクラスの相違点は、その機能や目的によるものです。
次に相違点を示します。
ストリーム クラスは、その特徴に応じて名前が付けられています。たとえば、TranscodingSeekableTextInputStream のインスタンスは、マルチバイト スキームでエンコードされたテキストの読み取りが可能な入力ストリームで、このストリーム内の任意の位置からデータを読み取ることができます。

バイト ストリーム

要約:
  • バイナリ ファイルの読み取りおよび書き込みには、バイト ストリームを使用します。
  • read-open-byte は、生バイナリ データとしてファイルを開きます。
  • データを格納するには byte 型のオブジェクトを使用します。
  • read および read-one はバイナリ データを読み取ります。
  • read-bytes-from は、1 度のステップでファイルを開いて読み取ります。
  • バイナリ ファイルの書き込みは、テキスト ファイルの書き込みと似ています。
バイト ストリームにより、生バイナリ データの読み取りと書き込みが可能になります。Curl® 実行環境 ではデータの解釈は行なわれません。これらのストリームは、主に低レベルでバイナリ データ (イメージ ファイルなど) を操作する必要がある時に使用します。通常は、規定のバイト ブロック単位でこれらのストリームの 1 つに対して読み取りおよび書き込みを行います。

バイナリ ファイルの読み取り

read-open-byte プロシージャを使用して、ファイルを開いてバイト単位で読み取ることができます。このプロシージャは、ByteInputStream クラスのインスタンスを返します。
バイナリ ストリームからの読み取りは、1 つの例外を除けばテキスト ストリームからの読み取りと似ています。byte として宣言されたオブジェクトに値を読み込む必要があります。Curl® 言語の文字は 1 バイトより大きく、Unicode 文字表記が可能なため、char として宣言されたオブジェクトを使用できません。
バイナリ ファイルはテキスト ファイルのように行に分割できないので、ストリームからデータを読み取るには InputStream-of.read または InputStream-of.read-one を使用する必要があります。(ByteInputStream は、InputStream-of を継承することに注意してください。)
次の例では、byte 配列にバイナリ ファイル (ユーザー提供のイメージ ファイル) が読み込まれることを示しています。read メソッドが使用されています。通常このメソッドでは、1 度の呼び出しでファイル全体を配列に読み込むことになります。

例: byte 配列にバイナリ ファイルを読み込む
|| Get an input stream from a Url, read data into an array of bytes
|| report the number of bytes read.

{let results:TextFlowBox = {TextFlowBox}}

{let image-file-types:{Array-of FileDialogFilter} =
    {new
        {Array-of FileDialogFilter},
        {FileDialogFilter
            "Image files",
            {new
                {Array-of FileDialogTypeFilter},
                {FileDialogTypeFilter "bmp"},
                {FileDialogTypeFilter "jpg"},
                {FileDialogTypeFilter "jpeg"},
                {FileDialogTypeFilter "gif"},
                {FileDialogTypeFilter "png"}
            }
        }
    }
}

{paragraph
    Click the button below to choose an image file from
    your system. If you do not have one handy, there is
    a {monospace .gif} file located at {value
    {url "../../default/images/url-accessor-diagram.gif"}.name}
}

{VBox
    {CommandButton
        label="Choose an Image File",
        {on Action do
            {results.clear}
            let file-url:#Url = {choose-file filters=image-file-types}

            {if-non-null file-url then
                let my-input:#ByteInputStream
                {try
                    || Open the file
                    set my-input = {read-open-byte file-url}

                    || Read from the file. This usually will return the
                    || entire content of the file, if you are reading
                    || from the local file system.
                    let (buffer:#{Array-of byte}, count:int) = {my-input.read}
                    {results.add
                        {text Got {value count} bytes of data from the file.}
                    }

                 catch err:IOException do
                    {results.add
                        {text Error occurred while reading the file:
                            {italic {value err.value}}
                        }
                    }

                 finally {if-non-null my-input then {my-input.close}}
                }
            }
        }
    },
    results
}
配列に生バイナリ データとしてファイルを読み込む場合、ショートカット プロシージャ read-bytes-from を使用できます。詳細は『API リファレンス』の read-bytes-from エントリを参照してください。

バイナリ ファイルの書き込み

ファイルへのバイトの書き込みは、テキスト ファイルへの書き込みと似ています。ファイルにバイトを書き込むには、次を実行します。
  1. write-open-byte を使用して、書き込み用のファイルを開きます。
  2. write または write-one メソッドを使用してファイルにデータを書き込みます。
  3. リソースにすべてのデータが書き込まれたことを確認するには、flush メソッドを使用します。
  4. close メソッドを使用してファイルを閉じます。
注意: 書き込まれたファイルは、必ず適切に閉じてください。適切に閉じられていないと、データが失われる可能性があります。

ストリームのバッファ

要約:
  • 既定ではすべてのストリームがバッファに格納されます。
  • ほとんどの場合、ファイルを開くプロシージャでバッファ サイズを指定できます。
  • バイト ストリームをバッファに格納することを望まない場合もあります。
バッファ ストリームでは、リソースに対して読み取りおよび書き込みが行なわれるデータがメモリ内の一時バッファに格納されます。リソースへのアクセスによるオーバーヘッド (ディスクへのアクセス待機やネットワーク上のホスト待機など) が大きくなる場合が多いため、バッファを使用してプログラムを効率的に実行します。
既定では、ファイルを開くための通常のプロシージャ (read-openwrite-open-byte など) はすべてバッファ ストリームを返します。これらのすべてのメソッドは、ストリーム上で使用されるバッファ サイズを微調整するための buffer-size パラメータをオプションとして持っています。バッファ サイズはデータ量やデータを書き込む頻度に応じて選択できます。
多量のデータの読み取りまたは書き込みでは、バッファを使用するとリソースに直接書き込むより経費がかかる場合があります。この場合、バッファ サイズをゼロに設定してバイト ストリーム上のバッファを削除できます。

シーク可能ストリーム

要約:
  • シーク可能ストリームでは、ストリームの任意の位置にアクセスできます。
  • すべてのストリームがシーク可能ではありません。
  • ストリームがシーク可能かどうか判別するには isa Seekable を使用します。
  • 対応する Seekable にストリームをキャストする必要があります。
既定のストリーム クラスでは、ストリーム内の現在の位置でのみ読み取りまたは書き込みが可能です。たとえば、通常の入力ストリームでは先頭から読み始め、最後まで継続することしかできません。
シーク可能ストリームでは、ストリーム内の異なる位置で読み取りまたは書き込みができます。シーク可能ストリームを使用すると、ファイルからデータを抽出したり、ファイル内の特定の位置にデータを格納する必要があるようなファイルを構築できます。
シーク可能にできるのは、不連続にアクセスできるリソース対して出入力するストリームに限られます。ローカル ファイル システムのファイルは、内容全体を一度でアクセスできるのでシーク可能です。ソケットやファイルから HTTP やソケットを介して読み取るストリームは、最初から最後まで直線的に読み取るしかできないのでシーク可能ではありません。
ストリームがシーク可能かどうかを判断するには、次のように Seekable かどうかをテストします。
{if my-stream isa Seekable then
    || ... use the stream as a seekable
}
このテストに成功すれば、ストリームをシーク可能ストリームの適切なクラス (SeekableTextInputStream など) にキャストできます。使用可能なシーク可能クラスの一覧については、『API リファレンス マニュアル』で「シーク可能」を参照してください。

シーク可能メソッド

要約:
  • seek によりストリーム内を移動できます。
  • seek-style-supported? では、特定の位置を基準にして移動できることを確認します。
シーク可能ストリームはすべて Seekable から 3 つのメソッドを継承します。最も重要な Seekable.seek は、ストリーム内の別の位置に移動するのに使用するメソッドです。
{Seekable.seek offset:int64, from:SeekFrom}:int64
offset パラメータは、ストリーム内で移動する距離を表します。from パラメータは SeekFrom 列挙型のメンバで、offset の基準となるストリーム内の位置を示します。seek がサポートする位置の一覧については、『API リファレンス マニュアル』の SeekFrom エントリを参照してください。
ストリーム内の任意の位置にシークする前に、その位置からのシークをストリームがサポートしているかどうかを確認します。ストリームが SeekFrom に示されるすべての位置からのシークをサポートしていない場合があります。Seekable.seek-style-supported? メソッドを使用すると、ストリームが該当する位置からシーク可能かどうかを確認できます。
{Seekable.seek-style-supported? style:SeekStyle}:bool
このメソッドでは、パラメータとして SeekStyle 列挙型のメンバが必要です。この列挙では可能な組み合わせが多いため、SeekFrom の列挙とは異なります。style の値の一覧については、『API リファレンス マニュアル』の SeekStyle エントリを参照してください。

Seekable の使用例

次の例では、ストリームがシーク可能かどうかを判断し、シーク可能な場合にシーク可能メソッドを使用してストリーム内を移動する方法を示します。
注意: この例は、読み込み操作で character-encoding = "shift-jis" を使用します。 ユーザーが選択したファイルがこの文字エンコーディングと互換性がない場合は、エラーがスローされます。

例: シーク可能ストリームの使用
|| Demonstrate using a seekable stream.
{let results:VBox = {VBox}}

{let text-file-types:{Array-of FileDialogFilter} =
    {new
        {Array-of FileDialogFilter},
        {FileDialogFilter
            "Text files",
            {new
                {Array-of FileDialogTypeFilter},
                {FileDialogTypeFilter "txt"},
                {FileDialogTypeFilter "curl"}
            }}}}

{CommandButton
    label="Select a Text File",
    {on Action do
        let file-url:#Url = {choose-file filters=text-file-types}
        {results.clear}
        {if-non-null file-url then
            let my-input:#TextInputStream
            {try
                || Open the file
                let my-input = {read-open 
                                   character-encoding = "shift-jis",
                                   file-url}
                let count:int = 0
                || See if the file is seekable.
                {if my-input isa Seekable then

                    || Get a reference to the file that is an appropriate seekable
                    ||stream class.
                    let seek-input:SeekableTextInputStream =
                        (my-input asa SeekableTextInputStream)

                    || Use the tell method to determine where we are in the stream
                    {results.add
                        {text Current position is {seek-input.tell}}
                    }

                    || Ensure we can seek from the start of the stream.
                    {if {seek-input.seek-style-supported? "start"} then
                        || Jump 20 characters into the stream.
                        {seek-input.seek 20, "start"}

                        || Find out where we are now
                        {results.add
                            {text Now at {seek-input.tell}}
                        }

                        || Read next 10 characters
                        let buffer:StringBuf =
                            {seek-input.read-one-string n=10}
                        {results.add
                            {text The next 10 characters in the
                                stream are: {quote {pre {value buffer}}}
                            }
                        }
                        {results.add
                            {text After reading, we're at
                                {seek-input.tell}}
                        }
                    }

                    || See if it is safe to seek from the end of the stream
                    {if {seek-input.seek-style-supported? "end"} then
                        || move 10 characters from the end of the stream. Note
                        || that seeking from the end requires a negative offset.
                        {seek-input.seek -10, "end"}

                        || Find out where we are now
                        {results.add
                            {text After moving to the last 10 characters,
                                we're at {seek-input.tell}}
                        }
                        || Read next 10 characters
                        let buffer:StringBuf =
                            {seek-input.read-one-string n=10}
                        {results.add
                            {text The last 10 characters in the stream are:
                                {quote {pre {value buffer}}} (note that not all of
                                the characters read are printable).}
                        }
                    }

                    || Again, make sure it's OK to move relative to
                    || the start of the stream
                    {if {seek-input.seek-style-supported? "start"} then
                        || Move back to the start
                        {seek-input.seek 0, "start"}
                        || read the whole buffer into a StringBuf
                        let (buffer:StringBuf, numchars:int) =
                            {seek-input.read-one-string}

                        {results.add
                            {text The entire stream is:
                                {quote {pre {value buffer}}}
                                and is {value numchars} characters long.}}
                    }
                }

             catch err:IOException do
                {results.add
                    {text An error occurred while accessing the file:
                        {italic {value err.value}}
                    }
                }

             finally
                {if-non-null my-input then
                    {my-input.close}
                }
            }
        }
    }
}

{value results}

圧縮ストリーム

要約:
  • 出力用のバイナリ データを圧縮するには DeflateByteOutputStream クラスを使用します。
  • テキストは、トランスコード ストリームを使用して圧縮ストリームで処理できるバイトに変換すると圧縮可能になります。
  • 圧縮されたデータのストリームを読み取るには InflateByteInputStream クラスを使用します。
Curl 実行環境 は、圧縮データ ストリームに対する読み取りおよび書き込みをサポートしています。この機能を使用すると、ディスクへ書き込むファイル サイズを小さくしたり、データを圧縮して遅いネットワーク リンク上でより経済的にデータを転送することができます。
圧縮ストリームで使用される圧縮アルゴリズムは、DEFLATE 圧縮データ形式です。詳細については、『RFC 1951 DEFLATE Compressed Data Format Specification version 1.3』を参照してください。
2 つの圧縮ストリーム クラスがあります。1 つは入力ストリーム用 (InflateByteInputStream) 、もう 1 つは出力ストリーム用 (DeflateByteOutputStream) です。これらのクラスはパラメータとしてバイト ストリームのインスタンスを取り、これが圧縮ストリームの読み取りまたは書き込みを行う対象になります。
これらのクラスは CURL.IO.ZSTREAM パッケージの一部で、実行時のコア パッケージには含まれていません。したがって、アプレットで圧縮ストリームの読み取りおよび書き込みを可能にするには、このパッケージをインポートする必要があります。パッケージのインポートの詳細は、「import」を参照してください。
圧縮ファイルにデータを書き出すには、次を実行します。
  1. 圧縮データを格納するファイルに ByteOutputStream を開きます。
  2. DeflateByteOutputStream クラスのインスタンスを作成し、これに ByteOutputStream を渡します。
  3. DeflateByteOutputStream に圧縮されるデータを書き込みます。データが圧縮されて ByteOutputStream に送られ、ファイルに書き込まれます。
  4. データの書き込みが終了したら DeflateByteOutputStream.closeを呼び出して、圧縮ストリームと元のバイト ストリームの両方を閉じます。
テキストを圧縮するには、DeflateByteOutputStream ストリームを TranscodingTextOutputStream でラップします。トランスコード ストリームは、テキストをバイトに変換して DeflateByteOutputStream に送れるようにします。
圧縮ストリームの読み取りは、書き込みの逆に行なわれます。まず、圧縮データが格納されたファイルまたはその他のリソースから読み取りを行う ByteInputStream を作成します。次に、入力ストリームから読み取りを行う InflateByteInputStream を作成します。圧縮されたテキストの読み取りでは、TranscodingTextInputStream を作成して InflateByteInputStream から読み取り、バイナリ データをテキストに変換します。
次の例では、選択するファイルに TextArea の圧縮データを書き込んだ後で、圧縮データを再度読み取ります。最初はデータが圧縮されたことを示すために通常のバイト ストリームとして、次に圧縮されたテキスト データを読み取るために圧縮ストリームを使用します。

例: 圧縮ストリームの読み取りと書き込み
|| Import the package containing the compressed streams
{import * from CURL.IO.ZSTREAM}


{let output-area:VBox = {VBox}}

{let my-text:TextArea = {TextArea
                            value="This is some text that the " &
                            "compressed stream will write. " &
                            "It repeats in order to make the text " &
                            "more compressible. " &
                            "This is some text that the " &
                            "compressed stream will write. " &
                            "It repeats in order to make the text " &
                            "more compressible. "
                        }
}

{VBox
    width=5in,
    my-text,
    output-area,
    {CommandButton
        label="Choose & Write File",
        {on Action do
            {output-area.clear}
            || ask user for a file
            let target:#Url = {choose-file
                                  style=FileDialogStyle.save-as,
                                  title="Demonstrate Compressed Streams"
                              }

            {if-non-null target then
                let output-stream:#TranscodingTextOutputStream

                {try
                    || Open the file for writing, connecting the output
                    || stream to a compressed stream, then connecting the
                    || compressed stream to a stream that will turn
                    || ASCII text into bytes.
                    set output-stream = {TranscodingTextOutputStream
                                            {DeflateByteOutputStream
                                                {write-open-byte target}
                                            },
                                            CharEncoding.ascii,
                                            true
                                        }

                    || Write the string.
                    let output-size:int =
                        {output-stream.write-one-string my-text.value}

                    {output-area.add
                        {text Wrote the string:
                            {italic {value my-text.value}}
                            which was {value output-size} characters
                            long.
                        }
                    }
                 catch e:IOException do
                    {output-area.add
                        {text color="red", An error occurred while
                            accessing the file: {italic {value e.value}}
                        }
                    }
                 finally
                    || Close the topmost stream, which will flush &
                    || close all of the underlying streams as well.
                    {if-non-null output-stream then
                        {output-stream.close}
                    }
                }
                {try
                    || Read file back in to see how large it is
                    let (bytes:{Array-of byte}, read-size:int) =
                        {read-bytes-from (target asa Url)}

                    {output-area.add
                        {text The compressed file size is:
                            {value read-size}
                        }
                    }

                 catch e:IOException do
                    {output-area.add
                        {text color="red", An error occurred while
                            accessing the file: {italic {value e.value}}
                        }
                    }
                }
                || Now let's read the file back in and decompress it.
                let input-stream:#TranscodingTextInputStream
                {try
                    set input-stream = {TranscodingTextInputStream
                                           {InflateByteInputStream
                                               {read-open-byte target}
                                           },
                                           character-encoding=CharEncoding.ascii
                                       }

                    let (input-buf:{Array-of char}, in-count:int) = {input-stream.read}
                    {output-area.add
                        {text
                            Read {value in-count} characters back from the
                            compressed file.
                        }
                    }
                 catch err:IOException do
                    {output-area.add
                        {text color="red", Problem reading compressed file:
                            {value err.value}
                        }
                    }
                 finally
                    {if-non-null input-stream then
                        {input-stream.close}
                    }
                }
            }
        }
    }
}

シリアル化

Curl Serialization API は、CURL.IO.SERIALIZE パッケージで定義されています。この API を使用すると、値を SerializeOutputStream に書き込んでバイト ストリームに変換したり、値を SerializeInputStream から読み取ってバイトストリームを値に変換したりできます。ほとんどの Curl データ型と、参照 (ポインタ) などの任意の Curl データ構造の読み取りと書き込みが可能です。
バージョン 6.0 の Curl API はシリアル化のインターフェースに対し、数々の拡張を提供します。以下のリストでは変更点を要約しています。
次の例では、11 個の整数をシリアル化してファイルに書き込み、その後 deserializeプリミティブを使用してそれらの値を読み取り、シリアル化を解除しています。

例: 整数のシリアル化
{import * from CURL.IO.SERIALIZE}
{let stored-values:HBox = {HBox spacing = 5pt}}
{let read-ints:int}
{define-proc {save-and-restore target:Url}:void
    {with-open-streams
        out = {SerializeOutputStream {write-open-byte target}}
     do
        {for i:int=0 to 10 do
            {out.write-one i}
        }
    }
    {stored-values.clear}
    {with-open-streams
        in = {SerializeInputStream {read-open-byte target}}
     do
        {stored-values.add {TextFlowBox width = 3cm, "stored integers:"}}
        {for i:int=0 to 10 do
            set read-ints = {deserialize in, int}    
            {stored-values.add read-ints}
        }
    }
}
{VBox
    {CommandButton
        label="Select a File",
        {on Action do
            let target:#Url = {choose-file
                                  style = FileDialogStyle.save-as,
                                  title = "Demonstrate Serialization"
                              }
            {if-non-null target then {save-and-restore target}}
        }
    },
    stored-values
}
次の例では、10 個の整数の配列をシリアル化し、その後シリアル化を解除しています。配列全体が 1 つのデータ オブジェクトとして処理されていることに注意してください。

例: 配列のシリアル化
{import * from CURL.IO.SERIALIZE}
{let stored-values:HBox = {HBox spacing = 5pt}}
{let arr:{Array-of int} = {{Array-of int} 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}
{let read-arr:{Array-of int} = {new {Array-of int}}}
{define-proc {save-and-restore target:Url}:void
    {with-open-streams
        out = {SerializeOutputStream {write-open-byte target}}
     do
        {out.write-one arr}
    }
    {stored-values.clear}
    {with-open-streams
        in = {SerializeInputStream {read-open-byte target}}
     do
        set read-arr = {deserialize in, {Array-of int}}
        {stored-values.add {TextFlowBox width = 3cm, "stored array:"}}
        {for x:int in read-arr do
            {stored-values.add x}
        }
    }

}

{VBox
    {CommandButton
        label="Select a File",
        {on Action do
            let target:#Url = {choose-file
                                  style = FileDialogStyle.save-as,
                                  title = "Demonstrate Serialization"
                              }
            {if-non-null target then {save-and-restore target}}
        }
    },
    stored-values
}
                                                                                             

シリアル化可能なクラスの定義

ユーザー独自のシリアル化可能なクラスを定義すると、複雑な Curl データの保存と復元が可能になります。シリアル化可能なクラスを示すには、serializable 修飾子を使用します。シリアル化可能なクラスは、すべてのシリアル化可能なスーパークラスと、transient 修飾子で一時的として示されていないすべてのフィールドを書き込んだり復元したりします。
シリアル化可能なクラスの定義にはいくつかの制限があります。 object-serializeobject-deserialize についての詳細は、以下および CURL.IO.SERIALIZE パッケージのドキュメントを参照してください。
次のコード サンプルは、文字列 (name) と整数 (score) を含む単純なシリアル化可能クラスを示しています。最高得点者の名前と得点を保存するゲームのユース ケースをシミュレーションします。得点が格納されているファイルが存在する場合は、アプレットによってシリアル化可能クラス HighScore のインスタンスが復元され、復元された得点と新しい高得点が比較されます。新しい得点のほうが高ければ、そのクラスがファイルに書き込まれます。動作例を作成するには、このコードをアプレットにコピーして、[...] を有効な URL に置き換えます。
{curl 8.0 applet}

{import * from CURL.IO.SERIALIZE}

|| A class representing a "high score".
{define-class public serializable HighScore

  field constant name:String
  field constant score:int

  {constructor {default name:String, score:int}
    set self.name = name
    set self.score = score
  }
}
|| NOTE: Replace "[...]" below with a valid URL.
{let high-score-url:Url = [...]}

|| Start with "no" high score.
{let high-score:HighScore = {HighScore "nobody", 0}}

{try
    || Read any old high score.
    {with-open-streams
        in = {SerializeInputStream {read-open-byte high-score-url}}
     do
        set high-score = {deserialize in, HighScore}
    }
 catch e:MissingFileException do
    || The file may not exist yet.
}

|| NOTE: An actual game should be played here, but for this example,
|| we will just assume that a player named "somebody" beat the previous
|| high score by ten points.
{let name:String = "somebody"}
{let score:int = high-score.score + 10}

{if score > high-score.score then
    || Remember the new high score.
    set high-score = {HighScore name, score}
    || Save the new high score.
    {with-open-streams
        out = {SerializeOutputStream {write-open-byte high-score-url}}
     do
        {out.write-one high-score}
    }
}
次のコード サンプルでは、前のサンプルで定義した HighScore オブジェクトの配列を保存したり復元したりします。HighScore オブジェクトの配列全体が 1 つのデータ オブジェクトとして処理されていることに注意してください。動作例を作成するには、このコードをアプレットにコピーして、[...] を有効な URL に置き換えます。
{curl 8.0 applet}

{import * from CURL.IO.SERIALIZE}

|| A class representing a "high score".
{define-class public serializable HighScore

  field constant name:String
  field constant score:int

  {constructor {default name:String, score:int}
    set self.name = name
    set self.score = score
  }
}

|| A class representing a "high score list".
{define-class public serializable HighScoreList

  field constant high-scores:{Array-of HighScore}

  || Add a new high score, and keep the top ten, sorted.
  {method {add high-score:HighScore}:void
    {self.high-scores.append high-score}
    {self.high-scores.sort
        comparison-proc =
            {proc {hs1:HighScore, hs2:HighScore}:bool
                {return hs1.score >= hs2.score}
            }
    }
    {if self.high-scores.size > 10 then
        {self.high-scores.remove 10, length = self.high-scores.size - 10}
    }
  }

  {constructor {default}
    set self.high-scores = {new {Array-of HighScore}}
  }
}
|| NOTE: Replace "[...]" below with a valid URL.
{let high-score-list-url:Url = [...]}

|| The high score list.
{let high-score-list:HighScoreList = {HighScoreList}}

{try
    || Read the high score list, if it exists.
    {with-open-streams
        in = {SerializeInputStream {read-open-byte high-score-list-url}}
     do
        set high-score-list = {deserialize in, HighScoreList}
    }
 catch e:MissingFileException do
    || The file may not exist yet.
}

|| NOTE: An actual game should be played here, but for this example, we
|| will just assume that a player named "somebody" got a random score.
|| random number of points.
{let random:Random = {Random}}
{let score:int = {random.next-in-range 0, 999999}}

|| Add the new high score.
{high-score-list.add {HighScore "somebody", score}}

|| Save the high score list.
{with-open-streams
    out = {SerializeOutputStream {write-open-byte high-score-list-url}}
 do
    {out.write-one high-score-list}
}

シリアル化可能クラスのバージョン

serializable クラスによって保存されたデータを Curl アプリケーションの新しいリリースで変更する場合は、define-serialization を使用して、新しいクラス定義でクラス バージョン番号にゼロより大きい整数を指定する必要があります。object-serialize メソッドは、クラス バージョンをシリアル化されたデータ ストリームの一部として書き込みます。既定のクラス バージョン番号はゼロ (0) です。
変更したクラスでも、着信データのクラス バージョンをチェックして適切に動作する object-deserialize コンストラクタを指定する必要があります。次の例では、when というフィールドを追加して、高得点が出た日付と時刻を格納します。
{define-class public serializable HighScore

  field constant name:String
  field constant score:int
  field constant when:DateTime

  {define-serialization class-version = 1}

  {constructor public {object-deserialize in:SerializeInputStream}
    let cv:int = {in.read-class-version}
    set self.name = {deserialize in, String}
    set self.score = {deserialize in, int}
    {switch cv
     case 0 do
        set self.when = {DateTime}
     case 1 do
        set self.when = {deserialize in, DateTime}
     else
        {error "Unknown HighScore class-version ", cv, "."}
    }
  }

  {constructor {default name:String, score:int}
    set self.name = name
    set self.score = score
    set self.when = {DateTime}
  }
}
このコード行では、クラス バージョンを 1 に設定します。
{define-serialization class-version = 1}
object-deserialize コンストラクタは、まず着信データのクラス バージョンをチェックします。
let cv:int = {in.read-class-version}
次に、name および score フィールドに着信データを設定します。
set self.name = {deserialize in, String}
set self.score = {deserialize in, int}
そしてクラス バージョンを調べて、着信データのクラス バージョンが 1 であれば when フィールドにそのデータを設定します。データのクラス バージョンが 0 の場合は、着信データに日付情報は含まれていないため、アプレットは現在の日付と時刻を使用します。
{switch cv
 case 0 do
    set self.when = {DateTime}
 case 1 do
    set self.when = {deserialize in, DateTime}
 else
    {error "Unknown HighScore class-version ", cv, "."}
}
                                                                                                                                                          

シリアル化可能な型

以下に示すデータ型のオブジェクトはシリアル化できます。