[Java] Tip #13 java.io.File Surprises

コードでよく見かける、全体としてまずい前提があります。この前提は容易に悪影響を与えることがあります。その前提とは…
File.getAbsolutePath と getAbsoluteFile は相対でないパスを返す
正しくないのですよ。ちょっとね。少なくとも、多くの人が考えている通りではありません。
パスの最初が絶対パスで、パスの残りの部分に相対パスの要素を指定してみましょう。
以下のコードではどのような結果を出すと思いますか?
public class Main {
    public static void main(String[] args) {
        try {
            File f = new File("/temp/../temp/../temp/../");
            File abs  = f.getAbsoluteFile();
            File parent = abs.getParentFile();
            System.out.println("Exists: " + f.exists());
            System.out.println("Absolute Path: " + abs);
            System.out.println("FileName: " + abs.getName());
            System.out.printf("The Parent Directory of %s is %s\n", abs, parent);
            System.out.printf("The CANONICAL Parent Directory of CANONICAL %s is %s\n",
                        abs, abs.getCanonicalFile().getParent());
            System.out.printf("The CANONICAL Parent Directory of ABSOLUTE %s is %s\n",
                        abs, parent.getCanonicalFile());
            System.out.println("Canonical Path: " + f.getCanonicalPath());
        }
        catch (IOException ex) {
            System.out.println("Got an exception: " + ex);
        }
    }
} 
実行してみると…
Exists: true
Absolute Path: D:\temp\..\temp\..\temp\..
FileName: ..
The Parent Directory of D:\temp\..\temp\..\temp\.. is D:\temp\..\temp\..\temp
The CANONICAL Parent Directory of CANONICAL D:\temp\..\temp\..\temp\.. is null
The CANONICAL Parent Directory of ABSOLUTE D:\temp\..\temp\..\temp\.. is D:\temp
Canonical Path: D:\
[訳注]
実行環境によってルートディレクトリは異なります。

d:\の親はd:\tempと言っていることに注目して下さい。ファイルfは実際にはルートディレクトリですので、本当なら親はnullになるはずです。私はようやくこのことがわかりました。getParentXXX()は単にパスの最後の部分を切り取るだけなのです。上記のような全く予期しない結果が簡単に出るのです。
実はこの振る舞いは、数年前にバグ登録しています。
http://bt2ws.central.sun.com/CrPrint?id=6687287

推奨策
(1) getAbsoluteではなく、getCanonicalを使うと、ファイルとカノニカルファイル名を1:1に対応します。つまり、各ファイルには唯一のカノニカルファイル名がありますが、ファイル名中のパス要素とは明確な関連がありません。各ファイルの絶対パスは無数に存在します。

(2) File fの親ファイルを取得するためには、getParentFile()を使わずに以下のような方法を使いましょう。
File parent = new File(f, "..");

原文はこちら。
http://blogs.oracle.com/foo/entry/tip_13_java_io_file

0 件のコメント:

コメントを投稿