VC++實現獲取文件占用空間大小的兩種方法(非文件大小)


寫一個工具正好需要用到獲取文件在磁盤上占用空間的大小,先普及一下知識吧

首先說一下“文件大小”和“占用空間”的區別,文件大小是指文件自身的大小,不管這個文件放在哪里大小都不會發生改變,而占用空間是指文件所在分區占用的空間,文件放在不同的分區所占用的空間可能會有所不同,占用空間一般大於等於文件大小。這里我們先做個實驗,

在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
如果覺得本文對您有幫助,請支持一下,您的支持是我寫作最大的動力,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM