Bietet .NET eine einfache Möglichkeit, Bytes in KB, MB, GB usw. zu konvertieren?


112

Ich frage mich nur, ob .NET eine saubere Möglichkeit bietet, dies zu tun:

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...

Antworten:


192

Hier ist ein ziemlich prägnanter Weg, dies zu tun:

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); } 
    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]);
}

Und hier ist die ursprüngliche Implementierung, die ich vorgeschlagen habe. Sie ist zwar etwas langsamer, aber etwas einfacher zu befolgen:

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); } 

    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));

Eine Sache zu beachten - in der SI-Notation verwendet "Kilo" normalerweise einen Kleinbuchstaben k, während alle größeren Einheiten einen Großbuchstaben verwenden. Windows verwendet KB, MB, GB, daher habe ich oben KB verwendet, aber Sie können stattdessen kB in Betracht ziehen.


Der Fragesteller sucht nur nach 1 Dezimalstelle Genauigkeit. Können Sie ein Beispiel für eine Eingabe geben, die eine falsche Ausgabe erzeugt?
JLRishe

2
Beide Beispiele verwenden jetzt die Gleitkommadivision, sodass Rundungsfehler weniger bedenklich sein sollten.
JLRishe

Danke, genau das, wonach ich gesucht habe. (2. Implementierung.)
Snapplex

1
Sehr ordentliche Umsetzung. Beachten Sie, dass beim Übergeben des Werts 0 an diese Funktion eine IndexOutOfRangeException ausgelöst wird. Ich habe beschlossen, einen if (value == 0) { return "0"; }Check innerhalb der Funktion hinzuzufügen .
Bounav

Können Sie den Fall angeben, wenn die Dateigröße <0 ist? Für mich sieht es komisch aus ...
Ruslan F.

84

Überprüfen Sie die ByteSize- Bibliothek. Es ist das System.TimeSpanfür Bytes!

Es übernimmt die Konvertierung und Formatierung für Sie.

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

Es werden auch Zeichenfolgen dargestellt und analysiert.

// 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");

6
Einfach zu bedienen und zu verstehen und funktioniert mit .Net 4.0 und höher.
Der Joker

33
Dies sollte als Teil des .NET Frameworks enthalten sein
helios456

Das einzige Problem, das ich sehe, ist, dass die Konvertierungsmethoden nur von Nicht-Byte zu Byte funktionieren, aber nicht umgekehrt.
SuperJMN

@ SuperJMN was meinst du ohne Byte? Wie Bits? Es gibt eine .FromBits-Methode, die Sie verwenden können.
Omar

1
Wenn Ihre Quelldaten etwas anderes als "Bytes" sind und Sie in etwas konvertieren können müssen ... ist dies die Bibliothek, die Sie verwenden sollten.
James Blake

37

Da alle anderen ihre Methoden veröffentlichen, dachte ich, ich würde die Erweiterungsmethode veröffentlichen, die ich normalerweise dafür verwende:

BEARBEITEN: Int / Long-Varianten hinzugefügt ... und ein Copypasta-Tippfehler behoben ...

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;
    }
}

Beachten Sie jedoch, dass Kleinbuchstaben b in der Regel eher Bits als Bytes bedeuten können. :-) en.wikipedia.org/wiki/Data-rate_units#Kilobit_per_second
SharpC

32

Ich würde es lösen mit Extension methods, Math.PowFunktion und 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");
    }
}

und benutze es wie:

string h = x.ToSize(MyExtension.SizeUnits.KB);

3
Elegante Lösung!
Yossico

1
Ich habe Ihre Idee verwendet, um eine zu erstellen, die die Einheit automatisch bestimmt. +1
Louis Somers

2
Das ist eine sehr elegante Lösung, die viel sauberer ist und der zugelassenen Lösung entspricht. Streng genommen sollte es jedoch basierend auf den Enum-Werten auf der Potenz von 1000 basieren, dh nicht auf 1024- Code ( en.wikipedia.org/wiki/Terabyte ) ... public static string ToSize (dieser lange Wert, Unit unit) => $ "{value / Math.Pow (1000, (lange) Einheit): F2} {unit.ToString ()}";
Stoj

6

Die Kurzversion der am häufigsten gewählten Antwort hat Probleme mit TB-Werten.

Ich habe es entsprechend angepasst, um auch TB-Werte und immer noch ohne Schleife zu behandeln, und auch eine kleine Fehlerprüfung für negative Werte hinzugefügt. Hier ist meine Lösung:

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]);
}

1
Das angegebene Problem mit großen Werten sollte in der akzeptierten Antwort nicht mehr vorhanden sein.
JLRishe

5

Nein, hauptsächlich, weil es ein Nischenbedürfnis ist und es zu viele mögliche Variationen gibt. (Ist es "KB", "Kb" oder "Ko"? Ist ein Megabyte 1024 * 1024 Bytes oder 1024 * 1000 Bytes? - Ja, einige Orte verwenden das!)


1
+1 - laut Wikipedia kb => 1000 Bytes und KiB => 1024 Bytes.
Peter Majeed

5

Hier ist eine Option, die einfacher zu erweitern ist als Ihre, aber nein, es ist keine in die Bibliothek selbst integriert.

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();
}

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;
        }
    }

Dies ist auch eine Möglichkeit (die Nummer 1073741824.0 ist von 1024 * 1024 * 1024 aka GB)


3

@ Servys Antwort war nett und prägnant. Ich denke es kann noch einfacher sein?

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];
}

3

Basierend auf der eleganten Lösung von NeverHopeless:

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)
}

Vielleicht gibt es übermäßige Kommentare, aber ich neige dazu, sie zu hinterlassen, um zu verhindern, dass ich bei zukünftigen Besuchen dieselben Fehler mache ...



1

Ich habe einige der Antworten hier zu zwei Methoden zusammengefasst, die hervorragend funktionieren. Die zweite Methode unten konvertiert von einer Byte-Zeichenfolge (wie 1,5,1 GB) zurück in Bytes (wie 1621350140) als langen Typwert. Ich hoffe, dies ist nützlich für andere, die nach einer Lösung suchen, um Bytes in einen String und zurück in Bytes umzuwandeln.

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);
}

Darf ich fragen, warum Sie es gewohnt float.Parsesind double?
John_J

1

Ich weiß, das ist schon ein alter Thread. aber vielleicht wird jemand nach einer Lösung suchen. Und hier ist was ich benutze und der einfachste Weg

  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";
    }

0

Wie wäre es mit:

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

ZB gerne anrufen

printMB(123456);

Wird zur Ausgabe führen

"Size is 120,56 MB"

0

Ich habe mich für JerKimballs Lösung entschieden und Daumen hoch. Ich möchte jedoch hinzufügen / darauf hinweisen, dass dies in der Tat insgesamt kontrovers diskutiert wird. Bei meinen Recherchen (aus anderen Gründen) habe ich die folgenden Informationen gefunden.

Wenn normale Menschen (ich habe gehört, dass sie existieren) von Gigabyte sprechen, beziehen sie sich auf das metrische System, bei dem 1000 hoch 3 aus der ursprünglichen Anzahl von Bytes == der Anzahl von Gigabyte. Natürlich gibt es jedoch die IEC / JEDEC-Standards, die in Wikipedia gut zusammengefasst sind. Statt 1000 hoch x haben sie 1024. Was für physische Speichergeräte (und ich denke logisch wie Amazon und andere) eine bedeutet Immer größerer Unterschied zwischen Metrik und IEC. So ist beispielsweise 1 TB == 1 Terabyte-Metrik 1000 hoch 4, aber IEC bezeichnet die ähnliche Zahl offiziell als 1 TiB, tebibyte als 1024 hoch 4. Aber leider in nicht-technischen Anwendungen (würde ich Die Norm ist metrisch, und in meiner eigenen App für den internen Gebrauch erkläre ich derzeit den Unterschied in der Dokumentation. Aber zu Anzeigezwecken biete ich nicht einmal etwas anderes als Metrik an. Intern, obwohl es in meiner App nicht relevant ist, speichere ich nur Bytes und führe die Berechnung für die Anzeige durch.

Als Randnotiz finde ich es etwas mangelhaft, dass das .Net-Framework AFAIK (und ich irre mich häufig, danke an die vorhandenen Kräfte) selbst in seiner 4.5-Inkarnation in keiner internen Bibliothek etwas darüber enthält. Man würde erwarten, dass eine Open-Source-Bibliothek irgendwann NuGettable sein wird, aber ich gebe zu, dass dies ein kleiner Ärger ist. Andererseits haben System.IO.DriveInfo und andere auch nur Bytes (so lang), was ziemlich klar ist.



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);
    }
}

0

Wie wäre es mit einer Rekursion:

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");
  }
}

Dann können Sie es nennen:

ReturnSize(size, string.Empty);

0

Wie oben angegeben, ist die Rekursion mithilfe des Logarithmus der bevorzugte Weg.

Die folgende Funktion hat 3 Argumente: die Eingabe, die Dimensionsbeschränkung der Ausgabe, das ist das dritte 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);
}

Lassen Sie uns nun 12 GB RAM in mehrere Einheiten konvertieren:

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
}

0

Ich benutze dies für Windows (binäre Präfixe):

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;
}

0

Ich habe dies (mit wenig bis gar keiner Änderung) in einen UWP DataBinding Converter für mein Projekt integriert und dachte, es könnte auch für andere nützlich sein.

Der Code lautet:

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]);
        }
    }
}

Fügen Sie Ihrem UserControl oder Ihrer Page XAML eine lokale Ressource hinzu, um sie zu verwenden:

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

Verweisen Sie in einer Datenbindungsvorlage oder Datenbindungsinstanz darauf:

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

Und hey Presto. Die Magie passiert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.