16

を使用して特定のファイルが存在することを確認するために、さまざまなディレクトリをトラバースするプログラムに現在取り組んでいますFile.Exists()

アプリケーションは、実際には存在するのに特定のファイルが存在しないと主張してきました。最近、このエラーはパスが長すぎることが原因であることがわかりました。

File.Exists()間違った値を返すことに対処する SO に関する質問があることは認識していますが、この特定の問題を解決するものはないようです。

ディレクトリとファイルの名前を変更してパスを短くすることは実際にはオプションではないため、この時点で何をすべきかわかりません。この問題を解決する回避策はありますか?

使用中のコードは特別なものではありません (関係のないコードをいくつか切り取っています) が、役立つ場合に備えて以下に含めます。

    private void checkFile(string path)
    {
        if (!File.Exists(path))
            Console.WriteLine("   *  File: " + path + " does not exist.");
    }
4

5 に答える 5

12

MSDNから-ファイル、パス、名前空間の命名

Windows APIでは(次の段落で説明するいくつかの例外を除いて)、パスの最大長はMAX_PATHであり、これは260文字として定義されています。

..。

Windows APIには、最大合計パス長32,767文字の拡張長パスを許可するUnicodeバージョンを備えた多くの関数があります。このタイプのパスは、バックスラッシュで区切られたコンポーネントで構成され、各コンポーネントは、GetVolumeInformation関数のlpMaximumComponentLengthパラメーターで返される値(この値は通常255文字)までです。拡張長パスを指定するには、"\\?\"プレフィックスを使用します。たとえば、"\\?\D:\very long path"

..。

相対パスでプレフィックスを使用することはできない"\\?\"ため、相対パスは常に合計MAX_PATH文字に制限されます。

(強調追加)

すべてのパスがフルパスの場合、次のように拡張長パス指定子を使用するようにコードを更新できます。

const longPathSpecifier = @"\\?";

private void checkFile(string path)
{
    // Add the long-path specifier if it's missing
    string longPath = (path.StartsWith(longPathSpecifier) ? path : longPathSpecifier  + path);

    if (!File.Exists(longPath))
    {
        // Print the original path
         Console.WriteLine("   *  File: " + path + " does not exist.");
    }
}

アップデート:

ファイルI/Oの場合、パス文字列の「\?\」プレフィックスは、すべての文字列解析を無効にし、それに続く文字列をファイルシステムに直接送信するようにWindowsAPIに指示します。たとえば、ファイルシステムが大きなパスとファイル名をサポートしている場合、WindowsAPIによって強制されるMAX_PATHの制限を超える可能性があります。

少なくとも私のシステム(Windows 7を使用)では、長いファイル名はサポートされていないため、上記の解決策が機能するかどうかを確認できません。

更新:機能するソリューションを見つけましたが、かなり醜いです。これが私が擬似コードでしたことです:

  1. パスをディレクトリの配列に分割します
  2. パスの260文字未満の最長部分(MAX_PATH)を取得します。
  3. パスのその部分のDirectoryInfoを作成します(将来の参照用に「dir」)。
  4. パス内の残りのディレクトリについては、次のようにします
    。呼び出しdir.GetDirectories()て、次のディレクトリが結果に含まれているかどうかを確認します
    b。もしそうなら、それに設定dirし、DirectoryInfo掘り続け
    ますc。そうでない場合、パスは存在しません
  5. ファイルに至るまでのすべてのディレクトリを調べたら、呼び出しdir.GetFiles()て、返されたFileInfoオブジェクトにファイルが存在するかどうかを確認します。
于 2012-06-26T15:14:57.757 に答える
11

これは醜く非効率的ですが、MAX_PATH の制限を回避します。

const int MAX_PATH = 260;

private static void checkPath(string path)
{
    if (path.Length >= MAX_PATH)
    {
        checkFile_LongPath(path);
    }
    else if (!File.Exists(path))
    {
        Console.WriteLine("   *  File: " + path + " does not exist.");
    }
}

これが checkFile_LongPath 関数です。

private static void checkFile_LongPath(string path)
{
    string[] subpaths = path.Split('\\');
    StringBuilder sbNewPath = new StringBuilder(subpaths[0]);
    // Build longest subpath that is less than MAX_PATH characters
    for (int i = 1; i < subpaths.Length; i++)
    {
        if (sbNewPath.Length + subpaths[i].Length >= MAX_PATH)
        {
            subpaths = subpaths.Skip(i).ToArray();
            break;
        }
        sbNewPath.Append("\\" + subpaths[i]);
    }
    DirectoryInfo dir = new DirectoryInfo(sbNewPath.ToString());
    bool foundMatch = dir.Exists;
    if (foundMatch)
    {
        // Make sure that all of the subdirectories in our path exist.
        // Skip the last entry in subpaths, since it is our filename.
        // If we try to specify the path in dir.GetDirectories(), 
        // We get a max path length error.
        int i = 0;
        while(i < subpaths.Length - 1 && foundMatch)
        {
            foundMatch = false;
            foreach (DirectoryInfo subDir in dir.GetDirectories())
            {
                if (subDir.Name == subpaths[i])
                {
                    // Move on to the next subDirectory
                    dir = subDir;
                    foundMatch = true;
                    break;
                }
            }
            i++;
        }
        if (foundMatch)
        {
            foundMatch = false;
            // Now that we've gone through all of the subpaths, see if our file exists.
            // Once again, If we try to specify the path in dir.GetFiles(), 
            // we get a max path length error.
            foreach (FileInfo fi in dir.GetFiles())
            {
                if (fi.Name == subpaths[subpaths.Length - 1])
                {
                    foundMatch = true;
                    break;
                }
            }
        }
    }
    // If we didn't find a match, write to the console.
    if (!foundMatch)
    {
        Console.WriteLine("   *  File: " + path + " does not exist.");
    }
}
于 2012-06-26T16:39:17.750 に答える
4

自分で問題が発生したことはありません。別のSO投稿の誰かが、ファイルのハンドルを開くことを提案しているため、そもそも「存在する」チェック全体を回避できます。これにまだ「長いファイル名」の問題があるかどうかわからない:

ここでの2番目の答えです:

ファイル/ディレクトリが存在するかどうかを確認します。より良い方法はありますか?

それが役立つかどうかわからない:P

于 2012-06-26T15:16:38.913 に答える