zoukankan      html  css  js  c++  java
  • 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
    如果觉得本文对您有帮助,请支持一下,您的支持是我写作最大的动力,谢谢。


  • 相关阅读:
    Sass 初级学习三
    Sass 初级学习二
    Sass 初级学习一
    数据库0,1和flase true的转换
    Mybatis逆向工程-mybatis generator
    码畜生活现状
    【抽五分钟】使用VuePress创建在线文档中心
    面经整理
    C#引用fo-dicom读取dicom文件异常
    windows10更新后, VMWare无法启动虚拟机: vmdb-14 pipe connection has been broken
  • 原文地址:https://www.cnblogs.com/hwangbae/p/2602592.html
Copyright © 2011-2022 走看看