如何在Java中将字节大小转换为人类可读的格式?
比如1024应该变成“1 Kb”,1024*1024应该变成“1 Mb”。
我有点厌倦了为每个项目写这个实用方法。在Apache Commons中有这样的静态方法吗?
如何在Java中将字节大小转换为人类可读的格式?
比如1024应该变成“1 Kb”,1024*1024应该变成“1 Mb”。
我有点厌倦了为每个项目写这个实用方法。在Apache Commons中有这样的静态方法吗?
当前回答
如果你使用Android,你可以简单地使用Android .text.format. formatter . formatfilesize()。它的优点是易于使用,并且它取决于区域设置,以便为用户更好地显示它。缺点是它不处理EB,而且它只用于公制单位(每个Kilo是1000字节,不能作为1024字节使用)。
或者,这里有一个基于这篇热门文章的解决方案:
interface BytesFormatter {
/**called when the type of the result to format is Long. Example: 123KB
* @param unitPowerIndex the unit-power we need to format to. Examples: 0 is bytes, 1 is kb, 2 is mb, etc...
* available units and their order: B,K,M,G,T,P,E
* @param isMetric true if each kilo==1000, false if kilo==1024
* */
fun onFormatLong(valueToFormat: Long, unitPowerIndex: Int, isMetric: Boolean): String
/**called when the type of the result to format is Double. Example: 1.23KB
* @param unitPowerIndex the unit-power we need to format to. Examples: 0 is bytes, 1 is kb, 2 is mb, etc...
* available units and their order: B,K,M,G,T,P,E
* @param isMetric true if each kilo==1000, false if kilo==1024
* */
fun onFormatDouble(valueToFormat: Double, unitPowerIndex: Int, isMetric: Boolean): String
}
/**
* formats the bytes to a human readable format, by providing the values to format later in the unit that we've found best to fit it
*
* @param isMetric true if each kilo==1000, false if kilo==1024
* */
fun bytesIntoHumanReadable(
@IntRange(from = 0L) bytesToFormat: Long, bytesFormatter: BytesFormatter,
isMetric: Boolean = true
): String {
val units = if (isMetric) 1000L else 1024L
if (bytesToFormat < units)
return bytesFormatter.onFormatLong(bytesToFormat, 0, isMetric)
var bytesLeft = bytesToFormat
var unitPowerIndex = 0
while (unitPowerIndex < 6) {
val newBytesLeft = bytesLeft / units
if (newBytesLeft < units) {
val byteLeftAsDouble = bytesLeft.toDouble() / units
val needToShowAsInteger =
byteLeftAsDouble == (bytesLeft / units).toDouble()
++unitPowerIndex
if (needToShowAsInteger) {
bytesLeft = newBytesLeft
break
}
return bytesFormatter.onFormatDouble(byteLeftAsDouble, unitPowerIndex, isMetric)
}
bytesLeft = newBytesLeft
++unitPowerIndex
}
return bytesFormatter.onFormatLong(bytesLeft, unitPowerIndex, isMetric)
}
Sample usage:
// val valueToTest = 2_000L
// val valueToTest = 2_000_000L
// val valueToTest = 2_000_000_000L
// val valueToTest = 9_000_000_000_000_000_000L
// val valueToTest = 9_200_000_000_000_000_000L
val bytesToFormat = Random.nextLong(Long.MAX_VALUE)
val bytesFormatter = object : BytesFormatter {
val numberFormat = NumberFormat.getNumberInstance(Locale.ROOT).also {
it.maximumFractionDigits = 2
it.minimumFractionDigits = 0
}
private fun formatByUnit(formattedNumber: String, threePowerIndex: Int, isMetric: Boolean): String {
val sb = StringBuilder(formattedNumber.length + 4)
sb.append(formattedNumber)
val unitsToUse = "B${if (isMetric) "k" else "K"}MGTPE"
sb.append(unitsToUse[threePowerIndex])
if (threePowerIndex > 0)
if (isMetric) sb.append('B') else sb.append("iB")
return sb.toString()
}
override fun onFormatLong(valueToFormat: Long, unitPowerIndex: Int, isMetric: Boolean): String {
return formatByUnit(String.format("%,d", valueToFormat), unitPowerIndex, isMetric)
}
override fun onFormatDouble(valueToFormat: Double, unitPowerIndex: Int, isMetric: Boolean): String {
//alternative for using numberFormat :
//val formattedNumber = String.format("%,.2f", valueToFormat).let { initialFormattedString ->
// if (initialFormattedString.contains('.'))
// return@let initialFormattedString.dropLastWhile { it == '0' }
// else return@let initialFormattedString
//}
return formatByUnit(numberFormat.format(valueToFormat), unitPowerIndex, isMetric)
}
}
Log.d("AppLog", "formatting of $bytesToFormat bytes (${String.format("%,d", bytesToFormat)})")
Log.d("AppLog", bytesIntoHumanReadable(bytesToFormat, bytesFormatter))
Log.d("AppLog", "Android:${android.text.format.Formatter.formatFileSize(this, bytesToFormat)}")
其他回答
我通常是这样做的:
public static String getFileSize(double size) {
return _getFileSize(size,0,1024);
}
public static String _getFileSize(double size, int i, double base) {
String units = " KMGTP";
String unit = (i>0)?(""+units.charAt(i)).toUpperCase()+"i":"";
if(size<base)
return size +" "+unit.trim()+"B";
else {
size = Math.floor(size/base);
return _getFileSize(size,++i,base);
}
}
有趣的事实:这里发布的原始代码片段是Stack Overflow上被复制最多的Java代码片段,它是有缺陷的。它被修好了,但却变得一团糟。 本文的完整故事:有史以来复制最多的堆栈溢出代码片段是有缺陷的!
来源:格式化字节大小到人类可读的格式|编程。指南
SI(1 k = 1,000)
public static String humanReadableByteCountSI(long bytes) {
if (-1000 < bytes && bytes < 1000) {
return bytes + " B";
}
CharacterIterator ci = new StringCharacterIterator("kMGTPE");
while (bytes <= -999_950 || bytes >= 999_950) {
bytes /= 1000;
ci.next();
}
return String.format("%.1f %cB", bytes / 1000.0, ci.current());
}
二进制(1's = 1,024)
public static String humanReadableByteCountBin(long bytes) {
long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
if (absB < 1024) {
return bytes + " B";
}
long value = absB;
CharacterIterator ci = new StringCharacterIterator("KMGTPE");
for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
value >>= 10;
ci.next();
}
value *= Long.signum(bytes);
return String.format("%.1f %ciB", value / 1024.0, ci.current());
}
示例输出:
SI BINARY
0: 0 B 0 B
27: 27 B 27 B
999: 999 B 999 B
1000: 1.0 kB 1000 B
1023: 1.0 kB 1023 B
1024: 1.0 kB 1.0 KiB
1728: 1.7 kB 1.7 KiB
110592: 110.6 kB 108.0 KiB
7077888: 7.1 MB 6.8 MiB
452984832: 453.0 MB 432.0 MiB
28991029248: 29.0 GB 27.0 GiB
1855425871872: 1.9 TB 1.7 TiB
9223372036854775807: 9.2 EB 8.0 EiB (Long.MAX_VALUE)
实际上,兆字节已经足够人类阅读了。
long l = 1367343104l;
String s = String.format("%dm", l / 1024 / 1024);
1304米
下面是上面Java正确的共识答案的c# .NET等价版本 (下面还有一个代码更短的例子):
public static String BytesNumberToHumanReadableString(long bytes, bool SI1000orBinary1024)
{
int unit = SI1000orBinary1024 ? 1000 : 1024;
if (bytes < unit)
return bytes + " B";
int exp = (int)(Math.Log(bytes) / Math.Log(unit));
String pre = (SI1000orBinary1024 ? "kMGTPE" : "KMGTPE")[(exp - 1)] + (SI1000orBinary1024 ? "" : "i");
return String.Format("{0:F1} {1}B", bytes / Math.Pow(unit, exp), pre);
}
从技术上讲,如果我们坚持使用国际单位制,这个程序适用于任何常规的数字使用。专家们还给出了许多不错的答案。假设您正在对gridview上的数字进行数据绑定,有必要从它们中查看性能优化例程。
PS:这个帖子是因为当我在做一个c#项目时,这个问题/答案出现在谷歌搜索的顶部。
Kotlin版本通过扩展属性
如果您正在使用Kotlin,那么通过这些扩展名属性格式化文件大小非常容易。它是无循环的,完全基于纯数学。
HumanizeUtils.kt
import java.io.File
import kotlin.math.log2
import kotlin.math.pow
/**
* @author aminography
*/
val File.formatSize: String
get() = length().formatAsFileSize
val Int.formatAsFileSize: String
get() = toLong().formatAsFileSize
val Long.formatAsFileSize: String
get() = log2(if (this != 0L) toDouble() else 1.0).toInt().div(10).let {
val precision = when (it) {
0 -> 0; 1 -> 1; else -> 2
}
val prefix = arrayOf("", "K", "M", "G", "T", "P", "E", "Z", "Y")
String.format("%.${precision}f ${prefix[it]}B", toDouble() / 2.0.pow(it * 10.0))
}
用法:
println("0: " + 0.formatAsFileSize)
println("170: " + 170.formatAsFileSize)
println("14356: " + 14356.formatAsFileSize)
println("968542985: " + 968542985.formatAsFileSize)
println("8729842496: " + 8729842496.formatAsFileSize)
println("file: " + file.formatSize)
结果:
0: 0 B
170: 170 B
14356: 14.0 KB
968542985: 923.67 MB
8729842496: 8.13 GB
file: 6.15 MB