寫一個工具正好需要用到獲取文件在磁盤上占用空間的大小,先普及一下知識吧
首先說一下“文件大小”和“占用空間”的區別,文件大小是指文件自身的大小,不管這個文件放在哪里大小都不會發生改變,而占用空間是指文件所在分區占用的空間,文件放在不同的分區所占用的空間可能會有所不同,占用空間一般大於等於文件大小。這里我們先做個實驗,
在cmd里輸入如下代碼
fsutil file createnew F:\TestFile.dat 12345678
此時在F盤下創建了一個TestFile.dat的文件,然后把該文件復制到另外一個盤,文件大小和占用空間如圖所示:
可以看出。兩個一樣的文件,在不同分區下。出現了占用空間大小不相同,這涉及到了磁盤文件的存儲機制,下面解釋下原因。
磁盤文件大小以字節(Byte)為單位,在文件不發生改變的情況下,大小不變,而文件在磁盤上占用的空間,是以簇(Cluster)為單位,2^n個扇區組成一個簇,一個簇只能放一個文件,所以文件的占用空間需要對齊到簇大小的整數倍,如果文件大小剛好是簇的倍數。那么文件大小和占用空間是一致的,磁盤分區格式和容量大小決定了簇的大小。所以可能出現文件在不同分區的占用空間大小不一樣。在cmd下可以利用chkdsk命令來查看一個分區的簇大小,例如我這里查看F盤的結果:
這里所看到的分配單元就是簇大小,4096byte。
要計算某個文件占用空間的大小,可以使用如下的公式
簇數 = 向上取整(文件大小/簇大小)
占用空間 = 簇數 * 簇大小
獲取簇的大小可以使用GetDiskFreeSpace這個API來獲取每簇的扇區數和每扇區的字節數。然后相乘即可得到簇大小。
詳細代碼如下:
// GetFileSpaceSize.cpp : Defines the entry point for the console application. // /************************************************************************ * author: HwangBae * created: 2012/07/21 * Blog: http://hwangbae.cnblogs.com/ * Email: hwangbae@live.cn ************************************************************************/ #include <windows.h> #include <tchar.h> #include <stdio.h> #include <math.h> int _tmain(int argc, _TCHAR* argv[]) { if (argc < 2) { _tprintf_s(_T("Usage: GetFileSpaceSize filename\n")); return -1; } // 文件路徑 LPCTSTR szFileName = argv[1]; // 打開文件句柄 HANDLE hFile = ::CreateFile(szFileName, GENERIC_READ | FILE_SHARE_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { _tprintf_s(_T("Failed to create file handle: %s ! error code:%d\n"), szFileName, GetLastError()); return -1; } // 獲取文件大小 UINT64 uFileSize = 0; ::GetFileSizeEx(hFile, reinterpret_cast<PLARGE_INTEGER>(&uFileSize)); ::CloseHandle(hFile); // 獲取磁盤根路徑 TCHAR szVolumePathName[] = _T("C:\\"); ::GetVolumePathName(szFileName, szVolumePathName, sizeof(szVolumePathName) / sizeof(TCHAR)); // 保存簇信息的變量 DWORD dwSectorsPerCluster = 0; DWORD dwBytesPerSector = 0; DWORD dwNumberOfFreeClusters = 0; DWORD dwTotalNumberOfClusters = 0; // 獲取簇信息 if (!::GetDiskFreeSpace( szVolumePathName, //磁盤根路徑 &dwSectorsPerCluster, //每簇的扇區數 &dwBytesPerSector, //每扇區的字節數 &dwNumberOfFreeClusters, //空余簇的數量 &dwTotalNumberOfClusters //全部簇的數量 ) ) { _tprintf_s(_T("Failed to get disk cluster info! error code: %d\n"), GetLastError()); return -1; } // 簇大小 = 每簇的扇區數 * 每扇區的字節數 DWORD dwClusterSize = dwSectorsPerCluster * dwBytesPerSector; // 計算文件占用空間 // 公式:(以字節為單位) // 簇數 = 向上取整(文件大小 / 簇大小) // 占用空間 = 簇數 * 簇大小 UINT64 dwFileSpacesize = static_cast<UINT64>(ceil(uFileSize / static_cast<double>(dwClusterSize)) * dwClusterSize); _tprintf_s(_T("FileName : %s\n"), szFileName); _tprintf_s(_T("FileSize : %I64u Byte\n"), uFileSize); _tprintf_s(_T("FileSpacesSize : %I64u Byte\n"), dwFileSpacesize); return 0; }
Greatest這位朋友提供了一個更簡單的方法。用GetFileInformationByHandleEx這個API和FILE_STANDARD_INFO這個結構來獲取,代碼如下:
// GetFileSpaceSize.cpp : Defines the entry point for the console application. // /************************************************************************ * author: HwangBae * created: 2012/07/23 * Blog: http://hwangbae.cnblogs.com/ * Email: hwangbae@live.cn ************************************************************************/ #include <windows.h> #include <tchar.h> #include <stdio.h> #define CLOSE_HANDLE(handle) \ do \ { \ CloseHandle(handle); \ handle = NULL; \ } while (FALSE) int _tmain(int argc, _TCHAR* argv[]) { if (argc < 2) { _tprintf_s(_T("Usage: GetFileSpaceSize filename\n")); return -1; } // 文件路徑 LPCTSTR szFileName = argv[1]; // 打開文件句柄 HANDLE hFile = ::CreateFile(szFileName, GENERIC_READ | FILE_SHARE_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { _tprintf_s(_T("Failed to create file handle: %s ! error code:%d\n"), szFileName, GetLastError()); return -1; } // 獲取文件大小 UINT64 uFileSize = 0; ::GetFileSizeEx(hFile, reinterpret_cast<PLARGE_INTEGER>(&uFileSize)); FILE_STANDARD_INFO fsi = {0}; if (!::GetFileInformationByHandleEx(hFile, FileStandardInfo, &fsi, sizeof(FILE_STANDARD_INFO))) { _tprintf_s(_T("Failed to get file info! error code:%d\n"), GetLastError()); CLOSE_HANDLE(hFile); return -1; } _tprintf_s(_T("FileName : %s\n"), szFileName); _tprintf_s(_T("FileSize : %I64u Byte\n"), uFileSize); _tprintf_s(_T("FileSpacesSize : %I64u Byte\n"), fsi.AllocationSize); CLOSE_HANDLE(hFile); return 0; }
測試結果:
本文中所涉及的代碼在VS2012下編譯通過,其他環境請自測。如有其他問題,歡迎留言或E-mail。
歡迎轉載本文章,但請標明出處,原文地址:
http://www.cnblogs.com/hwangbae/archive/2012/07/21/2602592.html
如果覺得本文對您有幫助,請支持一下,您的支持是我寫作最大的動力,謝謝。