如何在 C++ 中不循环地打印人类可读的文件大小

问题描述 投票:0回答:1

我想用 C++ 打印文件大小。我的输入以字节为单位,如果超过

KiB
,我想在
1024
中打印它,如果超过
MiB
,则在
1024*1024
中打印,等等。或者,它应该在
KB
中打印
1000
和以上,依此类推。

它还应该有一个小数部分,以便我可以区分

1.5 GiB
1.2 GiB

我所知道的是,我可以使用对数来计算选择哪个单位。因此,如果

log_1024(x) >= 1
那么它应该在
KiB
中。这样我就可以避免不必要的循环。

另外,我已经有一个打印分数的功能了

std::string stringifyFraction(unsigned numerator,
                              unsigned denominator,
                              unsigned precision);
c++ file c++17 radix logarithm
1个回答
4
投票

对数底

1000
1024
确实可以用来确定正确的单位。实际上我们只需要对数的整数部分,即小数点前面的部分。在现代硬件上,整数对数可以在
O(1)
中计算,因此这比使用
for
循环获得正确的单位要快一些。 在这里您可以了解如何有效计算数字的整数对数

如果整数部分是

0
,我们在
B
中打印,对于
1
KiB
中打印,等等。我们可以创建一个查找表,其中键是我们的对数:

constexpr const char FILE_SIZE_UNITS[8][3] {
    "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"
};

请注意,该表使用

3
作为内部大小,因为所有字符串均以 null 结尾。 您可能还想知道为什么查找表不包含
KiB
单位。这是因为中间的
i
是恒定的,不需要成为表格的一部分。此外,文件大小有两种不同的单位系统,一种是基本
1000
,另一种是基本
1024
。请参阅文件大小单位:“KiB”与“KB”与“kB”。我们可以轻松地在一个函数中支持这两种功能。

然后我们可以实现我们的

stringifyFileSize
方法,如下所示:

// use SFINAE to only allow base 1000 or 1024
template <size_t BASE = 1024, std::enable_if_t<BASE == 1000 || BASE == 1024, int> = 0>
std::string stringifyFileSize(uint64_t size, unsigned precision = 0)
{
    static constexpr char FILE_SIZE_UNITS[8][3] {
        "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"
    };

    // The linked post about computing the integer logarithm
    // explains how to compute this.
    // This is equivalent to making a table: {1, 1000, 1000 * 1000, ...}
    // or {1, 1024, 1024 * 1024, ...}
    static constexpr auto powers = makePowerTable<Uint, BASE>();

    unsigned unit = logFloor<BASE>(size);

    // Your numerator is size, your denominator is 1000^unit or 1024^unit.
    std::string result = stringifyFraction(size, powers[unit], precision);
    result.reserve(result.size() + 5);

    // Optional: Space separating number from unit. (usually looks better)
    result.push_back(' ');
    char first = FILE_SIZE_UNITS[unit][0];
    // Optional: Use lower case (kB, mB, etc.) for decimal units
    if constexpr (BASE == 1000) {
        first += 'a' - 'A';
    }
    result.push_back(first);

    // Don't insert anything more in case of single bytes.
    if (unit != 0) {
        if constexpr (BASE == 1024) {
            result.push_back('i');
        }
        result.push_back(FILE_SIZE_UNITS[unit][1]);
    }

    return result;
}
© www.soinside.com 2019 - 2024. All rights reserved.