ディレクトリ クラスとファイル クラス

要約:
  • Curl®言語のソース オブジェクトは、2 つのカテゴリであるファイルとディレクトリに分けられます。
  • ファイル という用語は、Curl 言語でデータの読み取りや書き込みを行なうリソース オブジェクトを指します。
  • ディレクトリはファイルや他のディレクトリのコレクションで、ファイル システムにあるディレクトリに似ています。
この章の前半のセクションでは、リソースの識別、読み取り、書き込みの方法について説明しました。通常これらのタスクを実行するときは、その対象となる構造や基礎となるオブジェクトのことを考える必要はありません。これとは別に、リソース オブジェクトそのものの情報が必要になる場合があります。たとえば、ファイルの更新時刻を取得したりディレクトリの内容を表示する場合などでは、直接リソース オブジェクトにアクセスする必要が生じます。
注意: アプレットがローカル ファイル システムにあるファイルにアクセスするためには、特権が必要です。セキュリティの観点から、このドキュメントの例では特権を付けると実行されません。そのため、このセクションでは実行可能な例ではなく、ソースコードでの例を提供します。コードをアプレット ファイルにコピーして特権のあるロケーションから実行してください。詳細は「特権付きアプレット」を参照してください。
Curl 言語リソース オブジェクトは、2 つのカテゴリであるファイルとディレクトリに分けられます。

ファイル オブジェクト

ファイルという用語は、Curl 言語でデータの読み取りおよび書き込みを行うリソース オブジェクトを指します。ファイルには、ファイル システムのファイル、Web サーバー上のページ、文字列ファイルおよびその他のオブジェクトがあります。
Curl 言語では、ファイルは File クラスで表されます。このクラスには、アクセス可能なさまざまなファイルの種類を表すサブクラスがあります。これらのクラスの詳細については、『API リファレンス』を参照してください。
サブクラスにしかないメソッドやアクセッサを必要とする場合を除き、通常はサブクラスではなく File クラスを使用することを推奨します。また、アクセスを要するリソース オブジェクトが適切なサブクラスに属していることを確認する必要があります。

ディレクトリ オブジェクト

ディレクトリ はファイルや他のディレクトリのコレクションで、ファイル システムのディレクトリに似ています。Curl 言語では、ディレクトリは Directory クラスで表されます。このクラスにはあらゆる種類のディレクトリが含まれています。ディレクトリは、ファイル システム内、Web サーバー上、およびディレクトリに似た構成でグループ化されたリソースを含むその他のシステムに存在します。
Directory クラスは抽象的な方法でディレクトリの内容へのアクセスを提供しているため、通常はアクセスするディレクトリの種類を気にする必要はありません。ただし操作の中には特定の種類のディレクトリにしか意味をなさないものがあり、したがって作業対象のディレクトリの種類を確定する必要が生じる場合もあります。
Directory クラスには、さまざまな種類のディレクトリを表すサブクラスが多数含まれています。

URL をオブジェクトに変換

通常 File または Directory オブジェクトは、アクセス対象のリソ-スを識別する Url オブジェクトの resolve メソッドを呼び出してインスタンスを作成します。Url.resolveany を返します。これは、Url が何を指すかによって File または Directory クラスのいずれかのインスタンスになります。
{value
    let myurl:Url = {url "support/io/example.txt"}
    let resource:any = {myurl.resolve}
}
Url が表すリソースが実在しない場合、resolve メソッドは MissingFileException をスローします。curl: スキームの最上位か ObjectDirectory のいずれかにある Url の変換の場合、ファイルまたはディレクトリが存在しなければ resolveKeyNotFoundException をスローします。
次の例では、URL を入力して、それに対して resolve メソッドがクラスを返します。URL が識別する File または Directory のサブクラスを識別します。この例のコードをアプレットにコピーし、特権のあるロケーションから実行してください。
{import * from CURL.IO.HTTP}
{define-proc {typed-file-list uri:String}:String
    let id:String = "<error>"
    {try
        || 解析の結果が例外になる場合に注意してください。
        let uriobj:any = {{url uri}.resolve}

        || 結果が何か判別してください。解析された結果が各クラスと一致するかチェックします。
        {type-switch uriobj
         case hd:HttpDirectory do
            set id={String uri, " is an HttpDirectory."}
         case hf:HttpFile do
            set id={String uri, " is an HttpFile."}
         case ld:LocalDirectory do
            set id={String uri, " is a LocalDirectory."}
         case lf:LocalFile do
            set id={String uri, " is a LocalFile."}
         case sd:StringDirectory do
            set id={String uri, " is a StringDirectory."}
         case sf:StringFile do
            set id={String uri, " is a StringFile."}
         case f:File do
            set id={String uri, " is a File."}
         case od:ObjectDirectory do
            set id={String uri, " is an ObjectDirectory."}
         else
            set id={String uri, " is something I can't identify!"}
        }

        || 入力した URL が見つからないときに実行します。
     catch err:MissingFileException do
        set id = {String uri, " not found."}

     catch err:FileException do
        set id = {String uri, " not found."}
        || KeyNotFound の例外は URL を存在しない curl:// スキームの
        ||トップで解析しようとした場合に起こります。
     catch err:KeyNotFoundException do
        set id = {String uri, " not found. Stop messing around in the curl:// hierarchy!"}

    } || try
    {return id}
}

URL を入力して、{bold What is it?} を、この例では、URL を解決して、もしあれば、その URL に存在するオブジェクトのクラスを知らせます。

{value
    let dirfield:TextField =
        {TextField width=2in, value="."}
    let results:Graphic =
        {Fill}
    let clickme:CommandButton =
        {CommandButton
            label="What is it?",
            {on Action do
                set results =
                    {results.replace-with
                        {TextFlowBox
                            {typed-file-list dirfield.value}
                        }
                    }
            }
        }
    {spaced-vbox
        {spaced-hbox {text URL to probe:}, dirfield},
        {spaced-hbox {Fill}, clickme},
        {spaced-hbox results}}
}

ファイルまたはディレクトリを表す Url の判定

Curl 言語には、Url の変換によって File または Directory のどちらのオブジェクトが返されるかをすばやく判定する 2 つのプロシージャがあります。これは 1 種類のオブジェクトしか扱わない場合に役に立ちます。これらのプロシージャが true を返す場合、Url で識別されるオブジェクトが存在すること、および File または Directory のいずれかであることを意味します。
local-file-exists?Url を取り、Url.resolveLocalFile オブジェクトを返す場合には true を返します。 同様に、local-directory-exists?Url を取り、url.resolveLocalDirectory オブジェクトを返す場合に true を返します。 この例のコードをアプレットにコピーし、特権のあるロケーションから実行してください。
{value
    let extfield:TextField =
        {TextField width=2in, value="."}
    let extresults:Graphic =
        {Fill}
    let extbutton:CommandButton =
        {CommandButton
            label="Does it exist?",
            {on Action do
                set extresults =
                    {extresults.replace-with {TextFlowBox}}
                let exturl:Url = {url extfield.value}
                {if {local-file-exists? exturl} then
                    {extresults.add
                        {text {value exturl}
                            is a File. }
                    }
                 else
                    {extresults.add
                        {text {value exturl}
                            is {text color = "red", not}
                            a File. }
                    }
                }
                {if {local-directory-exists? exturl} then
                    {extresults.add
                        {text {value exturl}
                            is a Directory.}
                    }
                 else
                    {extresults.add
                        {text {value exturl}
                            is {text color = "red", not}
                            a Directory.}
                    }
                }
            }
        }

    {VBox
        {HBox {text Enter a URL:}, extfield},
        {HBox {Fill}, extbutton},
        {HBox extresults}}
}
注意: ファイルやディレクトリにアクセスする前にその存在が確認されていても、アクセス時に必ず MissingFileException がキャッチできるようにしておく必要があります。この理由は、たとえば上記のプロシージャを使用してファイルの存在を確認してから、実際にアクセスするまでの間にファイルが削除されている可能性があるためです。もちろん、スローされる可能性のある他の例外もキャッチする必要があります。

ディレクトリに関する操作

UrlDirectory オブジェクトに変換したら、このオブジェクトを使用してディレクトリに関する情報にアクセスできるようになります。以下のセクションでは、ディレクトリの内容にアクセスしたりその他の情報を取得する方法について説明します。

ディレクトリ内容へのアクセス

Directory クラスはディレクトリ内のファイルやディレクトリを Association-of String に一覧表示します。したがって for ... in ループを使用すればディレクトリ内容の名前を繰り返し処理することができます。キーで指定するオブジェクトにアクセスする場合は、Directory.get メソッドを使用してアクセスする File または Directory を作成します。
次の例では、ディレクトリの内容を繰り返して処理しています。typed-file-list プロシージャに URL が渡されて変換されます。変換されたオブジェクトが Directory の場合、オブジェクト内のキーを繰り返し処理してオブジェクト名を StringBuf に追加します。さらに、キーで指定されるファイルまたはディレクトリを表すオブジェクトのインスタンスを作成します。このオブジェクトがディレクトリまたはシンボリック リンクの場合、プロシージャはオブジェクトの名前に / または @ を追加して、普通のファイルと区別します。この例のコードをアプレットにコピーし、特権のあるロケーションから実行してください。
{define-proc {typed-file-list diruri:String}:String
    let keys:StringBuf = {StringBuf ""}
    {try || ファイル関連のエラーをキャッチします
        let dir:any = {{url diruri}.resolve}
        || 返却されたオブジェクトが Directory であることを確認します。
        {if dir isa Directory then
            || ディレクトリ内の全てのファイルをチェックするループ構文です。
            {for key k:String in dir do
                {keys.concat k}
                || 各ファイルを検証します。 ディレクトリならばファイル名に / を追加します。
                let obj:any = {dir.get k}
                {if obj isa Directory then
                    {keys.append '/'}
                }
                || シンボリック・リンクならば '@' をファイル名に追加します。
                {if obj isa String then
                    {keys.append '@'}
                }
                {keys.append ' '}
            }
         else
            {set keys =
                {StringBuf diruri & " is not a directory."}
            }
        }

        || 指定した URL に何も存在しない場合に実行します。
     catch err:MissingFileException do
        {set keys = {StringBuf "Directory " & diruri & " not found."}
        }
    } || try
    || 結果を文字列に変換して返却します。
    {return {keys.to-String}
    }
}

|| UI部分: テキストボックスとボタンが上記のプロシージャを呼び出し、
|| スペースが結果を保持します。
{value
    let dirfield:TextField =
        {TextField width=4in, value="."}
    let results:Graphic =
        {Fill}  || ディレクトリ リストの結果を保持します。
    let clickme:CommandButton =
        {CommandButton
            label="List Directory",
            || プロシージャを呼び出し、現在ボックスが保持しているコンテンツを
            || 新規の結果と入れ替えます。
            {on Action do
                set results =
                    {results.replace-with
                        {TextFlowBox {typed-file-list dirfield.value}
                        }
                    }
            }
        }
    || 要素を整理し表示します。
    {spaced-vbox
        {spaced-hbox {text Directory URL:}, dirfield},
        {spaced-hbox {Fill},clickme},
        {spaced-hbox results}}
}

ディレクトリ情報の取得

Directory クラスには、それで表されるディレクトリに関する情報やその下にあるファイル システムの詳細を取得するためのいくつかのメソッドやアクセッサが用意されています。
次のようなアクセッサがあります。
アクセッサ名説明
{d:Directory.name}:StringDirectory オブジェクトの URL を含む String を返します。
{d:Directory.parent-dir}:Directoryこの Directory を含む Directory を返します。
{d:Directory.url}:UrlDirectory オブジェクトの URL を表す Url を返します。
{d:Directory.url-separator}:charDirectory オブジェクトの URL でパスの部分の要素を区切るために使用される文字を返します。
Directory クラスのメソッドにより、URL の操作や新しいUrl の作成が可能になります。
メソッド名説明
{d:Directory.concat tail:String}:Urlディレクトリの URL に相対パス tail を追加し、Url として結果を返します。このメソッドは Url.concat に似ていますが、Directory がディレクトリ オブジェクトしか表すことがないため、開始パスが常にディレクトリになるという点で異なります。Directory と相対的な位置にある新しい Url を作成するにはこのメソッドを使用します。
{d:Directory.special-entry? entry:String}:boolentry... のようなディレクトリ内の特別なエントリの名前の場合は true を返します。
{d:Directory.url-separator-fn}:charurl-separator アクセッサのクラス メソッド バージョン。
{d:Directory.url-valid-separator? sep:char}:boolsepDirectory を含むファイル システムの有効なパス区切り文字である場合、true を返します。
上記では、concat メソッドがおそらく最も頻繁に使用するメソッドでしょう。これを使用すると、Directory から (たとえば、その中にあるファイルにアクセスするために) 新しい Url を簡単に作成することができます。次の例で、ディレクトリの URL とそれに連結するパスを入力してみてください。この例のコードをアプレットにコピーし、特権のあるロケーションから実行してください。
{value
    || URLとパスを入力するフィールドと結果を表示するスペースを作成します。
    let urlfield:TextField =
        {TextField width = 4in, value="."}
    let concatfield:TextField =
        {TextField width = 4in, value="foo.curl"}
    let result:Graphic =
        {Fill}

    || このボタンで全ての作業を行います。
    || クリックすると URL がディレクトリを指しているかをチェックし、
    || 正しい場合は Directory オブジェクトを作成し、concat メソッドを使用します。
    let doconcat:CommandButton =
        {CommandButton
            label="Concat!",
            {on Action do
                let theurl:Url = {url urlfield.value}
                || URL がディレクトリ名であるか確認します。
                {if not {in-local-filesystem? theurl} or
                    {local-directory-exists? theurl}
                 then
                    || 正しい場合は Directory オブジェクトを入手します。
                    let thedir:Directory =
                        {theurl.resolve}
                    || パスを連結します。
                    let newurl:Url =
                        {thedir.concat concatfield.value}
                    || 結果を渡します。
                    set result =
                        {result.replace-with
                            {TextFlowBox
                                {paragraph The Url
                                    {text color="red",
                                        {value theurl.name}}
                                    concatenated with
                                    {text color="red",
                                        {value concatfield.value}} is
                                    {text color="green",
                                        {value newurl.name}}.}
                            }
                        }

                 else  || 既存のディレクトリを指定しなかった場合
                    set result =
                        {result.replace-with
                            {TextFlowBox
                                {paragraph Hey!
                                    {text color="red",
                                        {value theurl.name}}
                                    isn't a directory.}
                            }
                        }
                }
            }
        }
    || UI を配置します。
    {spaced-vbox
        {spaced-hbox {Fill}, {text Url:}, urlfield},
        {spaced-hbox {Fill}, {text Path to concat:}, concatfield},
        {spaced-hbox {Fill}, doconcat},
        {value result}
    }
}

ディレクトリの作成

create-Directory プロシージャを使用してファイル システム上で新しいディレクトリを作成します。このプロシージャは Url を引数として、新しいディレクトリを表す Directory オブジェクトを返します。既定では、そのディレクトリがファイル システムにすでに存在する場合に IOException がスローされます。error-if-exists? オプションを false に設定すると、この動作を変更することができます。

ファイルに関する操作

UrlFile オブジェクトに変更したら、オブジェクトを使用してリソースに関する情報を取得することができます。

ファイル アクセッサ

次の表は、役に立つ情報を提供する File クラスのアクセッサを一覧したものです。完全な詳細については、『API リファレンス マニュアル』の File の項目を参照してください。
アクセッサ説明
exists?ファイルが変換された場合に true を返します。これは通常ファイルが存在することを意味します。
parent-dirこのファイルが存在するディレクトリを表す Directory オブジェクトを返します。
when-last-accessedファイルが最後にアクセスされた日付と時刻を表す DateTime オブジェクトを返します。ファイルが存在するファイル システムからこの情報を取得できない場合、このメソッドは null を返します。
when-last-modifiedファイルが最後に書き込まれたかまたは別の方法で変更された時間を表す DateTime オブジェクトを返します。ファイルが存在するファイル システムからこの情報を取得できない場合、このアクセッサは null を返します。
writable?ファイルが書き込み可能な場合に true を返します。
この例のコードをアプレットにコピーし、特権のあるロケーションから実行してください。
{value
    let results:TextFlowBox = {TextFlowBox}
    let myurl:Url = {url "io-directory-file-classes.curl"}
    let myfile:File = {myurl.resolve}

    {results.add
        {paragraph This file is located in
            {value {value myfile.parent-dir}.name}.}}

    {results.add
        {paragraph It was last accessed
            {value myfile.when-last-accessed}.}}

    {results.add
        {paragraph It was last modified
            {value myfile.when-last-modified}.}}

    {if myfile isa LocalFile and (myfile asa LocalFile).writable? then
        {results.add {paragraph The file {bold is} writable.}}
     else
        {results.add {paragraph The file {bold is not} writable.}
        }
    }

    {value results}
}

ファイル メソッド

File クラスのメソッドの多くはプロシージャとして直接使用することができます (たとえば、File.write-openwrite-open プロシージャで呼び出せます)。ファイルにアクセスするために File オブジェクトのインスタンスを作成する必要がないという点で、プロシージャを使用する方が便利です。
上記の情報以外に、LocalFileInfo クラスからファイルの情報を得ることもできます。ANSI C の struct stat から得られる情報と同じような情報を提供します。このクラスのオブジェクトを直接インスタンス化することはできません。代わりに、LocalFile クラスの info メソッドを使用する必要があります。これは、File クラスの LocalFile サブクラスが特別に必要となるケースの 1 つです。
このクラスの詳細については、『API リファレンス マニュアル』の LocalFileInfo の項目を参照してください。

ファイルのコピー、削除、および名前の変更

ファイル システムのファイルを操作するには、ファイルを含むディレクトリを表す Directory オブジェクトをインスタンス化します。ファイル操作がより簡単になるように、Curl 言語ではファイル システムのファイルを直接操作するプロシージャがいくつか用意されています。これらのプロシージャは、ローカル ファイル システムのファイルを識別する Url オブジェクトのみを対象にしています。

コピー

プロシージャ{copy from:Url, to:Url, error-if-exists?:bool=true, recurse?:bool=false}:void
説明from で指定されたファイルを to で指定された場所にコピーします。to URL は、コピー先のファイルまたはディレクトリの名前になります。単にコピー先のディレクトリ 1 つを指しているのではありません。recurse?truefrom がディレクトリを指す場合、ディレクトリとその内容、およびその配下のディレクトリの内容もすべてコピーされます。
例外IOException がスローされるのは次のような場合です。
  • error-if-exists?true で、ファイルまたはディレクトリがすでに to に存在する場合。
  • from がディレクトリで、recursefalse の場合。
PermissionDeniedFileException がスローされるのは次のような場合です。
  • ユーザーが from の読み取り権限を持っていない場合。
  • ユーザーが to を作成するための書き込み権限を持っていない場合。
次の例で、2 つの URL を入力してみてください。1 つは存在するファイルの URL、もう 1 つはファイルのコピー先の場所を識別する URL です。サンプルコードをアプレット ファイルにコピーして、特権ロケーションから実行してください。
{value
    let copyfrom:TextField =
        {TextField width=4in}
    let copyto:TextField =
        {TextField width=4in}
    let copyresult:Graphic =
        {Fill}
    let recurse-button:CheckButton =
        {CheckButton || OK to copy a directory?
            label = "Recurse?"}
    let overwrite-button:CheckButton =
        {CheckButton || OK to overwrite a file?
            label = "Overwrite?"}
    let docopy:CommandButton =
        {CommandButton
            label = "Copy",
            {on Action do
                {if (copyfrom.has-value? and copyto.has-value?) then
                    {try || Catch any exceptions thrown by copy
                        {copy {url copyfrom.value},
                            {url copyto.value},
                            recurse? = recurse-button.value,
                            error-if-exists? = overwrite-button.value}
                        || Update message indicating successful copy
                        set copyresult =
                            {copyresult.replace-with
                                {TextFlowBox "Copied " & copyfrom.value
                                    & " to " & copyto.value}}
                        || Catch possible exceptions caused by copy?
                     catch err:MissingFileException do
                        set copyresult =
                            {copyresult.replace-with
                                {TextFlowBox copyfrom.value &
                                    " does not exist!"}}
                     catch err:PermissionDeniedFileException do
                        set copyresult =
                            {copyresult.replace-with
                                {TextFlowBox "Permission to read " &
                                    copyfrom.value &
                                    " or write to " &
                                    copyto.value &
                                    " denied."}}
                     catch err:IOException do
                        set copyresult =
                            {copyresult.replace-with
                                {TextFlowBox copyfrom.value &
                                    " is a directory and you " &
                                    " did not check Recurse?," &
                                    " or you are trying to " &
                                    " overwrite a file and " &
                                    " Overwrite? is not checked."
                                }
                            }
                    } || Try
                else || One of the TextFields was blank
                    set copyresult =
                        {copyresult.replace-with
                            {TextFlowBox "You must supply a value " &
                                " in both boxes!"}}
                }|| If
            }
        }


    {spaced-vbox width=6in,
        {spaced-hbox
            {Fill},
            {text Copy from:},
            copyfrom
        },
        {spaced-hbox
            {Fill},
            {text Copy to:},
            copyto
        },
        {spaced-hbox
            {Fill},
            {VBox
                recurse-button, overwrite-button},
            {Fill}, docopy},
        {spaced-hbox {text Message:}, copyresult}
    }
}

削除

プロシージャ{delete url:Url, error-if-missing?:bool=true, recurse?:bool=false}:void
説明url で指定されたファイルまたはディレクトリを削除します。url がディレクトリを識別し、recursetrue の場合、ディレクトリとともにその内容および配下の内容がすべてが削除されます。
例外
MissingFileException がスローされるのは、ファイルまたはディレクトリが存在せず error-if-missing?true の場合です。
recursefalseurl がディレクトリの場合は、IOException がスローされます。
プログラムが url を削除する権限を持っていない場合は、PermissionDeniedFileException がスローされます。

名前の変更

プロシージャ{rename from:Url, to:Url, error-if-exists?:bool=true}:void
説明from にあるファイルまたはディレクトリの名前を to に変更します。from のステム パスが to と異なる場合は、ファイル名が変更されるだけでなく to で指定された場所へファイルが移動します。
例外
IOException がスローされるのは次のような場合です。
  • to の最後のファイル名またはディレクトリ名の上位のディレクトリが存在しない場合
  • from にファイルもディレクトリも存在しない場合
  • to がすでに存在し、error-if-exists?true の場合
ユーザーが from の名前を変更する権限を持っていない場合、renamePermissionDeniedFileException をスローします。

シンボリック リンク

プロシージャ{symlink url:Url, linked:String}:void
説明ファイルまたはディレクトリの pathname へのシンボリック リンクである linked を作成します。pathlinked と相対的な位置にあるファイルまたはディレクトリを指す String です。pathnameで指定されたファイルまたはディレクトリが実際に存在するかどうかはチェックされません。結果として、壊れたシンボリック リンクがファイル システムに生成される可能性もあります。
例外
FileException がスローされるのは次のような場合です。
  • ファイル システムがシンボリック リンクをサポートしない場合。
  • ファイルまたはディレクトリがすでに linked に存在する場合。
linked のパスで指定されたディレクトリが存在しない場合は、MissingFileException がスローされます。
プログラムが linked を作成する権限を持っていない場合は、PermissionDeniedFileException がスローされます。