我想用 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);
对数底
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;
}