我想用C++打印一个文件大小。我的输入是以字节为单位的,如果它超过1024
,我想在KiB
中打印,如果超过1024*1024
,则在MiB
中打印,等等。或者,对于1000
和更高版本,它应该在KB
中打印,以此类推
它还应该有一个分数部分,这样我就可以区分1.5 GiB
和1.2 GiB
。
我所知道的是,我可以用对数来计算选择哪个单位。所以如果是log_1024(x) >= 1
,那么它应该在KiB
中。这样我就可以避免不必要的循环。
此外,我已经有了打印分数的功能:
std::string stringifyFraction(unsigned numerator,
unsigned denominator,
unsigned precision);
对数基数1000
或1024
确实可以用于确定正确的单位。我们实际上只需要对数的整数部分,所以小数点前面的部分。在现代硬件上,整数对数可以在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;
}