テキスト ファイルの読み取りと書き込み

要約:
  • テキストの読み取りと書き込みは、リソースへのアクセスで最も一般的なタスクです。
  • 読み取りと書き込みは、いずれも基本ステップは同じです。
テキストの読み取りと書き込みは、リソースへのアクセスでもっとも一般的なタスクです。たとえば、アプリケーションでエンド ユーザーが作業しているファイルをディスクに保存したり、Web ページを読み取って情報を取り出すことが必要になることがあります。
リソースとの間で読み取りや書き込みを行うプロセスは次の手順に従います。
  1. リソースの URL を使用して Url オブジェクトをインスタンス化することによりリソースを識別します。File のような特定のオブジェクトを使用することもできます。
  2. ストリーム オブジェクトをインスタンス化して、リソースへの入力または出力ストリームを開きます。
  3. ストリーム オブジェクトで、メソッドを使用してリソースからデータを読み取るか、または、リソースにデータを書き込みます。
  4. 読み取りまたは書き込みを完了したら、ストリーム オブジェクトの close メソッドを呼び出してストリームを閉じます。
ストリームの詳細については、「ストリーム」セクションを参照してください。

テキスト ファイルの読み取り

要約:
  • テキスト ファイルは TextInputStream オブジェクトを介して読み取ります。
  • read-open を使用してテキスト ストリームをインスタンス化します。
  • read-open では常に try ブロックを使用します。
  • TextInputStream オブジェクトのメソッドを使用してデータを読み取ります。
  • これらのメソッドはデータと、読み取るデータが他にあるかどうかを知らせる bool を返します。
  • close メソッドを使用してストリームを閉じます。
  • read-from を使用してファイル全体を StringBuf に読み取ります。
  • テキスト ストリームは 1 文字ずつ読み取ることができます。
リソースから読み取るための簡単な方法は、リソースをテキスト ファイルとして開くことです。Web ページ、ASCII (American Standard Code for Information Interchange) テキスト データ ファイル、および構成ファイルなど多くのリソースをこの方法で読み取ることができます。次のセクションでは、テキスト ファイルを開く、読み取る、および閉じる方法について説明し、それらの手順を実例にまとめています。

テキスト ファイルを開く

テキスト ファイルから読み取るには、TextInputStream オブジェクトを作成する必要があります。このオブジェクトはストリーム クラスの 1 つで、リソースからのデータのシーケンスを表します。ストリームはデータ リソースに一般的なインターフェイスを提供します。ディスク上のファイルから来るストリームを読み取るのは、ネットワークまたはその他のソース上を移動しているストリームから読み取るのと同じです。
TextInputStream オブジェクトをインスタンス化する簡単な方法は、read-open プロシージャを使用することです。開こうとしているリソースを識別する Url オブジェクトにこのプロシージャを渡すと、TextInputStream が返されます。
Url オブジェクトを持っていても、識別されるリソースが存在すること、またはそのリソースにアクセスできることは保証されないことに注意してください。存在しないリソースを表す read-openUrl に渡すと、MissingFileException がスローされます。常に read-open プロシージャ コールを try ブロックに置き、例外を処理できるようにします。
{try
    || Instantiate the input stream from results of read-open
    let intext:TextInputStream =
        {read-open {url "http://www.example.com/somefile.txt"}}
 catch err:HttpPermissionDeniedFileException do
    || Oops! Handle problem opening the file
}
|| Read from the file here

ファイルのアクセス中にスローされる例外

Url からストリームを開こうとする際に、スローされる可能性のある様々な例外を以下に示します。
例外説明
IOExceptionその他のファイルに関連する例外の継承元になる高レベルの例外です。これが直接スローされることはまれです。
HttpExceptionこの高レベルの例外は、いくつかのその他の例外クラスの親になっています。それらの例外クラスはすべて、HTTP プロトコルを使用してファイル (Web サイトの任意のファイル) へアクセス中に問題が発生した場合にスローされます。
FileExceptionこれはトップレベルのファイル特有の例外です。この例外は IOException から継承し、ファイルに関連するその他の例外はすべてこの例外から継承します。この特定の例外は、例外的な環境でスローされることがあります。
KeyNotFoundException実際には、ファイル関連の例外ではありません。curl: スキーム (たとえば、curl://bogus-curl-directory など) のトップレベルにある無効な URL 、または、他の ObjectDirectory を解決しようと試みることによってこれがスローされる場合があります。
MissingFileException および HttpMissingFileExceptionこれらの例外は、存在しないファイルにアクセスしよう試みた場合に常にスローされます。
ExistingFileExceptionこの例外は、既に存在するファイルを作成しようと試み、メソッドまたはプロシージャの error-if-exists? パラメータを false に設定していない場合に常にスローされます。
PermissionDeniedFileException および HttpPermissionDeniedFileExceptionこれらの例外は、アクセスしようとするファイルに対し、プログラムに読み書きのための十分なアクセス権が無い場合にスローされます。
SecurityExceptionこの例外は、アプレットがファイルにアクセスする際に、セキュリティ制限に違反した場合にスローされます。実行環境のセキュリティ制限についての詳細な説明は、「セキュリティ」セクションを参照してください。
例外処理の詳細については、「例外」セクションを参照してください。

入力ストリームからの読み取り

TextInputStream オブジェクトを取得したら、そのメソッドの 1 つを使用してデータを読み取ることができます。メソッドによって読み取るデータの量が異なります。一度に 1 文字だけ読み取るものがある一方、1 行ごとまたは複数行を一度に読み取るものもあります。使用できるメソッドには次のものがあります。
メソッド{t:TextInputStream.read-line buf:#StringBuf=null, newline-sequence:NewlineSequence=NewlineSequence.all}:#StringBuf
説明ストリームからテキストを 1 行取得します。次の新しい改行文字とそこまでのストリーム内のすべての文字が文字列に返されます。eol-sequence を提供することにより、行を終了する文字を定義することができます。
メソッド{t:TextInputStream.read-one}:{return val:char, eof?:bool}
説明入力ストリームから文字を一つ取得します。
メソッド{t:TextInputStream.read-one-string buf:#StringBuf=null, append?:bool=false, n:int={if append? and buf != null then max-int - buf.size else max-int }}:{return buf:StringBuf, n:int}
説明入力ストリームから bufn 個の文字を読み取ります。n が指定されていない場合、ファイル全体が buf に読み込まれます。
メソッド{t:TextInputStream.read out:#{Array-of char}=null, start:int=0, n:int=max-int - start, allow-short-read?:bool=false, non-blocking?:bool=false}:{return vals:{Array-of char}, n:int}
説明ストリームから n 個の文字を取得します。
使用できるメソッドの詳細なリストについては、『API リファレンス』の TextInputStream の項目を参照してください。
ほとんどの場合、テキスト ファイルの最後まで読み取ります。データの読み取りに使用するメソッドのほとんどは、ストリームがファイルの最終に達したかどうかを示す bool 値を返します。TextInputStream.end-of-stream? アクセッサを使用して、ストリーム内に他に読み取るものがあるかどうかを判断することもできます。

入力ストリームを閉じる

データの読み取りが終了したら、TextInputStream.close を呼び出してストリームをシャットダウンし、使用中のリソースをクリーン アップする必要があります。ストリームを閉じないと、開いているファイル ハンドラが過剰に存在することになります。
ファイルからの読み取りに問題がある場合でも、ストリームが閉じたことを確認する必要があります。閉じたことを確認する最良の方法は、ストリームでのすべての読み取り操作を取り巻く try ブロックの finally 部分で閉じるための呼び出しを行うことです。

集約

次の例は、ファイルを開いて TextFlowBox に読み取る方法を示しています。
注意: この例は、読み込み操作に character-encoding = "shift-jis" を使用します。 ユーザーが選択したファイルがこの文字エンコーディングと互換性がない場合は、エラーがスローされます。

例: テキスト ファイルの読み取り
|| Get an input stream from a Url, dump the text read
|| from stream into a TextFlowBox
{paragraph
    Please click the button below and select a text file to read
    in. There is a text file located at
    {{url "curl://install/docs/default/support/example.txt"}.canonicalize}
    if you cannot find one.
}

{let buffer:TextFlowBox =
    {TextFlowBox width=5in, border-width=1pt,
        border-color="black", margin=2pt
    }
}
{VBox
    buffer,
    {CommandButton
        label="Select File to Read",
        {on Action do
            {buffer.clear} || Empty any contents of display area
            let file-url:#Url = {choose-file}
            {if-non-null file-url then
                let myinput:#TextInputStream
                {try || First we open the file
                     set myinput={read-open 
                                     character-encoding = "shift-jis",
                                     file-url}
                 catch err:IOException do
                    || Problem opening the file. Just error out.
                    {buffer.add {text A problem occurred while opening the file:
                                    {value err.message}
                                }
                    }
                }

                || a separate try block for reading from the stream
                || First, check if the open operation failed by leaving
                || the input stream null.
                {if-non-null myinput then
                    {try
                        || Keep getting lines until there's no more
                        {until myinput.end-of-stream? do

                            || Get a line from the file, add it to the TextFlowBox
                            {buffer.add {myinput.read-line}}}

                     catch err:IOException do
                        {error
                            "A problem occurred while reading the file: "
                            & err.message
                        }

                     finally
                        || ensure the stream is closed
                        {myinput.close}
                    }
                }
            }
        }
    }
}

ファイル全体を文字列に読み取る

前の例では、ファイルを行ごとに読み取る方法を示しました。次の行を読み取る前に各行をいくつかの方法で処理する場合には優れた方法です。ファイルの内容全体をメモリに取得する場合、read-from プロシージャを使用してテキスト ファイルの内容を一括プロシージャ コールで StringBuf に読み取ることができます。read-from プロシージャは、読み取ったファイルが格納されている StringBuf、および読み取った文字数を返します。詳細については、『API リファレンス マニュアル』の read-from の項目を参照してください。
注意: この例は、読み込み操作に character-encoding = "shift-jis" を使用します。 ユーザーが選択したファイルがこの文字エンコーディングと互換性がない場合は、エラーがスローされます。

例: ファイルを文字列に読み取る
{paragraph
    Please click the button below and select a text file to read
    in. There is a text file located at
    {{url "curl://install/docs/default/support/example.txt"}.canonicalize}
    if you cannot find any others.
}

{let result-frame:Frame={Frame border-color="black", border-width=1pt,
                            {Fill}
                        }
}

{VBox halign="right",
    result-frame,
    {CommandButton
        label="Choose File to Read",
        {on Action do
            let file-url:#Url = {choose-file}
            {if-non-null file-url then
                {try
                    let (my-buf:StringBuf, numread:int) =
                        {read-from 
                            character-encoding = "shift-jis",
                            file-url}
                    {result-frame.add
                        {text Read {value numread} characters: {value my-buf}},
                        replace?=true
                    }
                 catch err:IOException do
                    {result-frame.add {text Error!: {value err.message}}, replace?=true}
                }
            }
        }
    }
}

文字ごとの読み取り

TextInputStream を連続する文字として読み取ることもできます。TextInputStream.read-one メソッドは、char および読み取る文字が終わったかどうかを示す bool を返します。(通常は、単に読み取るものがあるかどうかを判別する場合は、TextInputStream.end-of-stream? を使用し、実際に読み取りを行っていつ停止するかを判別する場合は、TextInputStream.read-one の第 2 戻り値を使用します。)
TextInputStream の他のメソッドの中には、一度に 1 文字を入力する場合に便利なものがあります。
メソッド説明
{t:TextInputStream.peek-one}:{return char, eof?:bool}入力ストリームから削除せずに次の読み取る文字を返します。入力ストリーム内での位置が前に進まないことを除けば、事実上 read-one と同じです。
{t:TextInputStream.unread-one e:char}:voide を入力ストリームに挿入します。これは、eがストリームから読み取られる次の文字であることを意味します。
次に入力ストリームから 1 文字ごとに読み取る例を示します。
注意: この例は、読み込み操作に character-encoding = "shift-jis" を使用します。 ユーザーが選択したファイルがこの文字エンコーディングと互換性がない場合は、エラーがスローされます。

例: 入力ストリームから読み取る
{value
    let result:TextFlowBox = {TextFlowBox width=4in}

    {VBox halign="right",
        result,
        {CommandButton
            label="Select File",
            {on Action do
                {result.clear}
                let myfile:#Url = {choose-file}
                {if-non-null myfile then
                    let instream:#TextInputStream
                    {try
                        set instream = 
                            {read-open
                                character-encoding = "shift-jis",
                                myfile}
                     catch err:IOException do
                        {result.add {text There was a problem opening
                                        the file: {value err.message}
                                    }
                        }
                    }

                    {if-non-null instream then
                        let no-more?:bool = false
                        let c:char
                        let num-chars:int = 0

                        {try || reading from the file

                            {result.add {paragraph The first ten characters of the input are:}}
                            {for n = 1 to 10 do
                                set (c, no-more?) = {instream.read-one}
                                {if no-more? then
                                    {break}
                                 else
                                    {result.add c}
                                    set num-chars = num-chars + 1}}
                            {if no-more? then
                                {result.add
                                    {paragraph Oops! ran out of character before reaching 10!}
                                }
                            }
                            {result.add
                                {paragraph Read {value num-chars} characters in total.}
                            }

                         catch err:IOException do
                            {result.add
                                {paragraph
                                    A problem occurred while reading the
                                    file: {italic {value err.message}}
                                }
                            }
                         finally
                            {instream.close}
                        }
                    }
                }
            }
        }
    }
}

テキスト ファイルへの書き込み

要約:
  • テキスト ファイルの書き込みは読み取りと似ています。
  • write-open を使用して出力ストリームを開きます。
  • write-one メソッドを使用して 1 文字を書き込みます。
  • write を使用して配列から書き込みます。
  • write-one-string を使用して String を書き込みます。
  • flush を使用してストリーム内の出力をフラッシュします。
  • close メソッドを使用してストリームを閉じます。
  • ストリームを閉じないと結果的にデータが失われることがあります。
  • write-to を使用して文字列を 1 つの手順でファイルに書き込みます。
テキスト ファイルに書き込む手順は読み取りと似ています。ストリームを開き、ストリームでメソッドを使用し、ストリームを閉じるという同じ処理を行います。
ただし、TextInputStream の代わりに、データの書き込みに使用する TextOutputStream オブジェクトをインスタンス化する必要があります。出力ストリームは入力ストリームの逆です。リソースに送るデータ (この場合は、テキスト ファイルに保存するデータ) をストリーム オブジェクトのメソッドの 1 つを使用してそのストリーム オブジェクトに書き込みます。
TextOutputStream をインスタンス化する簡単な方法は、write-open プロシージャを使用することです。書き込み先のリソース (通常、ファイル システム内のファイル) を識別する Url をこのプロシージャに渡すと、そのリソースに接続される TextOutputStream オブジェクトが返されます。write-open の呼び出しは、書き込み先のリソースを開こうとする場合にスローされる例外をキャッチするため、try ブロック内にある必要があります。ファイルにアクセスしている間に発生する可能性のある「例外」のリストを参照して、どれを取得するのかを判断してください。
例外処理の詳細については、「例外」セクションを参照してください。

ストリームへの書き込み

TextOutputStream オブジェクトを取得したら、そのメソッドの 1 つを使用してデータをリソースに書き込みます。これらのメソッドには次のものがあります。
メソッド{s:TextOutputStream.write-one obj:char}:void
説明obj 1 文字を出力ストリームに書き込みます。
メソッド{s:TextOutputStream.write data:{Array-of char}, start:int=0, n:int=data.size - start, allow-short-write?:bool=false, non-blocking?:bool=false}:int
説明
文字の配列の一部を出力ストリームに書き込みます。data 配列は書き込む文字を保持し、start は配列内の最初の書き込む文字を示し、n は書き込む文字数を示します。既定では、このメソッドは配列内にある文字のすべてを出力に書き込みます。このメソッドは、実際に出力ストリームに書き込む文字数を返します。
オプション パラメータの allow-short-write?true の場合、メソッドは n 個未満の文字を出力に書き込むことができます。non-blocking?true の場合、メソッドは、出力ストリームがデータを受け取る準備ができるまで待つ代わりに、準備ができているかどうかを返します。これらの 2 つのオプションは通常ネットワーク接続にわたるデータ ストリームに使用されるだけです。
メソッド{s:TextOutputStream.write-one-string str:StringInterface, start:int=0, length:int=str.size - start}:int
説明StringInterfacestr を出力ストリームに書き込みます。既定では、str のすべてを書き込みます。ただし、start パラメータを使用して、送信する str の開始文字、および書き込む文字数を判断する length を指示することができます。
使用できる出力メソッドの詳細なリストについては、『API リファレンス』のTextOutputStreamエントリを参照してください。

出力のフラッシュ

ストリームにデータを書き込んだら、TextOutputStream.flush メソッドを呼び出して、ストリームのキューに挿入されたすべてのデータが実際に送信されたかどうかを確認するのは良い方法です。

ストリームを閉じる

すべてのデータをリソースに書き込んだら、TextOutputStream.close メソッドを呼び出してストリームを閉じる必要があります。これにより、書き出されていないデータがストリームからフラッシュされ、リソースが正しく閉じられたことが確実になります。
注意: 書き込み中のストリームを忘れずに閉じることは非常に重要です。それらを閉じ忘れると、ストリームを介して送信されないデータが生じることがあります。その結果、データがファイルに書き込まれないことになります。入力ストリームを閉じるのと同様に、出力ストリームを閉じるもっとも確実な方法は、try ブロックの finally 部分でデータ書き込み式を囲むことです。

集約

次の例で、いくつかの文字列をファイルに書き込む処理を示します。テキスト フィールドにテキストを入力し、[Write It]をクリックしてテキストをディスクに書き込みます。この例による書き込みを許可するファイルを選択してください。

例: ファイルへの書き込み
{let input:TextArea =
    {TextArea width=3in, height=1in, value="Type something here"}}
{let results:Frame = {Frame}}

{VBox halign="right",
    {spaced-hbox valign="top", {text Text to write:}, input},
    results,
    {CommandButton
        label="Write It",
        {on Action do
            || Get file to write to
            let out-file:#Url =
                {choose-file
                    style=FileDialogStyle.save-as,
                    title="Enter a File Write To"
                }
            {if-non-null out-file then
                let out:#TextOutputStream
                {try
                    || Open the output stream. If the file already exists,
                    || throw an exception.
                    set out = {write-open out-file, error-if-exists?=false}
                 catch err:IOException do
                    {popup-message
                        {text Oops. Error opening file:
                            {italic {value err.message}}
                        }
                    }
                }

                || Now write to the stream.
                {if-non-null out then
                    {try
                        let numout:int = {out.write-one-string input.value & "\n"}
                        {out.flush} || not strictly necessary, since close
                        || does a flush automatically.

                        {results.add
                            {text Wrote {value numout} characters
                                to {value out-file.local-filename}.},
                            replace?=true
                        }

                     catch err:IOException do
                        {results.add
                            {text color="red", A problem
                                occurred while writing the file:
                                {italic {value err.message}}},
                            replace?=true
                        }
                     finally || Be sure the file gets closed
                        {out.close}

                    } || try
                } || if-non-null
            } || if-non-null
        } || on Action
    } || CommandButton
}

文字列を一度にファイルに書き込む

一度にファイル全体を StringBuf に読み取る read-from プロシージャと同様に、write-to プロシージャは、ファイルを開いて StringInterface の内容を書き込み、ストリームをフラッシュしてからストリームを閉じます。ファイルに 1 文字だけ書き込む場合に便利です。詳細については、『API リファレンス』の項目 write-to を参照してください。
次の例は、基本的には前のものと同じですが、write-to プロシージャを使用して書き込み処理を簡単にしています。

例: ファイルを一度に書き込む
{let input:TextArea =
    {TextArea width=3in, height=1in, value="Type something here"}}
{let results:Frame = {Frame}}

{VBox halign="right",
    {spaced-hbox valign="top", {text Text to write:}, input},
    results,
    {CommandButton
        label="Write It",
        {on Action do
            || Get file to write to
            let out-file:#Url =
                {choose-file
                    style=FileDialogStyle.save-as,
                    title="Enter a File Write To"
                }
            {if-non-null out-file then
                {try
                    {write-to out-file, input.value & "\n"}
                    {results.add
                        {text Wrote to {value out-file.local-filename}.},
                        replace?=true
                    }

                 catch err:IOException do
                    {results.add
                        {text color="red", A problem
                            occurred while writing the file:
                            {italic {value err.message}}},
                        replace?=true
                    }

                } || try
            } || if-non-null
        } || on Action
    } || CommandButton
}

既存のファイルへの追加

要約:
  • 既存のファイルの最後にテキストを追加します。
  • append-open または append-to を使用して追加します。
標準 write-open プロシージャは、出力先を開き、既存のファイルを削除します。場合によっては、ファイルの古い内容を保持したまま、ログ ファイルなどの新しい項目を最後に追加したいことがあります。
ファイルを追加で開くには、append-open プロシージャを使用します。このプロシージャは write-open と同じですが、出力ストリームに書き込んだデータがファイルの最後に追加される点が異なります。
ファイルに 1 文字だけ追加するには、append-to プロシージャを使用します。read-from およびwrite-to のように、このプロシージャは、ファイルへの追加手順のすべてを 1 つのファンクション呼び出しで行います。詳細は、『開発者リファレンス』の項目 append-to を参照してください。

改行フィルタリング

異なるコンピュータ オペレーティング システムは、異なる規則を使用してテキスト ファイルに行末をマークします。たとえば、Windows® システムは、\r\n (キャリッジ リターンと改行文字) を使用して行末をマークします。一方、Linux システムは、\n (改行文字) を使用します。これらの違いを扱うために、CURL.IO.STREAM パッケージはさまざまな行末規則を認識する方法を提供し、必要に応じて行末規則を変換します。
列挙型 NewlineSequence により、行末を示す方法を指定することができます。たとえば、メソッド TextInputStream.read-line は、NewlineSequence 引数を受け取ります。これは、入力において行末を識別するために使用される規則を指定します。既定では、read-line はすべての規則を認識します。
また、API は、1 つの行末規則から他の行末規則に変換するこれらのクラスを提供します。
入力ストリーム クラスは、入力された指定の改行シーケンスを Curl 規則 \n へ変換します。既定では、すべての改行シーケンスが変換されます。この変換により、ソース テキストで使用される規則に関係なく、ストリームから読み込まれるテキスト内で一貫した行末規則を使用することが可能になります。
出力ストリーム クラスは、Curl 規則 \n から、指定された改行シーケンスに変換します。値 NewlineSequence.all は、出力では有効ではありません。この変換により、特定のターゲット システムに対する適切な行末シーケンスを含めることができます。NewlineSequence.host-default 値がどのような行末シーケンスを使用していても、それがCurl アプレットを実行しているホスト マシン上での標準となることに注意してください。
これらのクラスは、対応するテキスト入出力ストリームの周囲でラッパーとして動作します。フィルタ クラスのコンストラクタに対して適切な型のストリームを指定する必要があります。
次の例では、ホスト システムに適切な改行を使用するため、前述の例ファイルへの書き込みを変更します。このコードは、NewlineFilterTextOutputStream ラッパー out-newlineTextOutputStream out をラップすることに注意してください。
let out-newline:NewlineFilterTextOutputStream = 
    {NewlineFilterTextOutputStream
        out,
        NewlineSequence.host-default
    }   
テキストを書き込むために呼び出されるメソッドは、out-newline のメソッドです。

例: ホスト既定 EOL ファイルへの書き込み
{let input:TextArea =
    {TextArea width=3in, height=1in, value="Type something here"}}
{let results:Frame = {Frame}}

{VBox halign="right",
    {spaced-hbox valign="top", {text Text to write:}, input},
    results,
    {CommandButton
        label="Write It",
        {on Action do
            || Get file to write to
            let out-file:#Url =
                {choose-file
                    style=FileDialogStyle.save-as,
                    title="Enter a File Write To"
                }
            {if-non-null out-file then
                let out:#TextOutputStream
                {try
                    || Open the output stream. If the file already exists,
                    || throw an exception.
                    set out = {write-open out-file, error-if-exists?=false}
                 catch err:IOException do
                    {popup-message
                        {text Oops. Error opening file:
                            {italic {value err.message}}
                        }
                    }
                }
                || Now write to the stream.
                {if-non-null out then
                    let out-newline:NewlineFilterTextOutputStream = 
                        {NewlineFilterTextOutputStream
                            out,
                            NewlineSequence.host-default
                        }   
                    {try
                        let numout:int = {out-newline.write-one-string input.value}
                        {out-newline.flush} || not strictly necessary, since close
                        || does a flush automatically.

                        {results.add
                            {text Wrote {value numout} characters
                                to {value out-file.local-filename}.},
                            replace?=true
                        }

                     catch err:IOException do
                        {results.add
                            {text color="red", A problem
                                occurred while writing the file:
                                {italic {value err.message}}},
                            replace?=true
                        }
                     finally || Be sure the file gets closed
                        {out-newline.close}
                    } || try
                } || if-non-null
            } || if-non-null
        } || on Action
    } || CommandButton
}

文字エンコーディング

要約:
  • 文字エンコーディングは、文字のバイナリー値での表示方法をコントロールします。
  • ASCII およびユニコードは一般的なエンコーディング スキームです。
  • ファイルを読み取るプロシージャは、文字エンコーディングを自動的に判別します。
  • 場合によっては、ストリームの使用する文字エンコーディングを選択する必要があります。
  • 制限されているエンコーディングの範囲外の文字を書き込むとエラーが発生します。
文字エンコーディングは、文字のバイナリー値での表示方法をコントロールします。もっとも簡単なエンコーディング スキームの ASCII では、許可されている各文字はシングル バイトで表示できる値に割り当てられています。たとえば、大文字の A0x41 (十進数で 65) としてエンコードされます。
ASCII は、ほとんどのコンピュータ プラットフォームでサポートされているミニマム スタンダードです。一般に英語で使用されている文字やシンボルを含むだけの限られた文字セットです。他の文字エンコーディング スキームの中には、ASCII に他のシンボルを追加して多くのヨーロッパ言語で使用されている文字を表すことができるようにしたものがあります。
他の主要なエンコーディング標準に、ASCII の制限を克服するために作成されたユニコードがあります。ユニコードは、世界中のあらゆる主要な言語の文字をエンコードすることができます。ユニコード システムの詳細と仕様については、次の Unicode Web サイトでご覧になれます。http://www.unicode.org.

文字エンコーディングの指定

ストリームを作成するときにテキスト ストリームの文字エンコーディングを指定することができます。character-encoding オプション パラメータを使用します。これは、テキスト ストリームの作成に使用するほとんどのプロシージャとメソッドで利用可能です。このパラメータは、CharEncoding クラスのインスタンスを取ります。このインスタンスは、Curl ランタイムがサポートする異なるタイプの文字エンコーディングを示します。
アプレットが CharEncoding クラスのメンバを取得するには 次の 2 つの方法があります。
ランタイムでサポートされている文字エンコーディングの詳細なリストについては、『API リファレンス』の CharEncoding 項目を参照してください。

文字エンコーディングの指定が無い場合

入力ストリームのエンコーディングを指定しない場合、ストリームを開くプロシージャは CharEncoding.none-specified の既定の値を使用します。この値は、ストリームで使用されるエンコーディングのタイプが不明か重要でないことを示します。
none-specified エンコーディングを使用してストリームを書き込みで開く場合、Curl ランタイムは Curl 言語の既定のエンコーディング utf8 を使用してストリームを作成します。
ストリームを読み取りまたは追加で開く場合、Curl ランタイムはファイルの最初の数バイトをスキャンしてバイトオーダーマークが含まれているかどうかを確認し、バイトオーダー マークで指示されたエンコーディングでストリームを開きます。これらのバイトオーダー マークは、usc2-big-endian または usc2-little-endian のいずれかとして識別されます。
Curl ランタイムがバイトオーダー マークを見つけられない場合で、ファイルがローカル ファイル システムにあるときは、ランタイムはエンコーディング utf8 でファイルを開きます。ファイルが HTTP 接続で読み取られている場合、Curl RTE は HTTP ヘッダで指定されているエンコーディングを確認し、可能であれば、適切なエンコーディングを判断します。

マルチバイト エンコーディングで使用されるクラス

read-openwrite-open など、通常ストリームを開くために使用するプロシージャは、TranscodingTextInputStream または TranscodingTextOutputStream のインスタンスを返します。ストリーム クラスのサブクラス TextInputStream および TextOutputStream は、複数のバイトを使用して文字をエンコードするエンコーディング スキームを処理します。
read-openwrite-open および他の同様なプロシージャを常に使用する場合、どのストリーム クラスを使用しているかを考慮する必要はありません。ストリーム オブジェクトを直接インスタンス化する必要がある場合、ストリーム クラスを選択する方法の詳細については、「ストリームの取り扱い方」セクションを参照してください。

例外のエンコーディング

asciiiso-latin-1、または windows-latin-1 のような制限付きの文字セットを持つエンコーディング スキームを使用して文字セットにない文字をエンコードすると、TranscodingException になります。たとえば、このコードを実行すると次のような例外が発生します。
{do
    let myurl:Url = {url "curl://client-data/foo.txt"}
    let mystream:TextOutputStream =
        {write-open myurl, character-encoding=CharEncoding.ascii}

    || The Unicode character in the next string will cause an error,
    || since the ascii encoding cannot encode it.
    {mystream.write-one-string "this will cause an error: \u00FF \n"}
    {mystream.close}
}
書き込み中のデータが自分の選択したスキームでエンコードされていることが確かなときは、通常、エンコーディング スキームを指定します。

文字列のエンコーディング

時として、TextOutputStream の場合と同じ方法で String をバイト シーケンスに変換したいことがあります。encode-characters プロシージャを使用して文字列をロー バイトの ByteVec に変換することができます。少なくとも、このプロシージャにはバイトにエンコードする String、その結果を格納する ByteVec、および文字列をローバイトに変換するために使用する文字エンコーディングがあります。このプロシージャに渡した文字のすべてがエンコードされるとは限りません。エンコードした文字数を int として返します。
この例では、CharEncoding を取得するための方法として、get-character-encoding-by-name を使用する場合とクラス定数を使用する場合も示します。

例: 文字列のエンコーディング
{value
    let in:String = "This is a test\n"
    let asciiout:ByteVec = {ByteVec max-size={value in.size}}
    || Since Unicode characters use multiple bytes, need to calculate
    || the size of the array to store the encoded bytes by multiplying
    || by the minimum expansion factor.
    let unicodeout:ByteVec =
        {ByteVec
            max-size={value in.size} *
            CharEncoding.ucs2-big-endian.transcode-min-expansion-factor
        }
    || Use get-character-encoding-by-name procedure
    let encoding:CharEncoding =
        {get-character-encoding-by-name  "ucs2-big-endian"}
    let unicodecount:int =
        {encode-characters in, unicodeout, encoding}
    || Use ascii class constant
    let asciicount:int =
        {encode-characters in, asciiout, CharEncoding.ascii}


    {spaced-vbox
        {paragraph The raw bytes that encode-characters
            procedure encoded are:},
        {paragraph ASCII: {text font-family="monospace", {value asciiout}}
            (encoded {value asciicount} characters).},
        {paragraph UNICODE: {text font-family="monospace", {value unicodeout}}
            (encoded {value unicodecount} characters).}
    }
}
encode-characters プロシージャは、指定されたエンコーディング スキームでエンコードできない文字を検出すると、TranscodingException をスローします。
エンコードする文字が、String の代わりに CharVec のような文字の配列内にある場合、encode-characters の代わりに CharEncoding.encode-characters プロシージャを使用することができます。

出力フォーマット

ほとんどのオブジェクトには、画面に表示したり出力ストリームに書き込むために String に変換される場合、既定のフォーマットがあります。
オブジェクトの変換先となる String の外観を指定する文字列を取る format を使用して、データ オブジェクトの外観をコントロールすることができます。コード (C 言語の printf ステートメントで使用されるものと似ています) だけでなく String に変換されるオブジェクトのリストも含む Stringformat に渡します。文字列を渡したオブジェクトを表す String をフォーマット String に従って変換して返します。
出力する文字列を準備するのに便利なだけでなく、format を使用して out オプションのパラメータを使用する TextOutputStream に直接書き込むこともできます。出力ストリームを指定すると、format は常に null を返します。
このマクロの使い方の詳細については、『API リファレンス』の format 項目を参照してください。