Поставщик формата файла

Есть ли простой способ создать класс, использующий IFormatProvider, который записывает удобный для пользователя размер файла?

public static string GetFileSizeString(string filePath)
{
    FileInfo info = new FileInfo(@"c:\windows\notepad.exe");
    long size = info.Length;
    string sizeString = size.ToString(FileSizeFormatProvider); // This is where the class does its magic...
}

В результате должны получиться строки, отформатированные как «2,5 МБ», «3,9 ГБ», «670 байт» и так далее.

Что насчет stackoverflow.com/questions/281640/…?

Kiquenet 18.07.2013 12:08

И stackoverflow.com/questions/14488796/… ...

vapcguy 01.04.2015 06:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
71
2
45 664
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Ответ принят как подходящий

Я использую этот, я получаю его из Интернета

public class FileSizeFormatProvider : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter)) return this;
        return null;
    }

    private const string fileSizeFormat = "fs";
    private const Decimal OneKiloByte = 1024M;
    private const Decimal OneMegaByte = OneKiloByte * 1024M;
    private const Decimal OneGigaByte = OneMegaByte * 1024M;

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {    
        if (format == null || !format.StartsWith(fileSizeFormat))    
        {    
            return defaultFormat(format, arg, formatProvider);    
        }

        if (arg is string)    
        {    
            return defaultFormat(format, arg, formatProvider);    
        }

        Decimal size;

        try    
        {    
            size = Convert.ToDecimal(arg);    
        }    
        catch (InvalidCastException)    
        {    
            return defaultFormat(format, arg, formatProvider);    
        }

        string suffix;
        if (size > OneGigaByte)
        {
            size /= OneGigaByte;
            suffix = "GB";
        }
        else if (size > OneMegaByte)
        {
            size /= OneMegaByte;
            suffix = "MB";
        }
        else if (size > OneKiloByte)
        {
            size /= OneKiloByte;
            suffix = "kB";
        }
        else
        {
            suffix = " B";
        }

        string precision = format.Substring(2);
        if (String.IsNullOrEmpty(precision)) precision = "2";
        return String.Format("{0:N" + precision + "}{1}", size, suffix);

    }

    private static string defaultFormat(string format, object arg, IFormatProvider formatProvider)
    {
        IFormattable formattableArg = arg as IFormattable;
        if (formattableArg != null)
        {
            return formattableArg.ToString(format, formatProvider);
        }
        return arg.ToString();
    }

}

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

Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 100));
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 10000));

Кредиты на http://flimflan.com/blog/FileSizeFormatProvider.aspx

Проблема с ToString (), ожидается тип NumberFormatInfo, реализующий IFormatProvider, но класс NumberFormatInfo запечатан :(

Если вы используете C# 3.0, вы можете использовать метод расширения, чтобы получить желаемый результат:

public static class ExtensionMethods
{
    public static string ToFileSize(this long l)
    {
        return String.Format(new FileSizeFormatProvider(), "{0:fs}", l);
    }
}

Вы можете использовать это так.

long l = 100000000;
Console.WriteLine(l.ToFileSize());

Надеюсь это поможет.

Хороший ответ. Было бы даже лучше, если бы вы отредактировали, чтобы включить пример использования.

AR. 24.09.2008 21:55

Просто для пояснения ответ был обновлен примером использования. @Eduardo Campañó, спасибо, отлично работает! : D

Rami A. 09.12.2011 09:15

Хорошо, я не собираюсь заканчивать это как поставщик формата, но вместо того, чтобы изобретать колесо, есть вызов Win32 api для форматирования строки размера на основе предоставленных байтов, которые я много раз использовал в различных приложениях.

[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize( long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize );

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

Вот связь спецификации MSDN для StrFormatByteSize.

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

Simon Mourier 20.08.2013 12:51

На сайте PInvoke.net есть отличный пример обертывания этого метода, который можно легко преобразовать в метод расширения. pinvoke.net/default.aspx/shlwapi.strformatbytesize

James 23.03.2016 19:08

Мой код ... спасибо за Шона Остина.

[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize(long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize);

public void getFileInfo(string filename)
{
    System.IO.FileInfo fileinfo = new FileInfo(filename);
    this.FileName.Text = fileinfo.Name;
    StringBuilder buffer = new StringBuilder();
    StrFormatByteSize(fileinfo.Length, buffer, 100);
    this.FileSize.Text = buffer.ToString();
}

Я взял ответ Эдуардо и объединил его с аналогичным примером из другого места, чтобы предоставить дополнительные параметры форматирования.

public class FileSizeFormatProvider : IFormatProvider, ICustomFormatter
{
   public object GetFormat(Type formatType)
   {
      if (formatType == typeof(ICustomFormatter))
      {
         return this;
      }

      return null;
   }

   private const string fileSizeFormat = "FS";
   private const string kiloByteFormat = "KB";
   private const string megaByteFormat = "MB";
   private const string gigaByteFormat = "GB";
   private const string byteFormat = "B";
   private const Decimal oneKiloByte = 1024M;
   private const Decimal oneMegaByte = oneKiloByte * 1024M;
   private const Decimal oneGigaByte = oneMegaByte * 1024M;

   public string Format(string format, object arg, IFormatProvider formatProvider)
   {
      //
      // Ensure the format provided is supported
      //
      if (String.IsNullOrEmpty(format) || !(format.StartsWith(fileSizeFormat, StringComparison.OrdinalIgnoreCase) ||
                                            format.StartsWith(kiloByteFormat, StringComparison.OrdinalIgnoreCase) ||
                                            format.StartsWith(megaByteFormat, StringComparison.OrdinalIgnoreCase) ||
                                            format.StartsWith(gigaByteFormat, StringComparison.OrdinalIgnoreCase)))
      {
         return DefaultFormat(format, arg, formatProvider);
      }

      //
      // Ensure the argument type is supported
      //
      if (!(arg is long || arg is decimal || arg is int))
      {
         return DefaultFormat(format, arg, formatProvider);
      }

      //
      // Try and convert the argument to decimal
      //
      Decimal size;

      try
      {
         size = Convert.ToDecimal(arg);
      }
      catch (InvalidCastException)
      {
         return DefaultFormat(format, arg, formatProvider);
      }

      //
      // Determine the suffix to use and convert the argument to the requested size
      //
      string suffix;

      switch (format.Substring(0, 2).ToUpper())
      {
         case kiloByteFormat:
            size = size / oneKiloByte;
            suffix = kiloByteFormat;
            break;
         case megaByteFormat:
            size = size / oneMegaByte;
            suffix = megaByteFormat;
            break;
         case gigaByteFormat:
            size = size / oneGigaByte;
            suffix = gigaByteFormat;
            break;
         case fileSizeFormat:
            if (size > oneGigaByte)
            {
               size /= oneGigaByte;
               suffix = gigaByteFormat;
            }
            else if (size > oneMegaByte)
            {
               size /= oneMegaByte;
               suffix = megaByteFormat;
            }
            else if (size > oneKiloByte)
            {
               size /= oneKiloByte;
               suffix = kiloByteFormat;
            }
            else
            {
               suffix = byteFormat;
            }
            break;
         default:
            suffix = byteFormat;
            break;
      }

      //
      // Determine the precision to use
      //
      string precision = format.Substring(2);

      if (String.IsNullOrEmpty(precision))
      {
         precision = "2";
      }

      return String.Format("{0:N" + precision + "}{1}", size, suffix);
   }

   private static string DefaultFormat(string format, object arg, IFormatProvider formatProvider)
   {
      IFormattable formattableArg = arg as IFormattable;

      if (formattableArg != null)
      {
         return formattableArg.ToString(format, formatProvider);
      }

      return arg.ToString();
   }
}

Теперь я понимаю, что вы на самом деле просили что-то, что работало бы с String.Format () - думаю, мне следовало прочитать вопрос дважды, прежде чем публиковать ;-)

Мне не нравится решение, в котором вы должны каждый раз явно передавать поставщик формата - из того, что я мог понять из эта статья, лучший способ приблизиться к этому - реализовать тип FileSize, реализующий интерфейс IFormattable.

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

Вот код:

using System.Globalization;

public struct FileSize : IFormattable
{
    private ulong _value;

    private const int DEFAULT_PRECISION = 2;

    private static IList<string> Units;

    static FileSize()
    {
        Units = new List<string>(){
            "B", "KB", "MB", "GB", "TB"
        };
    }

    public FileSize(ulong value)
    {
        _value = value;
    }

    public static explicit operator FileSize(ulong value)
    {
        return new FileSize(value);
    }

    override public string ToString()
    {
        return ToString(null, null);
    }

    public string ToString(string format)
    {
        return ToString(format, null);
    }

    public string ToString(string format, IFormatProvider formatProvider)
    {
        int precision;

        if (String.IsNullOrEmpty(format))
            return ToString(DEFAULT_PRECISION);
        else if (int.TryParse(format, out precision))
            return ToString(precision);
        else
            return _value.ToString(format, formatProvider);
    }

    /// <summary>
    /// Formats the FileSize using the given number of decimals.
    /// </summary>
    public string ToString(int precision)
    {
        double pow = Math.Floor((_value > 0 ? Math.Log(_value) : 0) / Math.Log(1024));
        pow = Math.Min(pow, Units.Count - 1);
        double value = (double)_value / Math.Pow(1024, pow);
        return value.ToString(pow == 0 ? "F0" : "F" + precision.ToString()) + " " + Units[(int)pow];
    }
}

И простой модульный тест, демонстрирующий, как это работает:

    [Test]
    public void CanUseFileSizeFormatProvider()
    {
        Assert.AreEqual(String.Format("{0}", (FileSize)128), "128 B");
        Assert.AreEqual(String.Format("{0}", (FileSize)1024), "1.00 KB");
        Assert.AreEqual(String.Format("{0:0}", (FileSize)10240), "10 KB");
        Assert.AreEqual(String.Format("{0:1}", (FileSize)102400), "100.0 KB");
        Assert.AreEqual(String.Format("{0}", (FileSize)1048576), "1.00 MB");
        Assert.AreEqual(String.Format("{0:D}", (FileSize)123456), "123456");

        // You can also manually invoke ToString(), optionally with the precision specified as an integer:
        Assert.AreEqual(((FileSize)111111).ToString(2), "108.51 KB");
    }

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

Я думаю, вы могли бы пойти дальше, например, разрешив явный выбор формата, например "{0: KB}" для принудительного форматирования в килобайтах. Но я собираюсь оставить все как есть.

Я также оставляю свой первоначальный пост ниже, потому что эти двое предпочитают не использовать API форматирования ...


100 способов снять шкуру с кошки, но вот мой подход - добавление метода расширения к типу int:

public static class IntToBytesExtension
{
    private const int PRECISION = 2;

    private static IList<string> Units;

    static IntToBytesExtension()
    {
        Units = new List<string>(){
            "B", "KB", "MB", "GB", "TB"
        };
    }

    /// <summary>
    /// Formats the value as a filesize in bytes (KB, MB, etc.)
    /// </summary>
    /// <param name = "bytes">This value.</param>
    /// <returns>Filesize and quantifier formatted as a string.</returns>
    public static string ToBytes(this int bytes)
    {
        double pow = Math.Floor((bytes>0 ? Math.Log(bytes) : 0) / Math.Log(1024));
        pow = Math.Min(pow, Units.Count-1);
        double value = (double)bytes / Math.Pow(1024, pow);
        return value.ToString(pow==0 ? "F0" : "F" + PRECISION.ToString()) + " " + Units[(int)pow];
    }
}

С этим расширением в вашей сборке, чтобы отформатировать размер файла, просто используйте оператор вроде (1234567) .ToBytes ()

Следующий тест MbUnit точно разъясняет, как выглядит результат:

    [Test]
    public void CanFormatFileSizes()
    {
        Assert.AreEqual("128 B", (128).ToBytes());
        Assert.AreEqual("1.00 KB", (1024).ToBytes());
        Assert.AreEqual("10.00 KB", (10240).ToBytes());
        Assert.AreEqual("100.00 KB", (102400).ToBytes());
        Assert.AreEqual("1.00 MB", (1048576).ToBytes());
    }

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

Единственная проблема, которую я вижу в этом, - это метод расширения "ToBytes", который, как мне кажется, можно спутать с методом, преобразующим int в байты (как вы знаете, byte - это тип данных в C#). Лучше было бы переименовать его во что-то другое, возможно «AsByteCount». В противном случае я думаю, что оба варианта - хорошие решения.

Skurmedel 17.11.2010 17:29

+1 Обратите внимание, что модульные тесты инвертированы [должно быть Assert.AreEqual (ожидаемый, фактический)] в верхнем решении, а нижнее - в правильном порядке параметров.

jv42 18.08.2011 14:18

Это решение более или менее равно решению, которое я опубликовал. :) Самая большая разница в том, что моя реализация поддерживает больше форматов (long, short, kB и kiB). Но и подход тоже хороший!

Corniel Nobel 26.10.2018 18:04

Если вы измените:

      if (String.IsNullOrEmpty(precision))
      {
         precision = "2";
      }

в

      if (String.IsNullOrEmpty(precision))
      {
        if (size < 10)
        {
           precision = "2";
        }
        else if (size < 100)
        {
            precision = "1";
        }
        else
        {
           precision = "0";
        }
      }

результаты без дополнительного спецификатора точности (так что просто 0: fs вместо 0: fs3) начнут имитировать StrFormatByteSize () Win32 путем корректировки точности по размеру.

так как переключение передач - очень дешевая операция

public static string ToFileSize(this long size)
{
    if (size < 1024)
    {
        return (size).ToString("F0") + " bytes";
    }
    else if ((size >> 10) < 1024)
    {
        return (size/(float)1024).ToString("F1") + " KB";
    }
    else if ((size >> 20) < 1024)
    {
        return ((size >> 10) / (float)1024).ToString("F1") + " MB";
    }
    else if ((size >> 30) < 1024)
    {
        return ((size >> 20) / (float)1024).ToString("F1") + " GB";
    }
    else if ((size >> 40) < 1024)
    {
        return ((size >> 30) / (float)1024).ToString("F1") + " TB";
    }
    else if ((size >> 50) < 1024)
    {
        return ((size >> 40) / (float)1024).ToString("F1") + " PB";
    }
    else
    {
        return ((size >> 50) / (float)1024).ToString("F0") + " EB";
    }
}

Мне нужна была версия, которая может быть локализована для разных культур (десятичный разделитель, «байтовый» перевод) и поддержка всех возможных двоичные префиксы (до Exa). Вот пример, демонстрирующий, как его использовать:

// force "en-US" culture for tests
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(1033); 

// Displays "8.00 EB"
Console.WriteLine(FormatFileSize(long.MaxValue)); 

// Use "fr-FR" culture. Displays "20,74 ko", o is for "octet"
Console.WriteLine(FormatFileSize(21234, "o", null, CultureInfo.GetCultureInfo(1036)));

А вот код:

    /// <summary>
    /// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes or exabytes, depending on the size
    /// </summary>
    /// <param name = "size">The size.</param>
    /// <returns>
    /// The number converted.
    /// </returns>
    public static string FormatFileSize(long size)
    {
        return FormatFileSize(size, null, null, null);
    }

    /// <summary>
    /// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes or exabytes, depending on the size
    /// </summary>
    /// <param name = "size">The size.</param>
    /// <param name = "byteName">The string used for the byte name. If null is passed, "B" will be used.</param>
    /// <param name = "numberFormat">The number format. If null is passed, "N2" will be used.</param>
    /// <param name = "formatProvider">The format provider. May be null to use current culture.</param>
    /// <returns>The number converted.</returns>
    public static string FormatFileSize(long size, string byteName, string numberFormat, IFormatProvider formatProvider)
    {
        if (size < 0)
            throw new ArgumentException(null, "size");

        if (byteName == null)
        {
            byteName = "B";
        }

        if (string.IsNullOrEmpty(numberFormat))
        {
            numberFormat = "N2";
        }

        const decimal K = 1024;
        const decimal M = K * K;
        const decimal G = M * K;
        const decimal T = G * K;
        const decimal P = T * K;
        const decimal E = P * K;

        decimal dsize = size;

        string suffix = null;
        if (dsize >= E)
        {
            dsize /= E;
            suffix = "E";
        }
        else if (dsize >= P)
        {
            dsize /= P;
            suffix = "P";
        }
        else if (dsize >= T)
        {
            dsize /= T;
            suffix = "T";
        }
        else if (dsize >= G)
        {
            dsize /= G;
            suffix = "G";
        }
        else if (dsize >= M)
        {
            dsize /= M;
            suffix = "M";
        }
        else if (dsize >= K)
        {
            dsize /= K;
            suffix = "k";
        }
        if (suffix != null)
        {
            suffix = " " + suffix;
        }
        return string.Format(formatProvider, "{0:" + numberFormat + "}" + suffix + byteName, dsize);
    }

это самая простая реализация, которую я знаю для форматирования размеров файлов:

public string SizeText
{
    get
    {
        var units = new[] { "B", "KB", "MB", "GB", "TB" };
        var index = 0;
        double size = Size;
        while (size > 1024)
        {
            size /= 1024;
            index++;
        }
        return string.Format("{0:2} {1}", size, units[index]);
    }
}

В то время как Размер - это размер неформатированного файла в байтах.

Привет Христианин

http://www.wpftutorial.net

Вот более точное расширение:

    public static string FileSizeFormat(this long lSize)
    {
        double size = lSize;
        int index = 0;
        for(; size > 1024; index++)
            size /= 1024;
        return size.ToString("0.000 " + new[] { "B", "KB", "MB", "GB", "TB" }[index]);          
    }

Шлейф do...while на мой взгляд лучше, но вариант хороший. Немного копии ответа Кристиана Мозера - просто метод. Но я предполагаю, что есть некоторые, кто не понимает, что вы могли бы преобразовать его ответ в это.

vapcguy 01.04.2015 06:41

Подход, основанный на домене, можно найти здесь: https://github.com/Corniel/Qowaiv/blob/master/src/Qowaiv/IO/StreamSize.cs

Структура StreamSize представляет собой представление размера потока, позволяет вам как автоматически форматировать с правильным расширением, так и указывать, что вы хотите, в КБ / МБ или в любом другом формате. Это дает много преимуществ, не только потому, что вы получаете форматирование из коробки, но также помогает вам создавать лучшие модели, поскольку очевидно, что свойство или результат метода представляет размер потока. Он также имеет расширение размера файла: GetStreamSize (этот файл FileInfo).

Краткое обозначение

  • новый StreamSize (8900) .ToString ("s") => 8900b
  • new StreamSize (238900) .ToString ("s") => 238.9kb
  • new StreamSize (238900) .ToString ("S") => 238,9 кБ
  • new StreamSize (238900) .ToString ("0000.00 S") => 0238.90 kB

Полное обозначение

  • новый StreamSize (8900) .ToString ("0.0 f") => 8900.0 байт
  • new StreamSize (238900) .ToString ("0 f") => 234 килобайт
  • new StreamSize (1238900) .ToString ("0.00 F") => 1,24 мегабайта

Обычай

  • новый StreamSize (8900) .ToString ("0.0 kb") => 8.9 kb
  • new StreamSize (238900) .ToString ("0,0 МБ") => 0,2 МБ
  • new StreamSize (1238900) .ToString ("#, ## 0,00 килобайт") => 1,239,00 килобайт
  • новый StreamSize (1238900) .ToString ("#, ## 0") => 1,238,900

Существует NuGet-пакет, поэтому вы можете просто использовать его: https://www.nuget.org/packages/Qowaiv

Другие вопросы по теме