【发布时间】:2012-02-17 22:58:07
【问题描述】:
我正在尝试获取大文件 (12gb+) 的文件大小,但我不想打开文件这样做,因为我认为这会占用大量资源。有什么好的API可以做到这一点吗?我在 Windows 环境中。
【问题讨论】:
我正在尝试获取大文件 (12gb+) 的文件大小,但我不想打开文件这样做,因为我认为这会占用大量资源。有什么好的API可以做到这一点吗?我在 Windows 环境中。
【问题讨论】:
您应该调用GetFileSizeEx,它比旧的GetFileSize 更易于使用。您需要通过调用CreateFile 打开文件,但这是一个廉价的操作。您认为打开一个文件(即使是 12GB 文件)代价高昂的假设是错误的。
您可以使用以下函数来完成工作:
__int64 FileSize(const wchar_t* name)
{
HANDLE hFile = CreateFile(name, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile==INVALID_HANDLE_VALUE)
return -1; // error condition, could call GetLastError to find out more
LARGE_INTEGER size;
if (!GetFileSizeEx(hFile, &size))
{
CloseHandle(hFile);
return -1; // error condition, could call GetLastError to find out more
}
CloseHandle(hFile);
return size.QuadPart;
}
还有其他 API 调用会返回文件大小,而不会强制您创建文件句柄,特别是 GetFileAttributesEx。然而,这个函数只是在幕后打开文件是完全合理的。
__int64 FileSize(const wchar_t* name)
{
WIN32_FILE_ATTRIBUTE_DATA fad;
if (!GetFileAttributesEx(name, GetFileExInfoStandard, &fad))
return -1; // error condition, could call GetLastError to find out more
LARGE_INTEGER size;
size.HighPart = fad.nFileSizeHigh;
size.LowPart = fad.nFileSizeLow;
return size.QuadPart;
}
如果您使用 Visual Studio 进行编译并且希望避免调用 Win32 API,那么您可以使用_wstat64。
这是一个基于_wstat64的函数版本:
__int64 FileSize(const wchar_t* name)
{
__stat64 buf;
if (_wstat64(name, &buf) != 0)
return -1; // error, could use errno to find out more
return buf.st_size;
}
如果性能曾经成为您的问题,那么您应该为您所针对的所有平台上的各种选项计时,以便做出决定。不要以为不需要您调用CreateFile 的API 会更快。他们可能是,但你不会知道,直到你计时。
【讨论】:
CreateFile() can be rather slow if you're opening the file on slow media 喜欢网络驱动器,但速度慢是由于存储访问延迟,而不是因为文件很大。
我还担心打开文件和关闭文件只是为了获得它的大小而付出的代价。并决定询问performance counter^,看看这些操作到底有多贵是。
这是使用三种方法对同一文件执行 1 次文件大小查询所花费的周期数。在 2 个文件上进行了测试:150 MB 和 1.5 GB。有 +/- 10% 的波动,因此它们似乎不受实际文件大小的影响。 (显然这取决于 CPU,但它为您提供了一个很好的优势)
CreateFile, GetFileSizeEx, CloseHandle
GetFileAttributesEx
FindFirstFile, FindClose
The GIST with the code used^ 在此处可用。
正如我们从这个高度科学的:) 测试中看到的,最慢的实际上是文件打开器。第二慢的是文件查找器,而获胜者是属性读取器。 现在,就可靠性而言,CreateFile 应该优于其他 2 个。 但我仍然不喜欢打开文件只是为了读取文件大小的概念......除非我是做大小关键的事情,我会选择属性。
PS:当我有时间时,我会尝试读取已打开和正在写入的文件的大小。但不是现在……
【讨论】:
使用FindFirstFile 函数的另一个选项
#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
LPCTSTR lpFileName = L"C:\\Foo\\Bar.ext";
hFind = FindFirstFile(lpFileName , &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("File not found (%d)\n", GetLastError());
return -1;
}
else
{
ULONGLONG FileSize = FindFileData.nFileSizeHigh;
FileSize <<= sizeof( FindFileData.nFileSizeHigh ) * 8;
FileSize |= FindFileData.nFileSizeLow;
_tprintf (TEXT("file size is %u\n"), FileSize);
FindClose(hFind);
}
return 0;
}
【讨论】:
ULARGE_INTEGER 而不是手动旋转ULONGLONG 位,例如:ULARGE_INTEGER ul; ul.LowPart = FindFileData.nFileSizeLow; ul.HighPart = FindFileData.nFileSizeHigh; ULONGLONG FileSize = ul.QuadPart;。此外,%u 在 Windows 上需要 32 位 unsigned int,您需要使用 %Lu 代替 64 位整数。
从 C++17 开始,file_size 作为标准库的一部分。 (然后实现者决定如何有效地完成它!)
【讨论】:
GetFileSize 函数呢?
【讨论】:
GetFileSize() 要求首先打开文件,然后使用该句柄确定文件在文件系统中的位置,以便获取大小。如果你改用FindFirstFile(),它会查询文件系统而不需要打开文件。