سؤال

هل هناك أي طريقة سهلة لإنشاء فئة تستخدم 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 بايت" وما إلى ذلك وهلم جرا.

هل كانت مفيدة؟

المحلول

أنا أستخدم هذا، وأحصل عليه من الويب

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

أتمنى أن يساعدك هذا.

نصائح أخرى

حسنًا، لن أختتم الأمر كموفر تنسيق، ولكن بدلاً من إعادة اختراع العجلة، هناك استدعاء Win32 API لتنسيق سلسلة حجم بناءً على وحدات البايت المتوفرة التي استخدمتها عدة مرات في تطبيقات مختلفة.

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

لذلك أتصور أنك يجب أن تكون قادرًا على تجميع مزود باستخدام ذلك باعتباره رمز التحويل الأساسي.

وهنا أ وصلة إلى مواصفات MSDN لـ StrFormatByteSize.

أدرك الآن أنك كنت تطلب بالفعل شيئًا من شأنه أن يعمل مع String.Format() - أعتقد أنه كان يجب علي قراءة السؤال مرتين قبل النشر؛-)

لا يعجبني الحل الذي يتعين عليك فيه تمرير موفر التنسيق بشكل صريح في كل مرة - مما يمكنني جمعه منه هذا المقال, أفضل طريقة للتعامل مع هذا الأمر هي تنفيذ نوع FileSize، وتنفيذ واجهة IFormattable.

لقد تقدمت ونفذت بنية تدعم هذه الواجهة، والتي يمكن إخراجها من عدد صحيح.في واجهات برمجة التطبيقات المرتبطة بالملفات الخاصة بي، سأجعل خصائص .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}" لفرض التنسيق بالكيلوبايت.لكنني سأترك الأمر عند هذا الحد.

سأترك أيضًا مشاركتي الأولية أدناه لهذين الشخصين يفضلان عدم استخدام واجهة برمجة تطبيقات التنسيق...


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

ويمكنك بسهولة تغيير الوحدات والدقة إلى ما يناسب احتياجاتك :-)

هذا هو أبسط تطبيق أعرفه لتنسيق أحجام الملفات:

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

رمز بلدي...شكرا لشون أوستن.

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

كنت بحاجة إلى إصدار يمكن ترجمته ليناسب ثقافات مختلفة (فاصل عشري، ترجمة "بايت") ودعم لكل ما هو ممكن البادئات الثنائية (حتى إكسا).فيما يلي مثال يوضح كيفية استخدامه:

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

يمكن العثور على النهج الموجه بالمجال هنا: https://github.com/Corniel/Qowaiv/blob/master/src/Qowaiv/IO/StreamSize.cs

تعد بنية StreamSize تمثيلًا لحجم الدفق، وتسمح لك بالتنسيق التلقائي باستخدام الامتداد المناسب، ولكن أيضًا لتحديد أنك تريده بالكيلوبايت/ميجابايت أو أي شيء آخر.هذا له الكثير من المزايا، ليس فقط لأنك تحصل على التنسيق خارج الصندوق، بل يساعدك أيضًا على إنشاء نماذج أفضل، كما هو واضح من أن الخاصية أو نتيجة الطريقة تمثل حجم الدفق.كما أن لديها امتدادًا لحجم الملف:GetStreamSize (ملف FileInfo هذا).

تدوين قصير

  • new StreamSize(8900).ToString("s") => 8900b
  • حجم StreamSize الجديد (238900).ToString("s") => 238.9 كيلو بايت
  • حجم StreamSize(238900) الجديد.ToString("S") => 238.9 كيلو بايت
  • حجم StreamSize(238900) الجديد.ToString("0000.00 S") => 0238.90 كيلو بايت

التدوين الكامل

  • حجم StreamSize الجديد (8900).ToString("0.0 f") => 8900.0 بايت
  • new StreamSize(238900).ToString("0 f") => 234 كيلو بايت
  • new StreamSize(1238900).ToString("0.00 F") => 1.24 ميجابايت

مخصص

  • new StreamSize(8900).ToString("0.0 كيلو بايت") => 8.9 كيلو بايت
  • حجم StreamSize الجديد (238900).ToString("0.0 ميجابايت") => 0.2 ميجابايت
  • new StreamSize(1238900).ToString("#,##0.00 كيلو بايت") => 1,239.00 كيلو بايت
  • new StreamSize(1238900).ToString("#,##0") => 1,238,900

توجد حزمة NuGet، لذا يمكنك استخدام تلك الحزمة: https://www.nuget.org/packages/Qowaiv

لقد أخذت إجابة إدواردو ودمجتها مع مثال مماثل من مكان آخر لتوفير خيارات إضافية للتنسيق.

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

إذا قمت بتغيير:

      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 عن طريق ضبط الدقة حسب الحجم.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top