文件大小格式提供程序

有什么简单的方法可以创建一个使用 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 MB”、“ 3,9 GB”、“ 670字节”等等。

47909 次浏览

我用这个,从网上买的

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 ()有一个问题,它期望一个实现 IFormatProvider 的 NumberFormatInfo 类型,但是 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());

希望这个能帮上忙。

好吧,我不打算把它包装成一个格式提供者,但是这里有一个 Win32api 调用,可以根据提供的字节格式化一个大小字符串,这个重造轮子我在各种应用程序中已经使用过很多次了。

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

所以我认为你应该能够把一个提供者放在一起使用作为核心转换代码。

下面是针对 StrFormatByteSize 的 MSDN 规范的 链接

我的密码... 谢谢你的肖恩 · 奥斯汀。

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

我采用了 Eduardo 的答案,并将其与其他地方的一个类似示例结合起来,为格式化提供了其他选项。

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 接口。

我继续实现了一个支持这个接口的 struct,它可以从一个整数强制转换。在我自己的文件相关 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());
}

你可以很容易地改变单位和精度,以适合你的需要: -)

如果你改变:

      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) ,结果将通过调整精度到大小来模仿 Win32的 StrFormatByteSize ()。

因为转移是一个非常便宜的操作

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

而 Size 是未格式化的文件大小(以字节为单位)。

你好 克里斯蒂安

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

域驱动方法可以在这里找到: https://github.com/Corniel/Qowaiv/blob/master/src/Qowaiv/IO/StreamSize.cs

StreamSize 结构是流大小的表示形式,允许您使用适当的扩展名自动进行格式化,但也可以指定需要以 KB/MB 或其他形式进行格式化。这样做有很多好处,不仅因为可以立即进行格式设置,而且还有助于创建更好的模型,因为很明显,方法的属性或结果表示流大小。它还有一个文件大小的扩展名: GetStreamSize (这个 FileInfo 文件)。

简短的符号

  • New StreamSize (8900) . ToString (“ s”) = > 8900b
  • New StreamSize (238900) . ToString (“ s”) = > 238.9 kb
  • New StreamSize (238900) . ToString (“ S”) = > 238.9 kB
  • New StreamSize (238900) . ToString (“0000.00 S”) = > 0238.90 kB

完整符号

  • New StreamSize (8900) . ToString (“0.0 f”) = > 8900.0 byte
  • New StreamSize (238900) . ToString (“0 f”) = > 234 KB
  • New StreamSize (1238900) . ToString (“0.00 F”) = > 1.24 MB

习俗

  • New StreamSize (8900) . ToString (“0.0 kb”) = > 8.9 kb
  • New StreamSize (238900) . ToString (“0.0 MB”) = > 0.2 MB
  • New StreamSize (1238900) . ToString (“ # ,# # 0.00 Kilobyte”) = > 1,239.00 Kilobyte
  • New StreamSize (1238900) . ToString (“ # ,# # 0”) = > 1,238,900

有一个 NuGet 软件包,所以你可以直接使用它: https://www.nuget.org/packages/Qowaiv

使用 C # 9.0语法可以这样编写:

public static string ToFormatSize(ulong size)
{
return size switch
{
ulong s when s < 1024 => $"{size} bytes",
ulong s when s < (1024 << 10) => $"{Math.Round(size / 1024D, 2)} KB",
ulong s when s < (1024 << 20) => $"{Math.Round(size * 1D / (1024 << 10), 2)} MB",
ulong s when s < (1024 << 30) => $"{Math.Round(size * 1D / (1024L << 20), 2)} GB",
ulong s when s < (1024 << 40) => $"{Math.Round(size * 1D / (1024L << 30), 2)} TB",
ulong s when s < (1024 << 50) => $"{Math.Round(size * 1D / (1024L << 40), 2)} PB",
ulong s when s < (1024 << 60) => $"{Math.Round(size * 1D / (1024L << 50), 2)} EB",
_ => $"{size} bytes"
};
}