Обеспечивает ли.NET простой способ преобразования байтов в КБ, МБ, ГБ и т. Д.?

Просто интересно, если.NET предоставляет чистый способ сделать это:

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

так далее...

30 ответов

Решение

Вот довольно краткий способ сделать это:

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

И вот оригинальная реализация, которую я предложил, которая может быть немного медленнее, но немного легче следовать:

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

Оформить заказ на библиотеку ByteSize. Это System.TimeSpan для байтов!

Он обрабатывает преобразование и форматирование для вас.

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

Это также делает строковое представление и анализ.

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

Я бы решил это используя Extension methods, Math.Pow функция и 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");
    }
}

и используйте это как:

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

Поскольку все остальные публикуют свои методы, я решил опубликовать метод расширения, который я обычно использую для этого:

РЕДАКТИРОВАТЬ: добавил int/long варианты... и исправил опечатку 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;
    }
}

Я знаю, что это уже старая ветка. но может кто поищет решение. А вот что я использую и самый простой способ

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

Обновлено для C# 9.0реляционных шаблонов

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

Короткая версия ответа с наибольшим количеством голосов имеет проблемы со значениями ТБ.

Я настроил его соответствующим образом, чтобы обрабатывать также значения tb и все еще без цикла, а также добавил небольшую проверку ошибок для отрицательных значений. Вот мое решение:

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

@ Ответ Servy был приятным и лаконичным. Я думаю, что это может быть еще проще?

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

Нет. Главным образом потому, что это довольно нишевая потребность, и есть слишком много возможных вариантов. (Это "КБ", "КБ" или "Ко"? Мегабайт 1024 * 1024 байта или 1024 * 1000 байтов? - да, в некоторых местах это используется!)

Вот вариант, который легче расширить, чем ваш, но нет, в самой библиотеке нет встроенного.

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

Это один из способов сделать это также (номер 1073741824.0 от 1024*1024*1024 или ГБ)

На основе элегантного решения 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)
}

Возможно, есть чрезмерные комментарии, но я склонен оставлять их, чтобы не допустить повторения тех же ошибок в будущих посещениях...

Нет.

Но вы можете реализовать так;

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

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

Также посмотрите Как правильно преобразовать размер файла в байтах в мегабайты или гигабайты?

Поскольку люди сейчас в моде добавлять свои собственные алгоритмы в качестве ответов на этот вопрос, вот мой. (Хотя, по общему признанию, моя основная мотивация — опубликовать это где-нибудь, где я смогу найти его снова, когда он мне понадобится!)

Это кроссплатформенный метод расширения C#, реализующий возможности Windows.функция, которая выдает результаты с тремя или четырьмя значащими цифрами и до двух десятичных знаков. Он отличается от нескольких незначительных особенностей (см. ниже). Совместим с .NET 7 и более поздними версиями, хотя не составит труда перенести его на предыдущие версии.

ЕслиincludeTotalBytesявляетсяtrue, к строке добавляется общее количество байтов, аналогично тому, что показано в диалоговом окне свойств проводника Windows.

Код

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

Примеры использования

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

Отличия отStrFormatByteSize

  1. Числа в результирующей строке группируются там, где это необходимо.
  2. Результат округляется в большую или меньшую сторону по мере необходимости, тогда как StrFormatByteSize усекается после любого количества отображаемых десятичных знаков.
  3. Отрицательные значения работают должным образом, тогда как StrFormatByteSize предоставляет только общее количество байтов для отрицательных значений.

Как насчет рекурсии:

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

Тогда вы можете назвать это:

ReturnSize(size, string.Empty);

Я объединил некоторые ответы здесь в два метода, которые прекрасно работают. Второй метод, приведенный ниже, преобразует из байтовой строки (например, 1,5,1 ГБ) обратно в байты (например, 1621350140) как значение типа long. Я надеюсь, что это полезно для других, ищущих решение для преобразования байтов в строку и обратно в байты.

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

Недавно мне это понадобилось, и мне потребовалось преобразовать байты в число в long.

Применение: Byte.Kb.ToLong(1) должен дать 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.")
        };
    }
}

Тесты с использованием 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);
}

Я пошел на решение JerKimballs, и большие пальцы до этого. Тем не менее, я хотел бы добавить / указать, что это действительно вопрос спора в целом. В своем исследовании (по другим причинам) я нашел следующую информацию.

Когда нормальные люди (я слышал, что они существуют) говорят о гигабайтах, они ссылаются на метрическую систему, где 1000 в степени 3 от исходного числа байтов == число гигабайт. Тем не менее, конечно, есть стандарты IEC / JEDEC, которые хорошо подытожены в википедии, которые вместо 1000 к степени x имеют 1024. Что для физических запоминающих устройств (и я думаю, логично, таких как amazon и другие) означает постоянно увеличивающаяся разница между метрикой и IEC. Так, например, 1 ТБ == 1 терабайтная метрика равна 1000 в степени 4, но МЭК официально называет такое же число как 1 ТиБ, тэбибайт как 1024 в степени 4. Но, увы, в нетехнических приложениях (я бы по аудитории) норма является метрической, и в моем собственном приложении для внутреннего использования в настоящее время я объясняю разницу в документации. Но для демонстрации я даже не предлагаю ничего, кроме метрики. Внутренне, даже если это не имеет отношения к моему приложению, я сохраняю только байты и выполняю вычисления для отображения.

В качестве дополнительного примечания я нахожу несколько тусклым, что.Net-инфраструктура AFAIK (и я часто ошибаюсь, спасибо всем возможностям) даже в ее 4,5-ой версии не содержит ничего об этом ни в одной внутренней библиотеке. Можно было бы ожидать, что какая-то библиотека с открытым исходным кодом станет NuGettable в какой-то момент, но я признаю, что это небольшая мозоль. С другой стороны, System.IO.DriveInfo и другие также имеют только байты (длинные), что довольно понятно.

Как отмечалось выше, рекурсия является любимым способом с помощью логарифма.

Следующая функция имеет 3 аргумента: вход, ограничение размера выходных данных, то есть третий аргумент.

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

Теперь давайте конвертируем 12 ГБ ОЗУ в несколько единиц:

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
}

Хорошо... Давайте воспользуемся тем, что предоставляет Windows...

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

Я объединил код zackmark15 в универсальный подход к измерению файлов или каталогов:

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

        }

Я использую это для Windows (бинарные префиксы):

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

Вот мой ответ на @drzaus. Я изменил его, чтобы использовать ошибки округления в наших интересах и правильно управлять проблемами вокруг границ единиц. Он также обрабатывает отрицательные значения.

Брось это C# Program в LinqPad:

// Kudos: https://stackru.com/a/48467634/117797

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

Выход:

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

Как насчет:

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

Например, позвонить, как

printMB(123456);

Приведет к выводу

"Size is 120,56 MB"

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)

Искал более чистое решение, но, на мой взгляд, не могу его найти, так что вот.

Это предназначено для получения длинного значения размера файла с помощью File.Length, а затем преобразования его в тип размера и округления до двух десятичных знаков:

10 байт становятся 10Б

1030 КБ становится 1,01 МБ

16384 КБ становится 16 МБ

2 097152 КБ становятся 2 ГБ

13064424 КБ становится 12,46 ГБ

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

Я включил это (практически без изменений) в UWP DataBinding Converter для своего проекта и подумал, что он также может быть полезен другим.

Код такой:

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

Чтобы использовать его, добавьте локальный ресурс в свой UserControl или XAML страницы:

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

Ссылка на него в шаблоне привязки данных или экземпляре привязки данных:

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

И привет. Волшебство случается.

Вот как я это делаю.

      Console.Write(FileSizeInBytes > 1048576 ? FileSizeInBytes / 1048576f + " MB" : FileSizeInBytes / 1024f + " KB"); //1048576 = 1024 * 1024
Другие вопросы по тегам