●ファイルを再帰的に検索する


あるファイルをサブフォルダの隅々まで巡って再帰的に検索するサンプルはよく本やネットにありますが、
自分で作ったフォルダの中などはうまく動くと思いますが、OSのルートからファイルを検索するなどした場合には、そうは簡単にうまく行きませんでした。
実際に検索プログラムを書いて検索してみると、ディレクトリとシンボリックリンクの区別が付かずに参照先にまで検索が及んでしまい、検索が循環し無限ループにおちいりました。

そこで、ディレクトリとシンボリックリンクを判断するために、このように判断しました。

if(tmpFile.getAbsolutePath().equals(tmpFile.getCanonicalPath())) {}

getAbsolutePathにより絶対パスを取得します。
たとえシンボリックリンクだとしても、そのファイルの絶対パスを取得します。
getCanonicalPathはシンボリックリンクを解決して取得します。
対象がシンボリックリンクの場合にはリンク先にパスを取得します。
つまり、二つの結果を比較して同一ならばディレクトリ、異なるならシンボリックリンクと判断できるはずです。

この事を踏まえて再帰的にファイルを検索するプログラムを書いてみました。

private void search(File file) {
    // ファイル一覧取得
    File[] files = file.listFiles();
    if (files == null) {
        return;
    }

    for (File tmpFile : files) {
        // ディレクトリの場合
        if (tmpFile.isDirectory()) {
            try {
                if(tmpFile.getAbsolutePath().equals(tmpFile.getCanonicalPath())) {
                    search(tmpFile);//再帰的に検索
                }
            }catch(Exception e){}
        } else {
            // ファイルの場合
            if("current_now".equals(tmpFile.getName())){
                str=tmpFile.getPath();
                Log.d("testTAG", tmpFile.getPath() + "");
                return;
            }
        }
    }
}

▼上のプログラムをファイル名を正規表現で検索できるように書き換えたもの

//検索するファイルの正規表現パターン
Pattern p = Pattern.compile("current_.*");

private void search(File file) {
    // ファイル一覧取得
    File[] files = file.listFiles();
    if (files == null) {
        return;
    }

    for (File tmpFile : files) {
        // ディレクトリの場合
        if (tmpFile.isDirectory()) {
            try {
                if(tmpFile.getAbsolutePath().equals(tmpFile.getCanonicalPath())) {
                    search(tmpFile);
                }
            }catch(Exception e){}
        } else {
            // ファイルの場合
            if(p.matcher(tmpFile.getName()).matches()){
                str=str+tmpFile.getPath() + "\n\n";
                Log.d("testTAG", tmpFile.getPath() + "");
               // return;
            }
        }
    }
}

▼ハマった事

上の検索関数をプログラム上では戻り値を返して呼び出せるようにした方が美しく書けるような気がします。

String filename = search(new File("sys"));
このように。

しかしながら上の関数は再帰呼び出しであり、ただ単に戻り値を返すだけだと、呼び出し元に渡るだけで処理したい部分まで値が来ません。
そして呼び出してもnullしか返ってこなくてハマります。


//検索するファイルの正規表現パターン
Pattern p = Pattern.compile("batt_current|getcurrent|current_now|batt_chg_current|charger_current");

private String search(File file) {
    // ファイル一覧取得
    File[] files = file.listFiles();
    if (files == null) {
        return null;
    }

    for (File tmpFile : files) {
        // ディレクトリの場合
        if (tmpFile.isDirectory()) {
            try {
                if(tmpFile.getAbsolutePath().equals(tmpFile.getCanonicalPath())) {
                    String pos = search(tmpFile);
                    //ファイルが見つかったら検索をやめる
                    if(pos != null) return pos;
                }
            }catch(Exception e){}
        } else {
            // ファイルの場合
            if(p.matcher(tmpFile.getName()).matches()){
                return tmpFile.getPath();
            }
        }
    }
    return null;
}
戻り値を付けるなら結果を呼び出し元に返すようにするとマトモに動くようになります。


▲トップページ > android