Does .NET provide an easy way convert bytes to KB, MB, GB, etc.?
Asked Answered
Z

30

168

Just wondering if .NET provides a clean way to do this:

int64 x = 1000000;
string y = null;
if (x / 1024 == 0) {
    y = x + " bytes";
}
else if (x / (1024 * 1024) == 0) {
    y = string.Format("{0:n1} KB", x / 1024f);
}

etc...

Zounds answered 23/1, 2013 at 20:29 Comment(2)
You may consider the UnitsNet nuget package github.com/angularsen/UnitsNet/blob/master/UnitsNet/…Pyrophyllite
@Pyrophyllite in case someone decides to give the UnitsNet package a try, there's the Information class that implements the FromBytes method, which allows you to convert from bytes to another unit, e.g. double result = Information.FromBytes(1547821).Megabytes; => this will return 1.547 (MB).Enkindle
G
249

Here is a fairly concise way to do this:

static readonly string[] SizeSuffixes = 
                   { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(Int64 value, int decimalPlaces = 1)
{
    if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
    if (value < 0) { return "-" + SizeSuffix(-value, decimalPlaces); } 
    if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }

    // mag is 0 for bytes, 1 for KB, 2, for MB, etc.
    int mag = (int)Math.Log(value, 1024);

    // 1L << (mag * 10) == 2 ^ (10 * mag) 
    // [i.e. the number of bytes in the unit corresponding to mag]
    decimal adjustedSize = (decimal)value / (1L << (mag * 10));

    // make adjustment when the value is large enough that
    // it would round up to 1000 or more
    if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
    {
        mag += 1;
        adjustedSize /= 1024;
    }

    return string.Format("{0:n" + decimalPlaces + "} {1}", 
        adjustedSize, 
        SizeSuffixes[mag]);
}

And here's the original implementation I suggested, which may be marginally slower, but a bit easier to follow:

static readonly string[] SizeSuffixes = 
                  { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };

static string SizeSuffix(Int64 value, int decimalPlaces = 1)
{
    if (value < 0) { return "-" + SizeSuffix(-value, decimalPlaces); } 

    int i = 0;
    decimal dValue = (decimal)value;
    while (Math.Round(dValue, decimalPlaces) >= 1000)
    {
        dValue /= 1024;
        i++;
    }

    return string.Format("{0:n" + decimalPlaces + "} {1}", dValue, SizeSuffixes[i]);
}

Console.WriteLine(SizeSuffix(100005000L));

One thing to bear in mind - in SI notation, "kilo" usually uses a lowercase k while all of the larger units use a capital letter. Windows uses KB, MB, GB, so I have used KB above, but you may consider kB instead.

Grotesquery answered 23/1, 2013 at 20:38 Comment(9)
The asker is only looking for 1 decimal place of accuracy. Could you give an example of an input that produces an incorrect output?Grotesquery
Both examples now use floating point division so there should be much less concern about rounding errors.Grotesquery
Thank you, just what I was looking for. (2nd implementation.)Boggers
Very neat implementation. Note that if you pass the value 0 to this function it will throw an IndexOutOfRangeException. I decided to add a if (value == 0) { return "0"; } check inside the function.Izawa
Can you provide the case when file size is < 0 ? For me it looks weird...Caton
@RuslanF. The difference in size between two files, or any size differential can be a negative number, so there's one use case. It's just one extra line to handle the < 0 case gracefully and I think that's better than having the method blow up with an IndexOutOfRangeException (in the first example) or do nothing useful at all (in the second example).Grotesquery
Unlike the other values the k in kB is usually lowercase. :-) en.wikipedia.org/wiki/Kilobyte.Greet
@Greet Thanks, good point. Windows uses KB, so I'm going to leave it that way, but I have added a note.Grotesquery
@RuslanF - going along with @JLRishe's reply: Microsoft.Extensions.FileProviders.IFileInfo.Length can return "-1 for a directory or non-existing files", too.Tolerance
W
145

Checkout the ByteSize library. It's the System.TimeSpan for bytes!

It handles the conversion and formatting for you.

var maxFileSize = ByteSize.FromKiloBytes(10);
maxFileSize.Bytes;
maxFileSize.MegaBytes;
maxFileSize.GigaBytes;

It also does string representation and parsing.

// ToString
ByteSize.FromKiloBytes(1024).ToString(); // 1 MB
ByteSize.FromGigabytes(.5).ToString();   // 512 MB
ByteSize.FromGigabytes(1024).ToString(); // 1 TB

// Parsing
ByteSize.Parse("5b");
ByteSize.Parse("1.55B");
Weltschmerz answered 12/3, 2014 at 23:58 Comment(8)
Simple to use and understand, and it works with .Net 4.0 and up.Orta
This should be included as part of the .NET frameworkContent
The only problem I see is that the conversion methods only work from non-byte to byte, but no the other way round.Depress
@Depress what do you mean non-byte? Like bits? There’s a .FromBits method you can use.Weltschmerz
Sorry, I have not provided much info. I'm now testing your library. It's really cool! However I don't see how to convert a value (in bytes) to a ByteSize. Which method should I use? Thanks!Depress
OK, I have just located it. It's the constructor of ByteSize! Wonderful :)Depress
If your source data is something other than "bytes" and you need to be able to convert to anything...this is is the library you should be using.Cadence
Another problem I see with this lib as a French, is that the parsing and printing does not have translations for the units with the CultureInfos. In my language, KB is written Ko, MB -> Mo and so on. Otherwise it's really useful.Jeanejeanelle
A
72

I would solve it using Extension methods, Math.Pow function and Enums:

public static class MyExtension
{
    public enum SizeUnits
    {
        Byte, KB, MB, GB, TB, PB, EB, ZB, YB
    }

    public static string ToSize(this Int64 value, SizeUnits unit)
    {
        return (value / (double)Math.Pow(1024, (Int64)unit)).ToString("0.00");
    }
}

and use it like:

string h = x.ToSize(MyExtension.SizeUnits.KB);
Agustinaah answered 29/3, 2014 at 16:32 Comment(4)
Elegant solution!Outlandish
I used your idea to create one that automatically determines the unit. +1Mcintosh
That's a very elegant solution, which is much cleaner and consise that the approved solution. However, strictly speaking based on the enum values it should be based on power of 1000, i.e. not 1024 (en.wikipedia.org/wiki/Terabyte) code... public static string ToSize(this long value, Unit unit) => $"{value / Math.Pow(1000, (long) unit):F2}{unit.ToString()}";Electroplate
public enum SizeUnits { Byte, KB, MB, GB, TB, PB, EB, ZB, YB } public static string ConvertBytesTo(this long valueInBytes, SizeUnits unit, short decimalPlaces = 3) { return $"{(valueInBytes / Math.Pow(1024, (double)unit)).ToString($"n{decimalPlaces}")} {unit}"; }Bearded
B
48

Since everyone else is posting their methods, I figured I'd post the extension method I usually use for this:

EDIT: added int/long variants...and fixed a copypasta typo...

public static class Ext
{
    private const long OneKb = 1024;
    private const long OneMb = OneKb * 1024;
    private const long OneGb = OneMb * 1024;
    private const long OneTb = OneGb * 1024;

    public static string ToPrettySize(this int value, int decimalPlaces = 0)
    {
        return ((long)value).ToPrettySize(decimalPlaces);
    }

    public static string ToPrettySize(this long value, int decimalPlaces = 0)
    {
        var asTb = Math.Round((double)value / OneTb, decimalPlaces);
        var asGb = Math.Round((double)value / OneGb, decimalPlaces);
        var asMb = Math.Round((double)value / OneMb, decimalPlaces);
        var asKb = Math.Round((double)value / OneKb, decimalPlaces);
        string chosenValue = asTb > 1 ? string.Format("{0}Tb",asTb)
            : asGb > 1 ? string.Format("{0}Gb",asGb)
            : asMb > 1 ? string.Format("{0}Mb",asMb)
            : asKb > 1 ? string.Format("{0}Kb",asKb)
            : string.Format("{0}B", Math.Round((double)value, decimalPlaces));
        return chosenValue;
    }
}
Bullace answered 23/1, 2013 at 20:44 Comment(2)
Bear in mind though that lowercase b can typically signify bits rather than bytes. :-) en.wikipedia.org/wiki/Data-rate_units#Kilobit_per_secondGreet
Rather than > 1 I'd probably do >= 1Goldina
T
41

I know this is old thread already. but maybe someone will look for solution. And here's what I use and the easiest way

public static string FormatFileSize(long bytes)
{
    var unit = 1024;
    if (bytes < unit) { return $"{bytes} B"; }

    var exp = (int)(Math.Log(bytes) / Math.Log(unit));
    return $"{bytes / Math.Pow(unit, exp):F2} {("KMGTPE")[exp - 1]}B";
}

Get folder size (for example usage)

public static long GetFolderSize(string path, string ext, bool AllDir)
{
    var option = AllDir ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
    return new DirectoryInfo(path).EnumerateFiles("*" + ext, option).Sum(file => file.Length);
}

EXAMPLE USAGE:

public static void TEST()
{
    string folder = @"C:\Users\User\Videos";

    var bytes = GetFolderSize(folder, "mp4", true); //or GetFolderSize(folder, "mp4", false) to get all single folder only
    var totalFileSize = FormatFileSize(bytes);
    Console.WriteLine(totalFileSize);
}
Tenuous answered 2/7, 2020 at 14:4 Comment(4)
This is the most elegant solution here.Hydrophobic
@SergeyKovalev thanks :) I've been using this all the the time. Less cpu usage and easiy to useTenuous
I wish I could vote for this more than once, it's sooooooo neat and elegant. Thank you very muchOlsson
this deserves more upvotes, so elegantGawk
P
10

Updated for C# 9.0 Relational Patterns

public const long OneKB = 1024;

public const long OneMB = OneKB * OneKB;

public const long OneGB = OneMB * OneKB;

public const long OneTB = OneGB * OneKB;

public static string BytesToHumanReadable(ulong bytes)
{
    return bytes switch
    {
        (< OneKB) => $"{bytes}B",
        (>= OneKB) and (< OneMB) => $"{bytes / OneKB}KB",
        (>= OneMB) and (< OneGB) => $"{bytes / OneMB}MB",
        (>= OneGB) and (< OneTB) => $"{bytes / OneMB}GB",
        (>= OneTB) => $"{bytes / OneTB}"
        //...
    };
}
Pulvinate answered 12/6, 2021 at 4:38 Comment(0)
P
8

The short version of the most voted answer has problems with TB values.

I adjusted it appropriately to handle also tb values and still without a loop and also added a little error checking for negative values. Here's my solution:

static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(long value, int decimalPlaces = 0)
{
    if (value < 0)
    {
        throw new ArgumentException("Bytes should not be negative", "value");
    }
    var mag = (int)Math.Max(0, Math.Log(value, 1024));
    var adjustedSize = Math.Round(value / Math.Pow(1024, mag), decimalPlaces);
    return String.Format("{0} {1}", adjustedSize, SizeSuffixes[mag]);
}
Pisa answered 28/12, 2013 at 13:39 Comment(1)
The stated issue with large values should no longer be present in the accepted answer.Grotesquery
T
7

@Servy's answer was nice and succinct. I think it can be even simpler?

private static string[] suffixes = new [] { " B", " KB", " MB", " GB", " TB", " PB" };

public static string ToSize(double number, int precision = 2)
{
    // unit's number of bytes
    const double unit = 1024;
    // suffix counter
    int i = 0;
    // as long as we're bigger than a unit, keep going
    while(number > unit)
    {
        number /= unit;
        i++;
    }
    // apply precision and current suffix
    return Math.Round(number, precision) + suffixes[i];
}
Tatianna answered 26/1, 2018 at 18:44 Comment(0)
O
5

No. Mostly because it's of a rather niche need, and there are too many possible variations. (Is it "KB", "Kb" or "Ko"? Is a megabyte 1024 * 1024 bytes, or 1024 * 1000 bytes? -- yes, some places use that!)

Outstare answered 23/1, 2013 at 20:33 Comment(1)
+1 - according to Wikipedia, kb => 1000 bytes, and KiB => 1024 bytes.Cymbiform
B
5

Here is an option that's easier to extend than yours, but no, there is none built into the library itself.

private static List<string> suffixes = new List<string> { " B", " KB", " MB", " GB", " TB", " PB" };
public static string Foo(int number)
{
    for (int i = 0; i < suffixes.Count; i++)
    {
        int temp = number / (int)Math.Pow(1024, i + 1);
        if (temp == 0)
            return (number / (int)Math.Pow(1024, i)) + suffixes[i];
    }
    return number.ToString();
}
Balcer answered 23/1, 2013 at 20:35 Comment(0)
M
4
    private string GetFileSize(double byteCount)
    {
        string size = "0 Bytes";
        if (byteCount >= 1073741824.0)
            size = String.Format("{0:##.##}", byteCount / 1073741824.0) + " GB";
        else if (byteCount >= 1048576.0)
            size = String.Format("{0:##.##}", byteCount / 1048576.0) + " MB";
        else if (byteCount >= 1024.0)
            size = String.Format("{0:##.##}", byteCount / 1024.0) + " KB";
        else if (byteCount > 0 && byteCount < 1024.0)
            size = byteCount.ToString() + " Bytes";

        return size;
    }

    private void btnBrowse_Click(object sender, EventArgs e)
    {
        if (openFile1.ShowDialog() == DialogResult.OK)
        {
            FileInfo thisFile = new FileInfo(openFile1.FileName);

            string info = "";

            info += "File: " + Path.GetFileName(openFile1.FileName);
            info += Environment.NewLine;
            info += "File Size: " + GetFileSize((int)thisFile.Length);

            label1.Text = info;
        }
    }

This is one way to do it aswell (The number 1073741824.0 is from 1024*1024*1024 aka GB)

Meagan answered 6/8, 2014 at 6:59 Comment(0)
M
3

Based on NeverHopeless's elegant solution:

private static readonly KeyValuePair<long, string>[] Thresholds = 
{
    // new KeyValuePair<long, string>(0, " Bytes"), // Don't devide by Zero!
    new KeyValuePair<long, string>(1, " Byte"),
    new KeyValuePair<long, string>(2, " Bytes"),
    new KeyValuePair<long, string>(1024, " KB"),
    new KeyValuePair<long, string>(1048576, " MB"), // Note: 1024 ^ 2 = 1026 (xor operator)
    new KeyValuePair<long, string>(1073741824, " GB"),
    new KeyValuePair<long, string>(1099511627776, " TB"),
    new KeyValuePair<long, string>(1125899906842620, " PB"),
    new KeyValuePair<long, string>(1152921504606850000, " EB"),

    // These don't fit into a int64
    // new KeyValuePair<long, string>(1180591620717410000000, " ZB"), 
    // new KeyValuePair<long, string>(1208925819614630000000000, " YB") 
};

/// <summary>
/// Returns x Bytes, kB, Mb, etc... 
/// </summary>
public static string ToByteSize(this long value)
{
    if (value == 0) return "0 Bytes"; // zero is plural
    for (int t = Thresholds.Length - 1; t > 0; t--)
        if (value >= Thresholds[t].Key) return ((double)value / Thresholds[t].Key).ToString("0.00") + Thresholds[t].Value;
    return "-" + ToByteSize(-value); // negative bytes (common case optimised to the end of this routine)
}

Maybe there are excessive comments, but I tend to leave them to prevent myself from making the same mistakes over on future visits...

Mcintosh answered 9/2, 2018 at 14:6 Comment(0)
P
2

No.

But you can implement like this;

    static double ConvertBytesToMegabytes(long bytes)
    {
    return (bytes / 1024f) / 1024f;
    }

    static double ConvertKilobytesToMegabytes(long kilobytes)
    {
    return kilobytes / 1024f;
    }

Also check out How to correctly convert filesize in bytes into mega or gigabytes?

Pail answered 23/1, 2013 at 20:34 Comment(0)
A
1

I have combined some of the answers here into two methods that work great. The second method below will convert from a bytes string (like 1.5.1 GB) back to bytes (like 1621350140) as a long type value. I hope this is useful to others looking for a solution to convert bytes to a string and back into bytes.

public static string BytesAsString(float bytes)
{
    string[] suffix = { "B", "KB", "MB", "GB", "TB" };
    int i;
    double doubleBytes = 0;

    for (i = 0; (int)(bytes / 1024) > 0; i++, bytes /= 1024)
    {
        doubleBytes = bytes / 1024.0;
    }

    return string.Format("{0:0.00} {1}", doubleBytes, suffix[i]);
}

public static long StringAsBytes(string bytesString)
{
    if (string.IsNullOrEmpty(bytesString))
    {
        return 0;
    }

    const long OneKb = 1024;
    const long OneMb = OneKb * 1024;
    const long OneGb = OneMb * 1024;
    const long OneTb = OneGb * 1024;
    double returnValue;
    string suffix = string.Empty;

    if (bytesString.IndexOf(" ") > 0)
    {
        returnValue = float.Parse(bytesString.Substring(0, bytesString.IndexOf(" ")));
        suffix = bytesString.Substring(bytesString.IndexOf(" ") + 1).ToUpperInvariant();
    }
    else
    {
        returnValue = float.Parse(bytesString.Substring(0, bytesString.Length - 2));
        suffix = bytesString.ToUpperInvariant().Substring(bytesString.Length - 2);
    }

    switch (suffix)
    {
        case "KB":
            {
                returnValue *= OneKb;
                break;
            }

        case "MB":
            {
                returnValue *= OneMb;
                break;
            }

        case "GB":
            {
                returnValue *= OneGb;
                break;
            }

        case "TB":
            {
                returnValue *= OneTb;
                break;
            }

        default:
            {
                break;
            }
    }

    return Convert.ToInt64(returnValue);
}
Amenable answered 30/10, 2013 at 5:24 Comment(1)
May I ask why you use float.Parse to double ?M16
E
1

I went for JerKimballs solution, and thumbs up to that. However, I would like to add / point out that this is indeed a matter of controversy as a whole. In my research (for other reasons) I have come up with the following pieces of information.

When normal people (I have heard they exist) speak of gigabytes they refer to the metric system wherein 1000 to the power of 3 from the original number of bytes == the number of gigabytes. However, of course there is the IEC / JEDEC standards which is nicely summed up in wikipedia, which instead of 1000 to the power of x they have 1024. Which for physical storage devices (and I guess logical such as amazon and others) means an ever increasing difference between metric vs IEC. So for instance 1 TB == 1 terabyte metric is 1000 to the power of 4, but IEC officially terms the similar number as 1 TiB, tebibyte as 1024 to the power of 4. But, alas, in non-technical applications (I would go by audience) the norm is metric, and in my own app for internal use currently I explain the difference in documentation. But for display purposes I do not even offer anything but metric. Internally even though it's not relevant in my app I only store bytes and do the calculation for display.

As a side note I find it somewhat lackluster that the .Net framework AFAIK (and I am frequently wrong thank the powers that be) even in it's 4.5 incarnation does not contain anything about this in any libraries internally. One would expect an open source library of some kind to be NuGettable at some point, but I admit this is a small peeve. On the other hand System.IO.DriveInfo and others also only have bytes (as long) which is rather clear.

Extroversion answered 13/11, 2015 at 10:26 Comment(1)
Kudos on mentioning this! I created a library for Java to do byte size conversions and hit the IEC vs SI wall there. In the end I went all in and support both. Now that I'm working in the .NET world I might port that over to C#. But until then and for educational purposes, here's a link to my Java lib: github.com/StFS/YummyBytesKrone
S
1

How about some recursion:

private static string ReturnSize(double size, string sizeLabel)
{
  if (size > 1024)
  {
    if (sizeLabel.Length == 0)
      return ReturnSize(size / 1024, "KB");
    else if (sizeLabel == "KB")
      return ReturnSize(size / 1024, "MB");
    else if (sizeLabel == "MB")
      return ReturnSize(size / 1024, "GB");
    else if (sizeLabel == "GB")
      return ReturnSize(size / 1024, "TB");
    else
      return ReturnSize(size / 1024, "PB");
  }
  else
  {
    if (sizeLabel.Length > 0)
      return string.Concat(size.ToString("0.00"), sizeLabel);
    else
      return string.Concat(size.ToString("0.00"), "Bytes");
  }
}

Then you can call it:

ReturnSize(size, string.Empty);
Sweepstake answered 18/10, 2017 at 8:7 Comment(0)
L
1

I recently needed this and required to convert the in bytes to a number in long.

Usage: Byte.Kb.ToLong(1) should give 1024.

public enum Byte
{
    Kb,
    Mb,
    Gb,
    Tb
}

public static class ByteSize
{
    private const long OneKb = 1024;
    private const long OneMb = OneKb * 1024;
    private const long OneGb = OneMb * 1024;
    private const long OneTb = OneGb * 1024;

    public static long ToLong(this Byte size, int value)
    {
        return size switch
        {
            Byte.Kb => value * OneKb,
            Byte.Mb => value * OneMb,
            Byte.Gb => value * OneGb,
            Byte.Tb => value * OneTb,
            _ => throw new NotImplementedException("This should never be hit.")
        };
    }
}

Tests using xunit:

[Theory]
[InlineData(Byte.Kb, 1, 1024)]
[InlineData(Byte.Kb, 2, 2048)]
[InlineData(Byte.Mb, 1, 1048576)]
[InlineData(Byte.Mb, 2, 2097152)]
[InlineData(Byte.Gb, 1, 1073741824)]
[InlineData(Byte.Gb, 2, 2147483648)]
[InlineData(Byte.Tb, 1, 1099511627776)]
[InlineData(Byte.Tb, 2, 2199023255552)]
public void ToLong_WhenConverting_ShouldMatchExpected(Byte size, int value, long expected)
{
    var result = size.ToLong(value);

    result.Should().Be(expected);
}
Littlest answered 29/8, 2020 at 16:1 Comment(0)
F
1

Since it seems to be all the rage for people to add their own algorithms as answers to this question, here's mine. (Although, admittedly, my primary motivation is to post it somewhere I can find it again when I need it!)

It's a cross-platform C# extension method that implements the Windows StrFormatByteSize function, which provides results with three or four significant figures, and up to two decimal places. It differs from StrFormatByteSize in a few minor ways (see below). Compatible with .NET 7 and later, although it wouldn't be difficult to backport it to previous versions.

If includeTotalBytes is true, a total of the number of bytes is added to the string, akin to what's shown in the Windows File Explorer's Properties dialogue.

Code

using System;
using System.Numerics;

internal static class BinaryIntegerExtensions
{
    private static readonly string[] sizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB" };

    /// <summary>Converts the numeric value of this instance to a string that represents the number expressed as a size-dependent value in bytes, kilobytes, megabytes, etc, up to quettabytes.</summary>
    /// <remarks>The string expression units are based on powers of 2, represented by the colloquially-understood KB, MB, GB, etc, instead of the technically correct KiB, MiB, GiB, etc.</remarks>
    /// <param name="includeTotalBytes"><c>true</c> to append the total number of bytes to the string; otherwise, <c>false</c>.</param>
    /// <returns>The string representation of the value, expressed in bytes, kilobytes, megabytes, etc, up to quettabytes.</returns>
    /// <exception cref="OverflowException">The numeric value of this instance is out of range and cannot be converted.</exception>
    internal static string ToByteSizeString<T>(this T value, bool includeTotalBytes) where T : IBinaryInteger<T>
    {
        string result;

        if (T.IsZero(value))
            result = $"0 {sizeSuffixes[0]}";
        else
        {
            int magnitude, decimalPlaces;
            double absolute, fullResult, roundedResult;
            string bytesPart = string.Empty;

            absolute = double.CreateChecked(T.Abs(value));
            magnitude = Math.Min((int)Math.Floor(Math.Log(absolute, 1024)), sizeSuffixes.Length - 1);
            fullResult = T.Sign(value) * (absolute / Math.Pow(1024, magnitude));

            decimalPlaces = Math.Max(0, 2 - (int)Math.Floor(Math.Log10(fullResult)));
            roundedResult = Math.Round(fullResult, decimalPlaces, MidpointRounding.AwayFromZero);

            if (includeTotalBytes && (magnitude > 0))
                bytesPart = $" ({value:N0} {sizeSuffixes[0]})";

            result = $"{roundedResult:#,#.##} {sizeSuffixes[magnitude]}{bytesPart}";
        }

        return (result);
    }
}

Examples of use

int test1 = 46432131;
string result1 = test1.ToByteSizeString(false);
// result1 == "44.3 MB"
ulong test2 = 1748413218964;
string result2 = test2.ToByteSizeString(false);
// result2 == "1.59 TB"
long test3 = 56431242;
string result3 = test3.ToByteSizeString(true);
// result3 == "53.8 MB (56,431,242 bytes)"
short test4 = 512;
string result4 = test4.ToByteSizeString(true);
// result4 == "512 bytes"
UInt128 test5 = UInt128.MaxValue;
string result5 = test5.ToByteSizeString(true);
// result5 = "268,435,456 QB (340,282,366,920,938,463,463,374,607,431,768,211,455 bytes)"

Differences from StrFormatByteSize

  1. Numbers in the result string have grouping where appropriate.
  2. The result is rounded up or down as appropriate, whereas StrFormatByteSize truncates after however many decimal places it displays.
  3. Negative values work as expected, whereas StrFormatByteSize provides only a total number of bytes for negative values.
Fructose answered 6/8, 2023 at 10:18 Comment(0)
M
0

How about:

public void printMB(uint sizekB)   
{
    double sizeMB = (double) sizekB / 1024;
    Console.WriteLine("Size is " + sizeMB.ToString("0.00") + "MB");
}

E.g. call like

printMB(123456);

Will result in output

"Size is 120,56 MB"
Muse answered 29/3, 2014 at 15:56 Comment(0)
A
0

https://github.com/logary/logary/blob/master/src/Logary/DataModel.fs#L832-L837

let scaleBytes (value : float) : float * string =
    let log2 x = log x / log 2.
    let prefixes = [| ""; "Ki"; "Mi"; "Gi"; "Ti"; "Pi" |] // note the capital K and the 'i'
    let index = int (log2 value) / 10
    1. / 2.**(float index * 10.),
sprintf "%s%s" prefixes.[index] (Units.symbol Bytes)

(DISCLAIMER: I wrote this code, even the code in the link!)

Arvy answered 12/12, 2016 at 17:50 Comment(0)
L
0
public static class MyExtension
{
    public static string ToPrettySize(this float Size)
    {
        return ConvertToPrettySize(Size, 0);
    }
    public static string ToPrettySize(this int Size)
    {
        return ConvertToPrettySize(Size, 0);
    }
    private static string ConvertToPrettySize(float Size, int R)
    {
        float F = Size / 1024f;
        if (F < 1)
        {
            switch (R)
            {
                case 0:
                    return string.Format("{0:0.00} byte", Size);
                case 1:
                    return string.Format("{0:0.00} kb", Size);
                case 2:
                    return string.Format("{0:0.00} mb", Size);
                case 3:
                    return string.Format("{0:0.00} gb", Size);
            }
        }
        return ConvertToPrettySize(F, ++R);
    }
}
Lichtenfeld answered 12/10, 2017 at 3:5 Comment(0)
Y
0

As posted above, the recursion is the favorite way, with the help of logarithm.

The following function has 3 arguments : the input, the dimension constraint of the output, that is the third argument.

int ByteReDim(unsigned long ival, int constraint, unsigned long *oval)
{
    int base = 1 + (int) log10(ival);

    (*oval) = ival;
    if (base > constraint) {
        (*oval) = (*oval) >> 10;
        return(1 + ByteReDim((*oval), constraint, oval));
    } else
        return(0);
}

Now let's convert 12GB of RAM in several units:

int main(void)
{
    unsigned long RAM;
    int unit; // index of below symbols array
    char symbol[5] = {'B', 'K', 'M', 'G', 'T'};

    unit = ByteReDim(12884901888, 12, &RAM);
    printf("%lu%c\n", RAM, symbol[unit]); // output is 12884901888B

    unit = ByteReDim(12884901888, 9, &RAM);
    printf("%lu%c\n", RAM, symbol[unit]); // output is 12582912K

    unit = ByteReDim(12884901888, 6, &RAM);
    printf("%lu%c\n", RAM, symbol[unit]); // output is 12288M

    unit = ByteReDim(12884901888, 3, &RAM);
    printf("%lu%c\n", RAM, symbol[unit]); // output is 12G
}
Youlandayoulton answered 15/12, 2017 at 15:19 Comment(0)
A
0

I use this for Windows (binary prefixes):

static readonly string[] BinaryPrefix = { "bytes", "KB", "MB", "GB", "TB" }; // , "PB", "EB", "ZB", "YB"
string GetMemoryString(double bytes)
{
    int counter = 0;
    double value = bytes;
    string text = "";
    do
    {
        text = value.ToString("0.0") + " " + BinaryPrefix[counter];
        value /= 1024;
        counter++;
    }
    while (Math.Floor(value) > 0 && counter < BinaryPrefix.Length);
    return text;
}
Agace answered 12/6, 2018 at 8:52 Comment(0)
K
0

I have incorporated this (with little to no modification) into a UWP DataBinding Converter for my project and thought it might also be useful to others.

The code is:

using System;
using System.Text;
using Windows.UI.Xaml.Data;

namespace MyApp.Converters
{
    public class ByteSizeConverter : IValueConverter
    {
        static readonly string[] sSizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };

        // The number of decimal places the formatter should include in the scaled output - default 1dp
        public int DecimalPlaces { get; set; } = 1;

        public object Convert(object value, Type targetType, object parameter, string language)
        {
            Int64 intVal = System.Convert.ToInt64(value);

            return SizeSuffix(intVal);
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            // TODO: Parse string into number and suffix
            //       Scale number by suffix multiplier to get bytes
            throw new NotImplementedException();
        }

        string SizeSuffix(Int64 value)
        {
            if (this.DecimalPlaces < 0) { throw new ArgumentOutOfRangeException(String.Format("DecimalPlaces = {0}", this.DecimalPlaces)); }
            if (value < 0) { return "-" + SizeSuffix(-value); }
            if (value == 0) { return string.Format("{0:n" + this.DecimalPlaces + "} bytes", 0); }

            // magnitude is 0 for bytes, 1 for KB, 2, for MB, etc.
            int magnitude = (int)Math.Log(value, 1024);
            // clip magnitude - only 8 values currently supported, this prevents out-of-bounds exception
            magnitude = Math.Min(magnitude, 8);

            // 1L << (magnitude * 10) == 2 ^ (10 * magnitude) [i.e. the number of bytes in the unit corresponding to magnitude]
            decimal adjustedSize = (decimal)value / (1L << (magnitude * 10));

            // make adjustment when the value is large enough that it would round up to 1000 or more
            if (Math.Round(adjustedSize, this.DecimalPlaces) >= 1000)
            {
                magnitude += 1;
                adjustedSize /= 1024;
            }

            return String.Format("{0:n" + this.DecimalPlaces + "} {1}", adjustedSize, sSizeSuffixes[magnitude]);
        }
    }
}

To use it, add a local resource to your UserControl or Page XAML:

<UserControl.Resources>
    <converters:ByteSizeConverter x:Key="ByteFormat" DecimalPlaces="3" />
</UserControl.Resources>

Reference it in a data binding template or data binding instance:

<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center"
    Text="{x:Bind MyItem.FileSize_bytes, Mode=OneWay, Converter={StaticResource ByteFormat}}" />

And hey presto. The magic happens.

Kandi answered 18/11, 2019 at 16:59 Comment(0)
V
0

Here's my spin on @drzaus's answer. I modified it to use rounding errors to our advantage and correctly manage issues around unit boundaries. It also handles negative values.

Drop this C# Program into LinqPad:

// Kudos: https://mcmap.net/q/81994/-does-net-provide-an-easy-way-convert-bytes-to-kb-mb-gb-etc

void Main()
{
    0.ToFriendly().Dump();                      // 0 B
    857.ToFriendly().Dump();                    // 857 B
    (173*1024).ToFriendly().Dump();             // 173 KB
    (9541*1024).ToFriendly().Dump();            // 9.32 MB
    (5261890L*1024).ToFriendly().Dump();        // 5.02 GB

    1.ToFriendly().Dump();                      // 1 B
    1024.ToFriendly().Dump();                   // 1 KB
    1048576.ToFriendly().Dump();                // 1 MB
    1073741824.ToFriendly().Dump();             // 1 GB
    1099511627776.ToFriendly().Dump();          // 1 TB
    1125899906842620.ToFriendly().Dump();       // 1 PB
    1152921504606850000.ToFriendly().Dump();    // 1 EB
}

public static class Extensions
{
    static string[] _byteUnits = new[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" };

    public static string ToFriendly(this int number, int decimals = 2)
    {
        return ((double)number).ToFriendly(decimals);
    }

    public static string ToFriendly(this long number, int decimals = 2)
    {
        return ((double)number).ToFriendly(decimals);
    }

    public static string ToFriendly(this double number, int decimals = 2)
    {
        const double divisor = 1024;

        int unitIndex = 0;
        var sign = number < 0 ? "-" : string.Empty;
        var value = Math.Abs(number);
        double lastValue = number;

        while (value > 1)
        {
            lastValue = value;

            // NOTE
            // The following introduces ever increasing rounding errors, but at these scales we don't care.
            // It also means we don't have to deal with problematic rounding errors due to dividing doubles.
            value = Math.Round(value / divisor, decimals);

            unitIndex++;
        }

        if (value < 1 && number != 0)
        {
            value = lastValue;
            unitIndex--;
        }

        return $"{sign}{value} {_byteUnits[unitIndex]}";
    }
}

Output is:

0 B
857 B
173 KB
9.32 MB
1.34 MB
5.02 GB
1 B
1 KB
1 MB
1 GB
1 TB
1 PB
1 EB
Vala answered 25/8, 2020 at 23:37 Comment(0)
P
0
string Convert(float bytes)
{
    string[] Group = { "Bytes", "KB", "MB", "GB", "TB"};
    float B = bytes; int G = 0;
    while (B >= 1024 && G < 5)
    {
        B /= 1024;
        G += 1;
    }
    float truncated = (float)(Math.Truncate((double)B * 100.0) / 100.0);
    string load = (truncated + " " + Group[G]);

    return load;
}
Poppyhead answered 22/3, 2021 at 9:12 Comment(0)
G
0

This is how I do it.

Console.Write(FileSizeInBytes > 1048576 ? FileSizeInBytes / 1048576f + " MB" : FileSizeInBytes / 1024f + " KB"); //1048576 = 1024 * 1024
Gemoets answered 8/10, 2021 at 6:21 Comment(0)
P
0

I combined zackmark15's code into an all-purpose file or directory measuring approach:

public static string PathSize(string path)
{
    if (String.IsNullOrEmpty(path))
        throw new ArgumentNullException(nameof(path));

    long bytes;

    if (File.Exists(path))
        bytes = new FileInfo(path).Length;

    else if (Directory.Exists(path))
        bytes = new DirectoryInfo(path).EnumerateFiles("*", SearchOption.AllDirectories).Sum(fileInfo => fileInfo.Length);

    else
        throw new ArgumentException("Path does not exist.", nameof(path));

    const long UNIT = 1024L;

    if (bytes < UNIT)
        return $"{bytes} bytes";

    var exp = (int)(Math.Log(bytes) / Math.Log(UNIT));

    return $"{bytes / Math.Pow(UNIT, exp):F2} {("KMGTPE")[exp - 1]}B";
}
Phytosociology answered 29/3, 2022 at 0:53 Comment(0)
S
0

Okay... Let's use what windows provides ...

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern long StrFormatByteSizeW(long qdw,
    [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszBuf,
    int cchBuf);

public static string GetFileSize(this long number)
{
  var sb = new StringBuilder(32);
  StrFormatByteSizeW(number, sb, sb.Capacity);
  return sb.ToString();
}
Sasha answered 12/5, 2023 at 12:35 Comment(0)
I
0

Was looking for a cleaner solution but In my opinion I can't seem to find one, so here goes.

This is intended to take a file size long value using File.Length then convert it to a size type and round to two decimal places:

10 bytes becomes 10B

1030KB becomes 1.01MB

16384KB becomes 16MB

2,097,152KB becomes 2GB

13,064,424KB becomes 12.46GB

public static string FormatFileSize(long fileSizeBytes)
{
    const int KB = 1024;

    string[] sizeLabels = { "B", "KB", "MB", "GB", "TB" };
    double fileSize = fileSizeBytes;
    int labelIndex = 0;

    while (fileSize >= KB && labelIndex < sizeLabels.Length - 1)
    {
        fileSize /= KB;
        labelIndex++;
    }

    fileSize = Math.Round(fileSize, 2);

    return $"{fileSize} {sizeLabels[labelIndex]}";
}
Inside answered 21/5, 2023 at 3:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.