1
votes

Failed to find the similar topic in StackOverflow, the question is similar to How to convert byte size into human readable format in java?

How to convert byte size into human-readable format in Java? Like 1024 should become "1 Kb" and 1024*1024 should become "1 Mb".

I am kind of sick of writing this utility method for each project. Are there any static methods in Apache Commons for this?

But for Kotlin, had prepared something based on the accepted answer there and wanted to share it but thought should posting it in a separate thread is better to not distract people on that thread so others also can comment or post other idiomatic Kotlin answers here

4

4 Answers

1
votes

There's a more concise solution:

fun bytesToHumanReadableSize(bytes: Double) = when {
        bytes >= 1 shl 30 -> "%.1f GB".format(bytes / (1 shl 30))
        bytes >= 1 shl 20 -> "%.1f MB".format(bytes / (1 shl 20))
        bytes >= 1 shl 10 -> "%.0f kB".format(bytes / (1 shl 10))
        else -> "$bytes bytes"
    }
5
votes

Based on this Java code by @aioobe:

fun humanReadableByteCountBin(bytes: Long) = when {
    bytes == Long.MIN_VALUE || bytes < 0 -> "N/A"
    bytes < 1024L -> "$bytes B"
    bytes <= 0xfffccccccccccccL shr 40 -> "%.1f KiB".format(bytes.toDouble() / (0x1 shl 10))
    bytes <= 0xfffccccccccccccL shr 30 -> "%.1f MiB".format(bytes.toDouble() / (0x1 shl 20))
    bytes <= 0xfffccccccccccccL shr 20 -> "%.1f GiB".format(bytes.toDouble() / (0x1 shl 30))
    bytes <= 0xfffccccccccccccL shr 10 -> "%.1f TiB".format(bytes.toDouble() / (0x1 shl 40))
    bytes <= 0xfffccccccccccccL -> "%.1f PiB".format((bytes shr 10).toDouble() / (0x1 shl 40))
    else -> "%.1f EiB".format((bytes shr 20).toDouble() / (0x1 shl 40))
}

Can be improved by using ULong to drop the first condition but the type, currently (2019), is marked as experimental by the language. Prepend Locale.ENGLISH to .format( to ensure digits won't be converted in locales with different digits.

Let me know what can be improved to make it a more idiomatic and readable Kotlin code.

5
votes

As my usecase was for Android use turned out I could use this https://stackoverflow.com/a/26502430

android.text.format.Formatter.formatShortFileSize(activityContext, bytes)

and

android.text.format.Formatter.formatFileSize(activityContext, bytes)
1
votes

Another concise solution with locale-sensitive formatting and correct binary prefixes:

import java.util.*

object Bytes {
  fun format(value: Long, locale: Locale = Locale.getDefault()): String = when {
    value < 1024 -> "$value B"
    else -> {
      val z = (63 - java.lang.Long.numberOfLeadingZeros(value)) / 10
      String.format(locale, "%.1f %siB", value.toDouble() / (1L shl z * 10), " KMGTPE"[z])
    }
  }
}

Test:

val locale = Locale.getDefault()
println(Bytes.format(1L, locale))
println(Bytes.format(2L * 1024, locale))
println(Bytes.format(3L * 1024 * 1024, locale))
println(Bytes.format(4L * 1024 * 1024 * 1024, locale))
println(Bytes.format(5L * 1024 * 1024 * 1024 * 1024, locale))
println(Bytes.format(6L * 1024 * 1024 * 1024 * 1024 * 1024, locale))
println(Bytes.format(Long.MAX_VALUE, locale))

Output:

1 B
2.0 KiB
3.0 MiB
4.0 GiB
5.0 GiB
6.0 PiB
8.0 EiB