【问题标题】:Get file size without using System.IO.FileInfo?在不使用 System.IO.FileInfo 的情况下获取文件大小?
【发布时间】:2013-01-18 21:29:44
【问题描述】:

是否可以在完全不使用System.IO.FileInfo 的情况下在C# 中获取文件的大小

我知道您可以分别使用Path.GetFileName(yourFilePath)Path.GetExtension(yourFilePath) 来获取名称和扩展名等其他内容,但显然不是文件大小?有没有其他方法可以在不使用System.IO.FileInfo 的情况下获得文件大小

这样做的唯一原因是,如果我是正确的,FileInfo 获取的信息比我真正需要的要多,因此如果我唯一需要的是文件的大小,那么收集所有这些 FileInfo 需要更长的时间。有更快的方法吗?

【问题讨论】:

  • 过早的优化是万恶之源。使用FileInfo,分析代码,并确定它是否足够快以满足您的需求。如果您已确认它占应用程序运行时间的很大一部分,并且您的应用程序速度慢得令人无法接受,那么请考虑其他选项。
  • 我想是文件大小占用了大部分时间,其他项目基本上是免费的。
  • 过早的优化是万恶之源。这真的会给您带来问题吗?
  • @asawyer 这是假设信息一开始就没有延迟加载。
  • FileInfo 的一个众所周知的问题是它只获取您要求的数据。但是现在很方便,尝试优化它的原因是没有意义的。

标签: c# filepath filesize fileinfo


【解决方案1】:

我使用这两种方法进行了基准测试:

    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;
    }

针对超过 2300 个文件运行,GetFileSizeA 需要 62-63 毫秒才能运行。 GetFileSizeB 花费了 18 秒。

除非有人看到我做错了什么,否则我认为答案很清楚,即哪种方法更快。

有什么方法可以避免实际打开文件吗?

更新

将 FileAttributes.ReadOnly 更改为 FileAttributes.Normal 减少了时间,因此这两种方法的性能相同。

此外,如果您跳过 CloseHandle() 调用,GetFileSizeEx 方法的速度会提高 20-30%,尽管我不知道我是否会建议这样做。

【讨论】:

  • 可以通过使用 FindFirstFileEx 进一步改进并尽可能限制搜索。
  • @SmartK8 你是什么意思限制它?它正在搜索特定的文件名。在基准测试中,我得到了一个目录中所有文件的列表,然后使用完整路径和文件名调用 GetFileSizeA() 或 GetFileSizeB()。
  • 我假设 OP 表示“收集所有这些文件需要更长的时间”表示他正在抓取更多文件。他没有说他必须只在一个目录中查找一个文件。所以我要指出,我同意他在这种情况下应该使用 FindFirstFile (FindNextFile)。或者可能是 FindFirstFileEx。因为它提供了更多选项来指定搜索选项(仅文件夹、大型提取等)
  • @Pete:我在尝试测试您建议的方法时遇到了一些参考错误。我必须为这些调用任何特定的库吗?谢谢。
  • @sergeidav​​e 哎呀。修复了代码。改为将句柄更改为 IntPtr。使用 SafeHandle 需要额外付费,因为它需要发布。
【解决方案2】:

从我做的一个简短测试中,我发现使用 FileStream 比使用 Pete 的 GetFileSizeB 平均慢 1 毫秒(通过网络共享花了我大约 21 毫秒...)。就我个人而言,我更喜欢尽可能保持在 BCL 范围内。

代码很简单:

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

【讨论】:

  • 在我的场景中,这段代码比使用FileInfo慢?
【解决方案3】:

根据this comment

我有一个小型应用程序,它收集大小信息并将其保存到一个数组中......但我经常有 50 万个文件,给予或接受,并且需要一段时间才能浏览所有这些文件(我使用文件信息)。我只是想知道是否有更快的方法...

由于您发现了如此多文件的长度,因此您更有可能从并行化中受益,而不是尝试通过其他方法获取文件大小。 FileInfo 类应该足够好,并且任何改进都可能很小。

另一方面,并​​行化文件大小请求有可能显着提高速度。 (请注意,改进程度主要取决于您的磁盘驱动器,而不是您的处理器,因此结果可能会有很大差异。)

【讨论】:

  • 实际上,如果他从各个目录收集大量文件,他可能会受益于使用 FindFirstFile 和 FindNextFile() 遍历目录中的文件,尽管我没有数字来支持它。
【解决方案4】:

不是直接的答案...因为我不确定使用 .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);

运行它,在我的“程序文件”目录上运行需要 2709 毫秒,该目录大约有 22720 个文件。无论如何,这绝不是懈怠。此外,当我将*.txt 作为GetFiles 方法的第一个参数的过滤器时,它会将时间大幅缩短至461ms。

这在很大程度上取决于您的硬盘驱动器的速度,但我真的不认为 FileInfo 会影响性能。

注意:我认为这仅适用于 .NET 4+

【讨论】:

  • 这有什么关系?您正在使用 OP 不想使用的方法。
  • @aqua 这很重要,因为它表明使用 FileInfo 可能不会大幅降低性能。
  • 那是处理本地文件的时候。使用FileInfo 处理网络文件时速度很慢。有一个代码项目FastFileInfo 解决了这个问题。
  • @Loathing 非常感谢!那个 FastFileInfo 帮助了我,因为我通过网络获得了 23.5k 的 FileInfos,它花了大约 20 分钟。现在只需要 1:09 分钟 O.o!这太棒了!
  • @KeenoraFluffball 好东西。 codeproject 上有一个递归异常。我重写了,放到sourceforge.net上,sourceforge版本应该也快一点。
【解决方案5】:

如果您想在非 Windows 主机上的 .NET Core 或 Mono 运行时上执行此操作,这是一个快速的解决方案:

包括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 上测试了这个正在运行的 .NET Core - 不确定它是否可以在 Windows 上运行 - 考虑到这些是 POSIX 系统调用(并且软件包由 Microsoft 维护),它可能会。如果没有,请结合其他基于 P/Invoke 的答案以涵盖所有平台。

FileInfo.Length 相比,在获取另一个进程/线程正在写入的文件大小时,这给了我更可靠的结果。

【讨论】:

    【解决方案6】:

    你可以试试这个:

    [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;
    }
    

    【讨论】:

    • @Venson:肯定比你的好,我很抱歉;)
    • 当然,但这就是我所说的!这只是一个想法,请在投票前阅读!
    • @Venson 不,这不是你说的。你说他们是一样的。他们不是。你的很糟糕,这可能更烦人,但可能不会(至少)更糟。
    • @sergeidav​​e 两个最好的方法是 GetFileSizeEx()(如上)和 FindFirstFile(这是 FileInfo 使用的)。我不知道一个比另一个有什么特别的性能优势。但是如果性能真的很关键,你应该对这两种方法计时,看看哪个实际上更快。使用 FileInfo 可能一样快。
    • 这与FileStream.Length 相同,只是可读性较差。
    猜你喜欢
    • 1970-01-01
    • 2017-12-20
    • 2016-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多