web-dev-qa-db-ja.com

System.IO.FileInfoを使用せずにファイルサイズを取得しますか?

_System.IO.FileInfo_をまったく使用せずにC#でファイルのsizeを取得することは可能ですか?

Path.GetFileName(yourFilePath)Path.GetExtension(yourFilePath)をそれぞれ使用することで、名前や拡張子などの他のものを取得できることを知っていますが、ファイルサイズではないようです。 _System.IO.FileInfo_を使用せずにファイルサイズを取得する別の方法はありますか?

これの唯一の理由は、私が正しければ、FileInfoが実際に必要な情報よりも多くの情報を取得するため、必要なのがファイルのサイズだけである場合、これらすべてのFileInfoを収集するのに時間がかかることです。より速い方法はありますか?

13
sergeidave

次の2つの方法を使用してベンチマークを実行しました。

    public static uint GetFileSizeA(string filename)
    {
        WIN32_FIND_DATA findData;
        FindFirstFile(filename, out findData);
        return findData.nFileSizeLow;
    }

    public static uint GetFileSizeB(string filename)
    {
        IntPtr handle = CreateFile(
            filename,
            FileAccess.Read,
            FileShare.Read,
            IntPtr.Zero,
            FileMode.Open,
            FileAttributes.ReadOnly,
            IntPtr.Zero);
        long fileSize;
        GetFileSizeEx(handle, out fileSize);
        CloseHandle(handle);
        return (uint) fileSize;
    }

GetFileSizeAは、2300を少し超えるファイルに対して実行すると、実行に62〜63ミリ秒かかりました。 GetFileSizeBは18秒以上かかりました。

私が間違っていることを誰かが見ない限り、どちらの方法が速いかについての答えは明らかだと思います。

実際にファイルを開かないようにする方法はありますか?

更新

FileAttributes.ReadOnlyをFileAttributes.Normalに変更すると、タイミングが短縮され、2つのメソッドのパフォーマンスが同じになりました。

さらに、CloseHandle()呼び出しをスキップすると、GetFileSizeExメソッドが約20〜30%速くなりますが、それをお勧めするかどうかはわかりません。

8
Pete

私が行った短いテストから、FileStreamを使用するとPeteのGetFileSizeBを使用するよりも平均でわずか1ミリ秒遅いことがわかりました(ネットワーク共有で約21ミリ秒かかりました...)。個人的には、できる限りBCLの制限内にとどまるのが好きです。

コードは単純です:

using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    return file.Length;
}
6
Alon Bar

直接的な答えではありません.... NETフレームワークを使用するより速い方法があるかどうかわからないためです。

私が使用しているコードは次のとおりです。

  List<long> list = new List<long>();
  DirectoryInfo di = new DirectoryInfo("C:\\Program Files");
  FileInfo[] fiArray = di.GetFiles("*", SearchOption.AllDirectories);
  foreach (FileInfo f in fiArray)
    list.Add(f.Length);

それを実行すると、約22720ファイルである私の「ProgramFiles」ディレクトリで実行するのに2709msかかりました。それは決して前かがみではありません。さらに、*.txtGetFilesメソッドの最初のパラメーターのフィルターとして、時間を大幅に461msに短縮しました。

これの多くはハードドライブの速度に依存しますが、FileInfoがパフォーマンスを低下させているとは思いません。

注:これは.NET4以降でのみ有効だと思います

3
Jeff Johnson

このコメント

私はサイズ情報を収集して配列に保存する小さなアプリケーションを持っています...しかし、私はしばしば50万のファイルを持っており、それらのすべてのファイルを調べるのに時間がかかります(私はFileInfoを使用しています) 。もっと速い方法があるのだろうかと思っていました...

非常に多くのファイルの長さを見つけているので、別の方法でファイルサイズを取得しようとするよりも、並列化の恩恵を受ける可能性がはるかに高くなります。 FileInfoクラスは十分にである必要があり、改善はわずかである可能性があります。

一方、ファイルサイズの要求を並列化すると、速度が大幅に向上する可能性があります。 (改善の程度は、プロセッサではなくディスクドライブに大きく依存するため、結果は大きく異なる可能性があることに注意してください。)

3
Servy

Windows以外のホストの.NETCoreまたはMonoランタイムでこれを実行する場合は、簡単な「n」ダーティソリューション:

Mono.Posix.NETStandard NuGetパッケージを含めてから、次のようにします...

using Mono.Unix.Native;

private long GetFileSize(string filePath)
{
    Stat stat;
    Syscall.stat(filePath, out stat);
    return stat.st_size;
}

LinuxとmacOSで実行中の.NETCoreをテストしましたが、Windowsで動作するかどうかはわかりませんが、これらが内部のPOSIXシステムコールである(パッケージはMicrosoftによって管理されている)ことを考えると、テストできる可能性があります。そうでない場合は、他のP/Invokeベースの回答と組み合わせて、すべてのプラットフォームをカバーします。

FileInfo.Lengthと比較すると、これにより、別のプロセス/スレッドによってアクティブに書き込まれているファイルのサイズを取得するときに、はるかに信頼性の高い結果が得られます。

1
Mark Beaton

あなたはこれを試すことができます:

[DllImport("kernel32.dll")]
static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);

しかし、それはそれほど改善されていません...

Pinvoke.netから取得したサンプルコードは次のとおりです。

IntPtr handle = CreateFile(
    PathString, 
    GENERIC_READ, 
    FILE_SHARE_READ, 
    0, 
    OPEN_EXISTING, 
    FILE_ATTRIBUTE_READONLY, 
    0); //PInvoked too

if (handle.ToInt32() == -1) 
{
    return; 
}

long fileSize;
bool result = GetFileSizeEx(handle, out fileSize);
if (!result) 
{
    return;
}
0
d--b