Apakah .NET menyediakan cara mudah mengonversi byte ke KB, MB, GB, dll.?


112

Hanya ingin tahu apakah .NET menyediakan cara yang bersih untuk melakukan ini:

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

dll ...

Jawaban:


192

Berikut ini cara yang cukup ringkas untuk melakukan ini:

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

Dan inilah implementasi asli yang saya sarankan, yang mungkin sedikit lebih lambat, tetapi sedikit lebih mudah diikuti:

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

Satu hal yang perlu diingat - dalam notasi SI, "kilo" biasanya menggunakan huruf kecil k sedangkan semua unit yang lebih besar menggunakan huruf kapital. Windows menggunakan KB, MB, GB, jadi saya telah menggunakan KB di atas, tetapi Anda dapat mempertimbangkan kB.


Penanya hanya mencari akurasi 1 tempat desimal. Bisakah Anda memberi contoh masukan yang menghasilkan keluaran yang salah?
JLRishe

2
Kedua contoh tersebut sekarang menggunakan pembagian floating point sehingga perhatian tentang kesalahan pembulatan akan berkurang.
JLRishe

Terima kasih, apa yang saya cari. (Implementasi ke-2.)
snapplex

1
Implementasi yang sangat rapi. Perhatikan bahwa jika Anda meneruskan nilai 0 ke fungsi ini, IndexOutOfRangeException akan muncul. Saya memutuskan untuk menambahkan tanda if (value == 0) { return "0"; }centang di dalam fungsi.
bounav

Dapatkah Anda memberikan kasus ketika ukuran file <0? Bagi saya ini terlihat aneh ...
Ruslan F.

84

Checkout ByteSize perpustakaan. Ini System.TimeSpanuntuk byte!

Ini menangani konversi dan pemformatan untuk Anda.

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

Itu juga melakukan representasi string dan 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");

6
Mudah digunakan dan dipahami, serta berfungsi dengan .Net 4.0 dan yang lebih baru.
The Joker

34
Ini harus dimasukkan sebagai bagian dari kerangka .NET
helios456

Satu-satunya masalah yang saya lihat adalah bahwa metode konversi hanya bekerja dari non-byte ke byte, tetapi tidak sebaliknya.
SuperJMN

@SuperJMN apa maksudmu non-byte? Suka bit? Ada metode .FromBits yang dapat Anda gunakan.
Omar

1
Jika data sumber Anda adalah sesuatu selain "byte" dan Anda harus dapat mengonversinya menjadi apa pun ... inilah pustaka yang harus Anda gunakan.
James Blake

37

Karena semua orang memposting metode mereka, saya pikir saya akan memposting metode ekstensi yang biasanya saya gunakan untuk ini:

EDIT: menambahkan varian int / long ... dan memperbaiki kesalahan ketik copypasta ...

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

Ingatlah bahwa huruf kecil b biasanya dapat menandakan bit daripada byte. :-) en.wikipedia.org/wiki/Data-rate_units#Kilobit_per_second
SharpC

32

Saya akan menyelesaikannya menggunakan Extension methods, Math.Powfungsi dan 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");
    }
}

dan gunakan seperti:

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

3
Solusi yang elegan!
yossico

1
Saya menggunakan ide Anda untuk membuat satu yang secara otomatis menentukan unit. +1
Louis Somers

2
Itu adalah solusi yang sangat elegan, yang jauh lebih bersih dan konsisten daripada solusi yang disetujui. Namun, secara tegas berdasarkan nilai enum itu harus didasarkan pada pangkat 1000, yaitu bukan kode 1024 ( en.wikipedia.org/wiki/Terabyte ) ... string statis publik ToSize (nilai panjang ini, Unit unit) => $ "{nilai / Math.Pow (1000, (panjang) satuan): F2} {unit.ToString ()}";
stoj

6

Versi singkat dari jawaban yang paling banyak dipilih memiliki masalah dengan nilai TB.

Saya menyesuaikannya dengan tepat untuk menangani juga nilai tb dan masih tanpa loop dan juga menambahkan sedikit pengecekan kesalahan untuk nilai negatif. Inilah solusi saya:

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
Masalah yang dinyatakan dengan nilai-nilai besar seharusnya tidak lagi ada dalam jawaban yang diterima.
JLRishe

5

Tidak. Sebagian besar karena ini adalah kebutuhan yang khusus, dan ada terlalu banyak kemungkinan variasi. (Apakah itu "KB", "Kb" atau "Ko"? Apakah megabyte 1024 * 1024 byte, atau 1024 * 1000 byte? - ya, beberapa tempat menggunakannya!)


1
+1 - menurut Wikipedia , kb => 1000 byte, dan KiB => 1024 byte.
Peter Majeed

5

Berikut adalah opsi yang lebih mudah untuk diperluas daripada milik Anda, tetapi tidak, tidak ada yang ada di dalam perpustakaan itu sendiri.

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

Ini adalah salah satu cara untuk melakukannya juga (Nomor 1073741824.0 adalah dari 1024 * 1024 * 1024 alias GB)


3

Jawaban @ Servy bagus dan ringkas. Saya pikir itu bisa lebih sederhana?

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

Berdasarkan solusi elegan 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)
}

Mungkin ada komentar yang berlebihan, tetapi saya cenderung meninggalkannya untuk mencegah diri saya membuat kesalahan yang sama pada kunjungan berikutnya ...



1

Saya telah menggabungkan beberapa jawaban di sini menjadi dua metode yang bekerja dengan baik. Metode kedua di bawah ini akan mengonversi dari string byte (seperti 1,5.1 GB) kembali ke byte (seperti 1621350140) sebagai nilai tipe panjang. Saya harap ini berguna bagi orang lain yang mencari solusi untuk mengubah byte menjadi string dan kembali menjadi byte.

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

Bolehkah saya bertanya mengapa Anda gunakan float.Parseuntuk double?
John_J

1

Saya tahu ini adalah utas lama. tapi mungkin seseorang akan mencari solusinya. Dan inilah yang saya gunakan dan cara termudah

  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

Bagaimana tentang:

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

Misalnya panggilan suka

printMB(123456);

Akan menghasilkan keluaran

"Size is 120,56 MB"

0

Saya pergi untuk solusi JerKimballs, dan jempol untuk itu. Namun demikian, saya ingin menambahkan / menunjukkan bahwa ini memang merupakan kontroversi secara keseluruhan. Dalam penelitian saya (untuk alasan lain), saya mendapatkan informasi berikut.

Ketika orang normal (saya pernah mendengar mereka ada) berbicara tentang gigabyte, mereka mengacu pada sistem metrik di mana 1000 pangkat 3 dari jumlah asli byte == jumlah gigabyte. Namun, tentu saja ada standar IEC / JEDEC yang diringkas dengan baik di wikipedia, yang bukannya 1000 pangkat x mereka memiliki 1024. Yang untuk perangkat penyimpanan fisik (dan saya kira logis seperti amazon dan lainnya) berarti sebuah perbedaan yang semakin meningkat antara metrik vs IEC. Jadi misalnya 1 TB == 1 terabyte metrik adalah 1000 pangkat 4, tetapi IEC secara resmi menyebutkan angka yang sama sebagai 1 TiB, tebibyte sebagai 1024 pangkat 4. Namun, sayangnya, dalam aplikasi non-teknis (saya akan menurut audiens) normanya adalah metrik, dan di aplikasi saya sendiri untuk penggunaan internal saat ini saya menjelaskan perbedaan dalam dokumentasi. Tetapi untuk tujuan tampilan, saya bahkan tidak menawarkan apa pun kecuali metrik. Secara internal meskipun tidak relevan di aplikasi saya, saya hanya menyimpan byte dan melakukan perhitungan untuk tampilan.

Sebagai catatan tambahan, saya merasa agak kurang bersemangat bahwa kerangka kerja .Net AFAIK (dan saya sering salah terima kekuatan yang ada) bahkan dalam inkarnasinya 4,5 tidak mengandung apa pun tentang ini di perpustakaan mana pun secara internal. Orang akan mengharapkan perpustakaan sumber terbuka dari beberapa jenis menjadi NuGettable di beberapa titik, tapi saya akui ini adalah sedikit kesal. Di sisi lain System.IO.DriveInfo dan lainnya juga hanya memiliki byte (selama) yang agak jelas.


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

Bagaimana dengan beberapa rekursi:

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

Kemudian Anda dapat menyebutnya:

ReturnSize(size, string.Empty);

0

Seperti yang diposting di atas, rekursi adalah cara favorit, dengan bantuan logaritma.

Fungsi berikut memiliki 3 argumen: input, batasan dimensi output, itu adalah argumen ketiga.

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

Sekarang mari kita ubah RAM 12GB dalam beberapa unit:

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

Saya menggunakan ini untuk Windows (awalan biner):

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

Saya telah memasukkan ini (dengan sedikit atau tanpa modifikasi) ke dalam UWP DataBinding Converter untuk proyek saya dan berpikir itu mungkin juga berguna bagi orang lain.

Kodenya adalah:

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

Untuk menggunakannya, tambahkan sumber daya lokal ke UserControl atau Halaman XAML Anda:

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

Referensi dalam template data binding atau contoh data binding:

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

Dan hei presto. Keajaiban terjadi.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.