| 要約: | - テキストの読み取りと書き込みは、リソースへのアクセスで最も一般的なタスクです。
- 読み取りと書き込みは、いずれも基本ステップは同じです。
|
テキストの読み取りと書き込みは、リソースへのアクセスでもっとも一般的なタスクです。たとえば、アプリケーションでエンド ユーザーが作業しているファイルをディスクに保存したり、Web ページを読み取って情報を取り出すことが必要になることがあります。
リソースとの間で読み取りや書き込みを行うプロセスは次の手順に従います。
- リソースの URL を使用して Url オブジェクトをインスタンス化することによりリソースを識別します。File のような特定のオブジェクトを使用することもできます。
- ストリーム オブジェクトをインスタンス化して、リソースへの入力または出力ストリームを開きます。
- ストリーム オブジェクトで、メソッドを使用してリソースからデータを読み取るか、または、リソースにデータを書き込みます。
- 読み取りまたは書き込みを完了したら、ストリーム オブジェクトの close メソッドを呼び出してストリームを閉じます。
ストリームの詳細については、「
ストリーム」セクションを参照してください。
リソースから読み取るための簡単な方法は、リソースをテキスト ファイルとして開くことです。Web ページ、ASCII (American Standard Code for Information Interchange) テキスト データ ファイル、および構成ファイルなど多くのリソースをこの方法で読み取ることができます。次のセクションでは、テキスト ファイルを開く、読み取る、および閉じる方法について説明し、それらの手順を実例にまとめています。
テキスト ファイルから読み取るには、
TextInputStream オブジェクトを作成する必要があります。このオブジェクトはストリーム クラスの 1 つで、リソースからのデータのシーケンスを表します。ストリームはデータ リソースに一般的なインターフェイスを提供します。ディスク上のファイルから来るストリームを読み取るのは、ネットワークまたはその他のソース上を移動しているストリームから読み取るのと同じです。
Url オブジェクトを持っていても、識別されるリソースが存在すること、またはそのリソースにアクセスできることは保証されないことに注意してください。存在しないリソースを表す
read-open を
Url に渡すと、
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 からストリームを開こうとする際に、スローされる可能性のある様々な例外を以下に示します。
例外処理の詳細については、「
例外」セクションを参照してください。
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} |
| 説明 | 入力ストリームから buf に n 個の文字を読み取ります。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 個の文字を取得します。 |
データの読み取りが終了したら、
TextInputStream.close を呼び出してストリームをシャットダウンし、使用中のリソースをクリーン アップする必要があります。ストリームを閉じないと、開いているファイル ハンドラが過剰に存在することになります。
ファイルからの読み取りに問題がある場合でも、ストリームが閉じたことを確認する必要があります。閉じたことを確認する最良の方法は、ストリームでのすべての読み取り操作を取り巻く
try ブロックの
finally 部分で閉じるための呼び出しを行うことです。
注意: この例は、読み込み操作に 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 の他のメソッドの中には、一度に 1 文字を入力する場合に便利なものがあります。
次に入力ストリームから 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}
}
}
}
}
}
}
}
| |
テキスト ファイルに書き込む手順は読み取りと似ています。ストリームを開き、ストリームでメソッドを使用し、ストリームを閉じるという同じ処理を行います。
ただし、
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 を指示することができます。 |
すべてのデータをリソースに書き込んだら、
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
}
| |
次の例は、基本的には前のものと同じですが、
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 プロシージャは、出力先を開き、既存のファイルを削除します。場合によっては、ファイルの古い内容を保持したまま、ログ ファイルなどの新しい項目を最後に追加したいことがあります。
異なるコンピュータ オペレーティング システムは、異なる規則を使用してテキスト ファイルに行末をマークします。たとえば、Windows® システムは、
\r\n (キャリッジ リターンと改行文字) を使用して行末をマークします。一方、Linux システムは、
\n (改行文字) を使用します。これらの違いを扱うために、
CURL.IO.STREAM パッケージはさまざまな行末規則を認識する方法を提供し、必要に応じて行末規則を変換します。
また、API は、1 つの行末規則から他の行末規則に変換するこれらのクラスを提供します。
入力ストリーム クラスは、入力された指定の改行シーケンスを Curl 規則 \n へ変換します。既定では、すべての改行シーケンスが変換されます。この変換により、ソース テキストで使用される規則に関係なく、ストリームから読み込まれるテキスト内で一貫した行末規則を使用することが可能になります。
出力ストリーム クラスは、Curl 規則 \n から、指定された改行シーケンスに変換します。値 NewlineSequence.all は、出力では有効ではありません。この変換により、特定のターゲット システムに対する適切な行末シーケンスを含めることができます。NewlineSequence.host-default 値がどのような行末シーケンスを使用していても、それがCurl アプレットを実行しているホスト マシン上での標準となることに注意してください。
これらのクラスは、対応するテキスト入出力ストリームの周囲でラッパーとして動作します。フィルタ クラスのコンストラクタに対して適切な型のストリームを指定する必要があります。
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 では、許可されている各文字はシングル バイトで表示できる値に割り当てられています。たとえば、大文字の A
は 0x41 (十進数で 65) としてエンコードされます。
ASCII は、ほとんどのコンピュータ プラットフォームでサポートされているミニマム スタンダードです。一般に英語で使用されている文字やシンボルを含むだけの限られた文字セットです。他の文字エンコーディング スキームの中には、ASCII に他のシンボルを追加して多くのヨーロッパ言語で使用されている文字を表すことができるようにしたものがあります。
他の主要なエンコーディング標準に、ASCII の制限を克服するために作成されたユニコードがあります。ユニコードは、世界中のあらゆる主要な言語の文字をエンコードすることができます。ユニコード システムの詳細と仕様については、次の Unicode Web サイトでご覧になれます。
http://www.unicode.org.
ストリームを作成するときにテキスト ストリームの文字エンコーディングを指定することができます。
character-encoding オプション パラメータを使用します。これは、テキスト ストリームの作成に使用するほとんどのプロシージャとメソッドで利用可能です。このパラメータは、
CharEncoding クラスのインスタンスを取ります。このインスタンスは、Curl ランタイムがサポートする異なるタイプの文字エンコーディングを示します。
ランタイムでサポートされている文字エンコーディングの詳細なリストについては、『API リファレンス』の
CharEncoding 項目を参照してください。
入力ストリームのエンコーディングを指定しない場合、ストリームを開くプロシージャは CharEncoding.none-specified の既定の値を使用します。この値は、ストリームで使用されるエンコーディングのタイプが不明か重要でないことを示します。
none-specified エンコーディングを使用してストリームを書き込みで開く場合、Curl ランタイムは Curl 言語の既定のエンコーディング utf8 を使用してストリームを作成します。
ストリームを読み取りまたは追加で開く場合、Curl ランタイムはファイルの最初の数バイトをスキャンしてバイトオーダーマークが含まれているかどうかを確認し、バイトオーダー マークで指示されたエンコーディングでストリームを開きます。これらのバイトオーダー マークは、usc2-big-endian または usc2-little-endian のいずれかとして識別されます。
Curl ランタイムがバイトオーダー マークを見つけられない場合で、ファイルがローカル ファイル システムにあるときは、ランタイムはエンコーディング utf8 でファイルを開きます。ファイルが HTTP 接続で読み取られている場合、Curl RTE は HTTP ヘッダで指定されているエンコーディングを確認し、可能であれば、適切なエンコーディングを判断します。
read-open、
write-open および他の同様なプロシージャを常に使用する場合、どのストリーム クラスを使用しているかを考慮する必要はありません。ストリーム オブジェクトを直接インスタンス化する必要がある場合、ストリーム クラスを選択する方法の詳細については、「
ストリームの取り扱い方」セクションを参照してください。
ascii、
iso-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 として返します。
| 例:
文字列のエンコーディング |
 |
{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).}
}
}
| |
ほとんどのオブジェクトには、画面に表示したり出力ストリームに書き込むために
String に変換される場合、既定のフォーマットがあります。
オブジェクトの変換先となる
String の外観を指定する文字列を取る
format を使用して、データ オブジェクトの外観をコントロールすることができます。コード (C 言語の
printf ステートメントで使用されるものと似ています) だけでなく
String に変換されるオブジェクトのリストも含む
String を
format に渡します。文字列を渡したオブジェクトを表す
String をフォーマット
String に従って変換して返します。
出力する文字列を準備するのに便利なだけでなく、
format を使用して
out オプションのパラメータを使用する
TextOutputStream に直接書き込むこともできます。出力ストリームを指定すると、
format は常に
null を返します。
このマクロの使い方の詳細については、『API リファレンス』の
format 項目を参照してください。
Copyright © 1998-2008 Sumisho Computer Systems Corp.
All rights reserved.
Curl, the Curl logo, Surge, and the Surge logo are trademarks of Sumisho
Computer Systems Corp. that are registered in the United States. Surge
Lab, the Surge Lab logo, and the Surge Lab Visual Layout Editor (VLE)
logo are trademarks of Sumisho Computer Systems Corp.