How do I convert the size of a file into bytes for KB, MB, GB, TB, etc?

Asked

Viewed 3,614 times

2

I’m picking up the file size to display on the screen:

new FileInfo(arquivo).Length

Only this property returns me the total of bytes... I would like to display in a format that I could understand, for example, instead of 2147483647 want to show 2 GB.

How can I do that?

2 answers

10

Performance!

For those who, for whatever reason, need this format to perform well just use constants and torque displacement operators (shift):

public static string TamanhoAmigavel(long bytes)
{
    if (bytes < 0) throw new ArgumentException("bytes");

    double humano;
    string sufixo;

    if (bytes >= 1152921504606846976L) // Exabyte (1024^6)
    {
        humano = bytes >> 50;
        sufixo = "EB";
    }
    else if (bytes >= 1125899906842624L) // Petabyte (1024^5)
    {
        humano = bytes >> 40;
        sufixo = "PB";
    }
    else if (bytes >= 1099511627776L) // Terabyte (1024^4)
    {
        humano = bytes >> 30;
        sufixo = "TB";
    }
    else if (bytes >= 1073741824) // Gigabyte (1024^3)
    {
        humano = bytes >> 20;
        sufixo = "GB";
    }
    else if (bytes >= 1048576) // Megabyte (1024^2)
    {
        humano = bytes >> 10;
        sufixo = "MB";
    }
    else if (bytes >= 1024) // Kilobyte (1024^1)
    {
        humano = bytes;
        sufixo = "KB";
    }
    else return bytes.ToString("0 B"); // Byte

    humano /= 1024;
    return humano.ToString("0.## ") + sufixo;
}

Why the performance gain?

  • Using constants you avoid the runtime calculation of binary prefixes.
  • The operation of bit displacement is, theoretically*, less expensive for the processor than for mathematical operations common:

On simple low-cost Processors, typically, bitwise Operations are substantially Faster than Division, several times Faster than multiplication, and sometimes significantly Faster than addition. While Modern Processors usually perform addition and multiplication just as fast as bitwise Operations due to their longer Instruction pipelines and other architectural design Choices, bitwise Operations do commonly use Less power because of the Reduced use of Resources. (source)

Translating:

In low-cost processors, bit operations are usually substantially faster than split, often faster than multiplication, and sometimes faster than addition. Although modern processors usually do addition and multiplication as quickly as bit operations due to longer instruction queues and other architectural decisions, bit operations typically use less energy because they require a reduced amount of resources.

* Theoretically because in practice the compiler can optimize your code.


In the question of the reply quoted by @Brunolm there is a another interesting solution using the DLL Shlwapi windows:

[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize(long fileSize, [MarshalAs(UnmanagedType.LPTStr)]StringBuilder buffer, int bufferSize);

public static string StrFormatByteSize(long filesize)
{
    StringBuilder sb = new StringBuilder(11);
    StrFormatByteSize(filesize, sb, sb.Capacity);
    return sb.ToString();
}

In most cases it would be an exaggeration to import an external DLL for only this function. But probably (anyone confirms? ) the formatting done by this function is exactly the one used by Windows Explorer; in some scenario this consistency may be useful or even necessary.

  • 1

    Since the focus is performance, another optimization that can be done is the following: if (bytes >= 1024) is the same as if (bytes >> 10), after all and the number is greater or equal to 1024, then some bit after the tenth is 1 and the result of the shift will be nonnull. This is one less comparison and allows the compiler to reuse the subexpression bytes >> 10 at the two points where it appears. The same applies to 20, 30, etc.

  • 1

    @Guilhermebernal, there really would be a performance gain, but C# doesn’t play any other kind bool in a conditional expression. The operator >> will return a long and C# will force you to make the comparison on that return (Cannot implicitly Convert type 'long' to 'bool'). But it’s still a good tip for those who are taking advantage of the answer to another language, like C++ :-)

1


There are several ways to do this. One that caught my attention was the deepee1 method. I made a small adjustment and put this method in a class of Extension methods.

public static partial class NumericExtender
{
    private static IList<string> fileSizeUnits;

    static NumericExtender()
    {
        fileSizeUnits =
            new List<string>() { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
    }

    /// <summary>
    /// Retorna o valor formatado como tamanho de arquivo (KB, MB, GB...).
    /// </summary>
    /// <param name="totalBytes">Total de bytes do arquivo.</param>
    /// <param name="precision">Número de casas decimais para exibir.</param>
    /// <returns>Tamanho do arquivo formatado.</returns>
    public static string ToFileSize(this double totalBytes, int precision = 2)
    {
        if (totalBytes <= 0)
            return String.Format("0 {0}", fileSizeUnits[0]);

        double bytes = Math.Abs(totalBytes);
        int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
        if (place > fileSizeUnits.Count - 1)
            place = fileSizeUnits.Count - 1;

        double num = Math.Round(bytes / Math.Pow(1024, place), precision);
        return String.Format("{0} {1}", Math.Sign(totalBytes) * num, fileSizeUnits[place]);
    }
}

And I did a test method:

[TestMethod]
public void ToFileSize()
{
    Assert.AreEqual("0 B", Int32.MinValue.ToFileSize());
    Assert.AreEqual("0 B", UInt32.MinValue.ToFileSize());
    Assert.AreEqual("0 B", Int64.MinValue.ToFileSize());
    Assert.AreEqual("0 B", UInt64.MinValue.ToFileSize());
    Assert.AreEqual("0 B", Double.MinValue.ToFileSize());

    Assert.AreEqual("2 GB", Int32.MaxValue.ToFileSize());
    Assert.AreEqual("4 GB", UInt32.MaxValue.ToFileSize());
    Assert.AreEqual("8 EB", Int64.MaxValue.ToFileSize());
    Assert.AreEqual("16 EB", UInt64.MaxValue.ToFileSize());
    Assert.AreEqual("1.48701690847778E+284 YB", Double.MaxValue.ToFileSize());

    Assert.AreEqual("1.24 KB", 1270.ToFileSize());
    Assert.AreEqual("1.24023 KB", 1270.ToFileSize(5));
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.