.NET, baytları KB, MB, GB vb. Biçimlere dönüştürmek için kolay bir yol sağlıyor mu?


112

.NET'in bunu yapmak için temiz bir yol sağlayıp sağlamadığını merak ediyorum:

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

vb...

Yanıtlar:


193

İşte bunu yapmanın oldukça kısa bir yolu:

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

Ve işte önerdiğim orijinal uygulama, marjinal olarak daha yavaş olabilir, ancak izlemesi biraz daha kolay:

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

Unutulmaması gereken bir şey - SI gösteriminde, "kilo" genellikle küçük bir k harfini kullanırken, daha büyük birimlerin tümü büyük harf kullanır. Windows KB, MB, GB kullanıyor, bu yüzden yukarıdaki KB'yi kullandım, ancak bunun yerine kB'yi düşünebilirsiniz.


Soru soran kişi yalnızca 1 ondalık doğruluk hanesini arıyor. Yanlış çıktı üreten bir girdi örneği verebilir misiniz?
JLRishe

2
Her iki örnek de artık kayan nokta bölmesini kullanır, bu nedenle yuvarlama hataları hakkında daha az endişe duyulmalıdır.
JLRishe

Teşekkür ederim, tam da aradığım şey. (2. uygulama.)
snapplex

1
Çok düzgün bir uygulama. Bu işleve 0 değerini iletirseniz, bir IndexOutOfRangeException oluşturacağını unutmayın. if (value == 0) { return "0"; }Fonksiyonun içine bir kontrol eklemeye karar verdim .
bounav

Dosya boyutunun <0 olduğu durumu sağlayabilir misiniz? Benim için tuhaf görünüyor ...
Ruslan F.

86

Ödemeye bytesize kütüphanesi. Bu var System.TimeSpanbayt için!

Dönüştürmeyi ve biçimlendirmeyi sizin için halleder.

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

Aynı zamanda dize gösterimi ve ayrıştırması da yapar.

// 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
Kullanımı ve anlaşılması basittir ve .Net 4.0 ve üzeri ile çalışır.
The Joker

34
Bu, .NET çerçevesinin bir parçası olarak eklenmelidir
helios456

Gördüğüm tek sorun, dönüştürme yöntemlerinin yalnızca bayt olmayandan bayta doğru çalıştığı, ancak tersi olmadığı.
SuperJMN

@SuperJMN bayt olmayan ne demek istiyorsun? Bitler gibi mi? Kullanabileceğiniz bir .FromBits yöntemi var.
Omar

1
Kaynak verileriniz "bayt" dışında bir şeyse ve herhangi bir şeye dönüşebilmeniz gerekiyorsa ... kullanmanız gereken kitaplık budur.
James Blake

38

Herkes kendi yöntemlerini yayınladığından, genellikle bunun için kullandığım uzantı yöntemini göndereceğimi düşündüm:

DÜZENLEME: int / long varyantları eklendi ... ve bir copypasta yazım hatası düzeltildi ...

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

Küçük b harfinin baytlardan ziyade bitleri gösterebileceğini unutmayın. :-) en.wikipedia.org/wiki/Data-rate_units#Kilobit_per_second
SharpC

32

Ben kullanarak çözecek Extension methods, Math.Powfonksiyon ve 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");
    }
}

ve şu şekilde kullanın:

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

3
Zarif çözüm!
yossico

1
Birimi otomatik olarak belirleyen bir fikir oluşturmak için fikrinizi kullandım. +1
Louis Somers

2
Bu çok şık bir çözüm, çok daha temiz ve onaylanmış çözümden daha makul. Bununla birlikte, enum değerlerine göre kesin olarak konuşursak, 1000'in gücüne dayanmalıdır, yani 1024 ( en.wikipedia.org/wiki/Terabyte ) kodu değil ... public static string ToSize (bu uzun değer, Birim birimi) => $ "{value / Math.Pow (1000, (uzun) birim): F2} {unit.ToString ()}";
stoj

6

En çok oylanan cevabın kısa versiyonunda TB değerleriyle ilgili sorunlar var.

Tb değerlerini de işlemek için uygun şekilde ayarladım ve yine de döngü olmadan ve ayrıca negatif değerler için küçük bir hata kontrolü ekledim. İşte benim çözümüm:

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
Büyük değerlere sahip belirtilen konu artık kabul edilen cevapta yer almamalıdır.
JLRishe

5

Hayır. Çoğunlukla niş bir ihtiyaç olduğu ve çok fazla olası varyasyon olduğu için. ("KB", "Kb" veya "Ko" mu? Megabayt 1024 * 1024 bayt veya 1024 * 1000 bayt mı? - evet, bazı yerler bunu kullanıyor!)


1
+1 - Wikipedia'ya göre , kb => 1000 bayt ve KiB => 1024 bayt.
Peter Majeed

5

Burada genişletmesi sizinkinden daha kolay bir seçenek var, ancak hayır, kitaplığın kendisinde yerleşik hiçbir seçenek yok.

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

Bu aynı zamanda yapmanın bir yoludur (1073741824.0 sayısı 1024 * 1024 * 1024 aka GB'dir)


3

@ Servy'nin cevabı güzel ve kısa oldu. Daha da basit olabileceğini düşünüyorum.

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

NeverHopeless'ın zarif çözümüne dayanmaktadır:

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

Belki aşırı yorumlar var, ama onları gelecekteki ziyaretlerimde aynı hataları tekrar yapmaktan alıkoymak için bırakıyorum ...



1

Buradaki bazı cevapları harika çalışan iki yöntemde birleştirdim. Aşağıdaki ikinci yöntem, uzun bir tür değeri olarak bir bayt dizesinden (1.5.1 GB gibi) tekrar bayta (1621350140 gibi) dönüşecektir. Umarım bu, baytları bir dizeye ve tekrar bayta dönüştürmek için bir çözüm arayan başkaları için yararlıdır.

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

Kullanmak sorabilir miyim float.Parsehiç double?
John_J

1

Bunun eski ileti dizisi olduğunu biliyorum. ama belki birisi çözüm arayacaktır. Ve işte kullandığım şey ve en kolay yol

  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

Peki ya:

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

Örneğin şöyle arayın

printMB(123456);

Çıktıyla sonuçlanacak

"Size is 120,56 MB"

0

JerKimballs çözümüne gittim ve bunu çok beğendim. Bununla birlikte, bunun aslında bir bütün olarak bir tartışma konusu olduğunu eklemek / belirtmek isterim. Araştırmamda (başka nedenlerden dolayı) aşağıdaki bilgilerle karşılaştım.

Normal insanlar (var olduklarını duydum) gigabayttan bahsettiklerinde, metrik sisteme atıfta bulunurlar; burada, orijinal bayt sayısından 3'ün üssü == gigabayt sayısıdır. Bununla birlikte, elbette, wikipedia'da güzel bir şekilde özetlenen IEC / JEDEC standartları vardır; bunlar 1000'e x kuvvetine karşılık 1024'e sahiptir. Fiziksel depolama aygıtları için hangisi (ve amazon ve diğerleri gibi mantıksal) metrik ile IEC arasında giderek artan fark. Yani örneğin 1 TB == 1 terabayt metrik 1000 üzeri 4 üssüdür, ancak IEC resmi olarak benzer sayıyı 1 TiB, tebibyte olarak 1024 üssü 4 olarak ifade eder. Ancak, ne yazık ki teknik olmayan uygulamalarda kitleye göre gidin) norm metriktir ve dahili kullanım için kendi uygulamamda şu anda dokümantasyondaki farkı açıklıyorum. Ancak görüntüleme amaçlı olarak, metrikten başka bir şey bile teklif etmiyorum. Uygulamamla alakalı olmasa da dahili olarak yalnızca baytları depoluyorum ve görüntüleme için hesaplamayı yapıyorum.

Bir yan not olarak, .Net çerçevesi AFAIK'in 4.5 enkarnasyonunda bile dahili olarak herhangi bir kütüphanede bununla ilgili hiçbir şey içermediğini biraz yetersiz buluyorum. Biri bir tür açık kaynak kitaplığının bir noktada NuGettable olmasını bekleyebilir, ancak bunun küçük bir sorun olduğunu kabul ediyorum. Öte yandan System.IO.DriveInfo ve diğerleri de yalnızca baytlara (uzun süre) sahiptir ve bu oldukça açıktır.


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

Biraz özyinelemeye ne dersin:

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

O zaman arayabilirsin:

ReturnSize(size, string.Empty);

0

Yukarıda belirtildiği gibi, logaritmanın yardımıyla özyineleme en sık kullanılan yoldur.

Aşağıdaki fonksiyonun 3 argümanı vardır: girdi, çıktının boyut kısıtı, yani üçüncü argüman.

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

Şimdi 12GB RAM'i birkaç birimde dönüştürelim:

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

Bunu Windows için kullanıyorum (ikili önekler):

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

Bunu (çok az veya hiç değişiklik yapmadan) projem için bir UWP DataBinding Converter'a dahil ettim ve başkaları için de yararlı olabileceğini düşündüm.

Kod:

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

Kullanmak için, UserControl veya Page XAML'nize yerel bir kaynak ekleyin:

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

Bir veri bağlama şablonunda veya veri bağlama örneğinde referans verin:

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

Ve hey presto. Sihir gerçekleşir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.