如何在没有循环的情况下用C++打印人类可读的文件大小



我想用C++打印一个文件大小。我的输入是以字节为单位的,如果它超过1024,我想在KiB中打印,如果超过1024*1024,则在MiB中打印,等等。或者,对于1000和更高版本,它应该在KB中打印,以此类推

它还应该有一个分数部分,这样我就可以区分1.5 GiB1.2 GiB

我所知道的是,我可以用对数来计算选择哪个单位。所以如果是log_1024(x) >= 1,那么它应该在KiB中。这样我就可以避免不必要的循环。

此外,我已经有了打印分数的功能:

std::string stringifyFraction(unsigned numerator,
unsigned denominator,
unsigned precision);

对数基数10001024确实可以用于确定正确的单位。我们实际上只需要对数的整数部分,所以小数点前面的部分。在现代硬件上,整数对数可以在O(1)中计算,因此这将比使用for循环来获得正确的单位稍快。在这里你可以找到如何有效地计算一个数字的整数对数。

如果积分部分是0,我们在B中打印,在KiB中打印1,等等。我们可以创建一个查找表,其中关键字是我们的对数:

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) noexcept
{
constexpr const 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, ...}
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;
}

最新更新