web-dev-qa-db-ja.com

C#を使用してファイルが存在するかどうかを判断し、UNCパスを解決する

ファイルが存在するかどうかを判別する関数を記述しようとしています。 2つのメソッドは一貫性のない結果を返すことが証明されています(isFileFound()と比較して、fileExists()は正確な結果を提供するようです。これは誤検知を返します-インスタンスを作成しようとすると例外が発生します)。

protected bool isFileFound(string path, string fileName)
    {
        System.IO.FileInfo fi = null;

        bool found = false;
        try
        {
            fi = new System.IO.FileInfo(path + fileName);
            found = true;
        }
        catch (Exception e)
        {
            baselogger.Fatal(e.Message + " " + e.StackTrace + " \n" + path + fileName);
        }

        return found;
    }

    protected bool fileExists(string path, string pattern)
    {
        bool success = false;

        try
        {
            success = File.Exists(path + pattern);
        }
        catch (Exception e)
        {
            baselogger.Warn(e.Message + " " + e.StackTrace + " " + e.Source);
        }

        return success;
    }

どちらも次の構文のUNCパスを解決できないようです:\\ abcserver\c $\xyzfolder\foo.bar

これらの方法でuncパスが失敗する理由を教えていただければ幸いです。

27
steve_mtl

存在しないファイルのFileInfoを作成できます。ただし、FileInfo.Existsプロパティを確認して、ファイルが存在するかどうかを確認できます。例:

FileInfo fi = new FileInfo(somePath);
bool exists = fi.Exists;

更新:短いテストでは、これはUNCパスでも機能しました。このような:

FileInfo fi = new FileInfo(@"\\server\share\file.txt");
bool exists = fi.Exists;

アプリケーションを実行しているアカウントが共有にアクセスできることを確認しますか? (デフォルトでは)共有「c $」にアクセスするには管理者権限が必要だと思います。

37
M4N

この質問を参照してください:
。NETでファイルへのアクセスが拒否されているかどうかを簡単に確認するにはどうすればよいですか?

その質問の短いバージョンは、ファイルシステムが揮発性であるため、そうしないことです。ファイルを開こうとして、失敗した場合は例外をキャッチしてください。

isFileFoundメソッドが機能しない理由は、使用しているFileInfo構造体がファイルの作成にも使用できるためです。存在しないファイルに必要な情報を含むFileInfoオブジェクトを作成し、それを.Create()メソッドとして呼び出すと、必要なプロパティをすべて一度に設定できます。

UNCパスが失敗する理由は、1)アプリを実行しているユーザーから管理共有にアクセスする権限の問題、または2)_$_記号がスローされているためと思われますメソッドが正しく入力されていないか、基になる.Exists()実装のバグが原因です。

更新:

この提案を投稿すると、ほとんどの場合、例外のパフォーマンスに関する不満が出ます。それについて話しましょう。はい、例外の処理は高価です:非常に高価です。プログラミングで実行できる処理が少し遅いです。しかし、あなたはそれらのいくつかのものが何であるか知っていますか?ディスクおよびネットワークI/O。以下は、ディスクI/OとネットワークI/Oのコストを示すリンクです。

https://Gist.github.com/jboner/2841832

レイテンシ比較番号
 -------------------------- 
 L1キャッシュリファレンス0.5 ns 
ブランチmispredict 5 ns 
 L2キャッシュリファレンス7 ns 14x L1キャッシュ
 Mutexロック/ロック解除25 ns 
メインメモリリファレンス100 ns 20x L2キャッシュ、200x L1キャッシュ
圧縮1KバイトとZippy 3,000 ns 
 1Kバイトを1 Gbpsネットワーク経由で送信10,000 ns 0.01 ms 
 4KをSSDからランダムに読み取り* 150,000 ns 0.15 ms 
メモリから1 MBを順次読み取り250,000 ns 0.25 ms 
同じデータセンター内での往復500,000 ns 0.5 ms 
 SSDから順次1 MBを読み取る* 1,000,000 ns 1 ms 4Xメモリ
ディスクシーク10,000,000 ns 10 ms 20xデータセンターラウンドトリップ
ディスク20,000から1 MBを順次読み取り、 000 ns 20 ms 80xメモリ、20X SSD 
パケット送信CA->オランダ-> CA 150,000,000 ns 150 ms

ナノ秒単位で考えるのがあなたのものではない場合は、1つのCPUサイクルを1秒として正規化し、そこからスケーリングする別のリンクを次に示します。

http://blog.codinghorror.com/the-infinite-space-between-words/

 1 CPUサイクル0.3 ns 1 s 
レベル1キャッシュアクセス0.9 ns 3 s 
レベル2キャッシュアクセス2.8 ns 9 s 
レベル3キャッシュアクセス12.9 ns 43 s 
メインメモリアクセス120 ns 6分
ソリッドステートディスクI/O 50〜150μs2〜6日
回転ディスクI/O 1〜10 ms 1〜12数か月
インターネット:SFからNYCに40ミリ秒4年
インターネット:SFから英国に81ミリ秒8年
インターネット:SFからAUSに183ミリ秒19年
 OS仮想。再起動4秒423年
 SCSIコマンドタイムアウト30秒3000年
ハードウェア仮想化。再起動40秒4000年
物理システムの再起動5 m 32ミレニア

例外の最良のシナリオでも、ディスクからの最初の応答を待つ間、少なくとも480回メモリにアクセスできます。これは、非常に高速なSSDを想定しています。私たちの多くは、回転するハードドライブを必要としています。

そして、それは話の始まりにすぎません。 .Exists()を使用すると、この追加のコストが発生します(これは追加料金です:ファイルを開くときに同じ作業を再度行う必要があります) )everyの試行時。ディスクがファイルテーブルでファイルを探しに行く必要があるので、ファイルが存在するかどうかにかかわらず、このコストを支払います。例外メソッドを使用すると、障害が発生した場合にのみ、コールスタックの巻き戻しの追加コストを支払うだけです。

つまり、はい。例外は恐ろしくコストがかかります。しかし、ディスクチェックと比較すると、それはまだ高速です。わずかなマージンではありません。ありがたいことに、これでアプリの全体的なパフォーマンスが向上することはほとんどありません...しかし、この特定のタスクについては、「例外は遅い」という議論に取り掛かりたいと思います。

13
Joel Coehoorn

これは当てはまる場合とそうでない場合がありますが、いずれかのケースでパスとファイル名を誤って結合している可能性があります。

この:

成功= File.Exists(パス+パターン);

対:

成功= File.Exists(Path.Join(path、pattern));

3
llamaoo7

編集:さて、私はfile.existsが問題なく動作することを認識しました。それは間違いなく推奨される方法です。以下のコードは、共有に別のドメインアカウントでアクセスする必要がある場合に、Windowsにユーザーに認証を求めるプロンプトを表示するオプションを提供します。いつか誰かを助けるかもしれないので、ここにコードを残しておきます。

異なる資格情報を使用してUNCパスまたは管理共有にアクセスする必要がある場合: [〜#〜] msdn [〜#〜]

bootstrap WNetAddConnection2を使用するには、次のコードを使用します。

using System;
using System.Runtime.InteropServices;

namespace Win32Api
{
    public enum ResourceScope
    {
        RESOURCE_CONNECTED = 1,
        RESOURCE_GLOBALNET,
        RESOURCE_REMEMBERED,
        RESOURCE_RECENT,
        RESOURCE_CONTEXT
    };

    public enum ResourceType
    {
        RESOURCETYPE_ANY,
        RESOURCETYPE_DISK,
        RESOURCETYPE_PRINT,
        RESOURCETYPE_RESERVED = 8
    };

    [Flags]
    public enum ResourceUsage
    {
        RESOURCEUSAGE_CONNECTABLE = 0x00000001,
        RESOURCEUSAGE_CONTAINER = 0x00000002,
        RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
        RESOURCEUSAGE_SIBLING = 0x00000008,
        RESOURCEUSAGE_ATTACHED = 0x00000010,
        RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE |
                             RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
    };

    public enum ResourceDisplayType
    {
        RESOURCEDISPLAYTYPE_GENERIC,
        RESOURCEDISPLAYTYPE_DOMAIN,
        RESOURCEDISPLAYTYPE_SERVER,
        RESOURCEDISPLAYTYPE_SHARE,
        RESOURCEDISPLAYTYPE_FILE,
        RESOURCEDISPLAYTYPE_GROUP,
        RESOURCEDISPLAYTYPE_NETWORK,
        RESOURCEDISPLAYTYPE_ROOT,
        RESOURCEDISPLAYTYPE_SHAREADMIN,
        RESOURCEDISPLAYTYPE_DIRECTORY,
        RESOURCEDISPLAYTYPE_TREE,
        RESOURCEDISPLAYTYPE_NDSCONTAINER
    };

    [StructLayout(LayoutKind.Sequential)]
    public class NetResource
    {
        public ResourceScope Scope;
        public ResourceType Type;
        public ResourceDisplayType DisplayType;
        public ResourceUsage Usage;
        public string LocalName;
        public string RemoteName;
        public string Comment;
        public string Provider;
    };

    [Flags]
    public enum AddConnectionOptions
    {
        CONNECT_UPDATE_PROFILE = 0x00000001,
        CONNECT_UPDATE_RECENT = 0x00000002,
        CONNECT_TEMPORARY = 0x00000004,
        CONNECT_INTERACTIVE = 0x00000008,
        CONNECT_Prompt = 0x00000010,
        CONNECT_NEED_DRIVE = 0x00000020,
        CONNECT_REFCOUNT = 0x00000040,
        CONNECT_REDIRECT = 0x00000080,
        CONNECT_LOCALDRIVE = 0x00000100,
        CONNECT_CURRENT_MEDIA = 0x00000200,
        CONNECT_DEFERRED = 0x00000400,
        CONNECT_RESERVED = unchecked((int)0xFF000000),
        CONNECT_COMMANDLINE = 0x00000800,
        CONNECT_CMD_SAVECRED = 0x00001000,
        CONNECT_CRED_RESET = 0x00002000
    }

    public static class NativeMethods
    {
        [DllImport("mpr.dll", EntryPoint = "WNetAddConnection2")]
        public static extern int WNetAddConnection2(
            NetResource netResource, string password,
            string username, AddConnectionOptions options);

        [DllImport("mpr.dll")]
        public static extern int WNetCancelConnection2(string name, int flags,
        bool force);

    }
}
1
HAL9000

だから私は

bool success = File.Exists(path + Filename);

fileInfoルートを使用するのではなく、オプション。

すべての提案をありがとう!

1
steve_mtl

これはあなたを助けることができます:
http://www.codeplex.com/FileDirectoryPath
NDepend.Helpers.FilePathDirectoryは、「パス有効性チェックAPI」があり、便利な場合があります。

1
Click Ok