【问题标题】:Finding correct filesize over 4gb in windows在 Windows 中找到超过 4gb 的正确文件大小
【发布时间】:2013-02-18 23:38:21
【问题描述】:

我已使用此 c++ 代码在 windows 中查找某些文件的文件大小(使用 Visual Studio):

(p_findFileData->nFileSizeHigh * MAXDWORD) + p_findFileData->nFileSizeLow);

如果文件大于 4gb,这并没有给我正确的文件大小。经过一番研究,我尝试了:

(p_findFileData->nFileSizeHigh * (MAXDWORD+1)) + p_findFileData->nFileSizeLow); 

当我读到 nfilesizehigh 和 nfilesizelow 分别是 32 位的文件大小的 64 位值时,如果文件大小值大于 32 位,我们将 maxdword(在我的情况下为 0xffffffff)乘以 nfilesizehigh。第二种解决方案也不起作用,并且给了我比原来更小的尺寸。我又试了一次:

ULONGLONG FileSize = (FindFileData.nFileSizeHigh * 4294967296) + FindFileData.nFileSizeLow;

它奏效了。此外,我还使用了另一种解决方案来获取文件大小:

 ULONGLONG FileSize = FindFileData.nFileSizeHigh;
 FileSize <<= sizeof( FindFileData.nFileSizeHigh ) *8; 
 FileSize |= FindFileData.nFileSizeLow;

上述解决方案也有效:

我想知道为什么前两个解决方案不起作用,如果可能的话,我想知道最后一个解决方案的解释,因为我想知道代码的内部工作。非常感谢您的帮助。

【问题讨论】:

    标签: c++ windows visual-studio winapi


    【解决方案1】:

    使用ULARGE_INTEGER 结构来组合值,而不是尝试手动计算/移动它们:

    ULARGE_INTEGER ul;
    ul.HighPart = p_findFileData->nFileSizeHigh;
    ul.LowPart = p_findFileData->nFileSizeLow;
    ULONGLONG FileSize = ul.QuadPart;
    

    【讨论】:

      【解决方案2】:

      我会使用:

      ULONGLONG FileSize = (static_cast<ULONGLONG>(FindFileData.nFileSizeHigh) <<
                            sizeof(FindFileData.nFileSizeLow) *8) |
                           FindFileData.nFileSizeLow;
      

      在 32 位值进入 FileSize 变量之前,必须转换为 ULONGLONG 才能将 32 位值转换为 64 位值。

      您当然也可以使用乘法 - 但它在任何特定方面都不是“更好”,而且很可能会慢一点[在这种特殊情况下可能没什么大不了,但使用乘法没有任何好处我可以看到。

      现在对于“不工作”的变体:

      即使这确实有效:

       (p_findFileData->nFileSizeHigh * MAXDWORD) + p_findFileData->nFileSizeLow);
      

      你会得到错误的值,因为MAXDWORD 小于 4GB,所以你最终会得到错误的值 [是的,它可能很接近,但它至少会出错 1 个字节,可能是更多]。但是,由于我们处理的是 32 位值,它实际上变成了:

       -p_findFileData->nFileSizeHigh + p_findFileData->nFileSizeLow;
      

      因为MAXDWORD 与 -1 相同(是的,它很可能是一个无符号值,但如果您以这种方式使值溢出,它的行为与负符号值相同)。

      这个在数学上是正确的,但由于它溢出了一个 32 位值,所以它不起作用。

       (p_findFileData->nFileSizeHigh * (MAXDWORD+1)) + p_findFileData->nFileSizeLow);
      

      所以你得到low part + (0 * high part) 这当然是不正确的。

      使用演员表,这将起作用:

       static_cast<ULONGLONG>(p_findFileData->nFileSizeHigh) * (MAXDWORD+1) +
       p_findFileData->nFileSizeLow;
      

      【讨论】:

      • 嗨垫子,非常感谢您的详细回答。我很感激。我也有一些问题,例如:为什么我们在不工作的变体中将 filesizehigh 乘以 MAXDWORD 或 4gb?这背后的逻辑是什么?在第一个代码中,为什么我们将 filesizehigh 乘以 8?
      • MAXDWORD = (2^32)-1` [其中^ = 的幂次方,不是 C 的 XOR 符号],因此距离 4GB 相差一个。 sizeof(FileSizeHigh) * 8 是 FileSizeHigh` (32) 中的位数。左移 (&lt;&lt;) 与乘以 2^32 相同。实际上,我认为正确的做法实际上是移动 sizeof(FileSizeLow) * 8,因为这是低位的位数 - 它也是 32 位,但如果有人将 FileSizeLowFileSizeHigh 更改为64 位,数学将继续工作[除非你需要比ULONGLONG 更大的东西来存储它。
      • 我试过了,但它不起作用:static_cast(p_findFileData->nFileSizeHigh) * (MAXDWORD+1) + p_findFileData->nFileSizeLow;
      • 你可能需要一个(static_cast&lt;ULONGLONG&gt;(MAXDWORD)+1)来避免它变成0(这有点像汽车上的里程表,如果你已经跑了99999英里,下次它会翻转到00000 -与所有二进制文件相同)。
      • :) 是的,昨天试过了,明白发生了什么。感谢 Mats 的解释。
      【解决方案3】:
      (p_findFileData->nFileSizeHigh * MAXDWORD) + p_findFileData->nFileSizeLow);
      

      这不会给出正确的结果,因为 MAXDWORD 是错误的值。

      (p_findFileData->nFileSizeHigh * (MAXDWORD+1)) + p_findFileData->nFileSizeLow); 
      

      这不起作用,因为您在将类型转换为足以容纳该值的大小之前将 +1 添加到 MAXDWORD。

      (FindFileData.nFileSizeHigh * 4294967296) + FindFileData.nFileSizeLow;
      

      这是因为4294967296 是 64 位类型。

      FileSize <<= sizeof( FindFileData.nFileSizeHigh ) *8; 
      FileSize |= FindFileData.nFileSizeLow;
      

      这是有效的,因为您将高位移动 32(这与乘以 4294967296 相同),然后使用按位或“添加”低位。

      【讨论】:

      • 感谢您的回复托马斯。你能告诉我为什么我们在最后一个代码中将 filesizehigh 乘以 8 以及为什么第一个代码中的 MAXDWORD 大小错误?如果尺寸不对应该怎么办?如果可能,请告诉我。
      • MAXDWORD 是 4294967295。正确的值是 4294967296。但是 MAXDWORD+1 是 0(如果是 2s 补码)而不是 4294967296。为了能够加 1 并得到 4294967296 作为结果,您必须强制转换它首先转换为 64 位值。
      • 就我个人而言,我认为像这样对魔法值进行硬编码是个坏主意。需要您的解释性评论应该突出这个特殊的神奇价值的问题。在我看来,将FindFileData.nFileSizeHigh 转换为ULONGLONG,将其向左移动并添加FindFileData.nFileSizeLow 会更明智(也更容易理解)
      • 你不会在这里绕过魔法,你的魔法数字是 8、32 还是 2^32 都没有区别。
      • 有道理...非常感谢 Thomas 和 Nik 的投入
      【解决方案4】:

      我遇到了完全相同的问题,而且对于我的代码来说答案非常简单。只需将 MAXDWORD 转换为 64 位 int(例如 uint64_t)。这背后的解释可以在我收到的here的答案中找到。

      【讨论】:

        猜你喜欢
        • 2010-09-11
        • 2012-03-13
        • 2015-11-11
        • 2010-10-16
        • 2021-09-09
        • 1970-01-01
        • 1970-01-01
        • 2017-10-18
        • 1970-01-01
        相关资源
        最近更新 更多