C# 文件大小格式提供程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/128618/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
File-size format provider
提问by Seb Nilsson
Is there any easy way to create a class that uses IFormatProviderthat writes out a user-friendly file-size?
有没有什么简单的方法可以创建一个使用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...
}
It should result in strings formatted something like "2,5 MB", "3,9 GB", "670 bytes" and so on.
它应该导致字符串格式化为“ 2,5 MB”、“ 3,9 GB”、“ 670 字节”等。
采纳答案by Eduardo Campa?ó
I use this one, I get it from the web
我用这个,我从网上得到的
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();
}
}
an example of use would be:
一个使用示例是:
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 100));
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 10000));
Credits for http://flimflan.com/blog/FileSizeFormatProvider.aspx
http://flimflan.com/blog/FileSizeFormatProvider.aspx 的积分
There is a problem with ToString(), it's expecting a NumberFormatInfo type that implements IFormatProvider but the NumberFormatInfo class is sealed :(
ToString() 有问题,它需要一个实现 IFormatProvider 的 NumberFormatInfo 类型,但 NumberFormatInfo 类是密封的:(
If you're using C# 3.0 you can use an extension method to get the result you want:
如果您使用的是 C# 3.0,您可以使用扩展方法来获得您想要的结果:
public static class ExtensionMethods
{
public static string ToFileSize(this long l)
{
return String.Format(new FileSizeFormatProvider(), "{0:fs}", l);
}
}
You can use it like this.
你可以像这样使用它。
long l = 100000000;
Console.WriteLine(l.ToFileSize());
Hope this helps.
希望这可以帮助。
回答by Shaun Austin
OK I'm not going to wrap it up as a Format provider but rather than reinventing the wheel there's a Win32 api call to format a size string based on supplied bytes that I've used many times in various applications.
好的,我不打算将它包装为格式提供程序,而是使用 Win32 api 调用来根据我在各种应用程序中多次使用的提供的字节来格式化大小字符串,而不是重新发明轮子。
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize( long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize );
So I imagine you should be able to put together a provider using that as the core conversion code.
因此,我想您应该能够使用它作为核心转换代码组合一个提供程序。
Here's a linkto the MSDN spec for StrFormatByteSize.
这里有一个链接到MSDN规范的StrFormatByteSize。
回答by ariso
My code... thanks for Shaun Austin.
我的代码...感谢肖恩奥斯汀。
[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();
}
回答by Tyler Durden
I have taken Eduardo's answer and combined it with a similar example from elsewhere to provide additional options for the formatting.
我已经采用了 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();
}
}
回答by mindplay.dk
I realize now that you were actually asking for something that would work with String.Format() - I guess I should have read the question twice before posting ;-)
我现在意识到您实际上是在要求可以与 String.Format() 一起使用的东西-我想我应该在发布之前阅读该问题两次;-)
I don't like the solution where you have to explicitly pass in a format provider every time - from what I could gather from this article, the best way to approach this, is to implement a FileSize type, implementing the IFormattable interface.
我不喜欢每次都必须显式传入格式提供程序的解决方案 - 根据我从本文中收集到的信息,解决此问题的最佳方法是实现 FileSize 类型,实现 IFormattable 接口。
I went ahead and implemented a struct that supports this interface, and which can be cast from an integer. In my own file-related APIs, I will have my .FileSize properties return a FileSize instance.
我继续实现了一个支持这个接口的结构,它可以从一个整数转换。在我自己的文件相关 API 中,我将让我的 .FileSize 属性返回一个 FileSize 实例。
Here's the code:
这是代码:
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];
}
}
And a simple Unit Test that demonstrates how this works:
一个简单的单元测试演示了它是如何工作的:
[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");
}
As you can see, the FileSize type can now be formatted correctly, and it is also possible to specify the number of decimals, as well as applying regular numeric formatting if required.
如您所见,现在可以正确格式化 FileSize 类型,还可以指定小数位数,并在需要时应用常规数字格式。
I guess you could take this much further, for example allowing explicit format selection, e.g. "{0:KB}" to force formatting in kilobytes. But I'm going to leave it at this.
我想您可以更进一步,例如允许显式格式选择,例如“{0:KB}”强制以千字节为单位进行格式化。但我要离开它。
I'm also leaving my initial post below for those two prefer not to use the formatting API...
我还将在下面留下我最初的帖子,因为这两个不喜欢使用格式化 API ...
100 ways to skin a cat, but here's my approach - adding an extension method to the int type:
给猫剥皮的 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];
}
}
With this extension in your assembly, to format a filesize, simply use a statement like (1234567).ToBytes()
在程序集中使用此扩展名,要格式化文件大小,只需使用类似 (1234567).ToBytes() 的语句
The following MbUnit test clarifies precisely what the output looks like:
以下 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());
}
And you can easily change the units and precision to whatever suits your needs :-)
您可以轻松地将单位和精度更改为适合您需要的任何内容:-)
回答by wvd_vegt
If you change:
如果你改变:
if (String.IsNullOrEmpty(precision))
{
precision = "2";
}
into
进入
if (String.IsNullOrEmpty(precision))
{
if (size < 10)
{
precision = "2";
}
else if (size < 100)
{
precision = "1";
}
else
{
precision = "0";
}
}
the results without additional precision specifier (so just 0:fs instead of 0:fs3) will start to mimic Win32's StrFormatByteSize() by adjusting precision to size.
没有额外精度说明符的结果(所以只有 0:fs 而不是 0:fs3)将通过调整大小的精度开始模仿 Win32 的 StrFormatByteSize()。
回答by fubo
since shifting is a very cheap operation
因为转移是一个非常便宜的操作
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";
}
}
回答by Simon Mourier
I needed a version that can be localized for different cultures (decimal separator, "byte" translation) and support for all possible binary prefixes(up to Exa). Here is an example that demonstrates how to use it:
我需要一个可以针对不同文化(十进制分隔符、“字节”翻译)进行本地化并支持所有可能的二进制前缀(最多 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)));
And here is the code:
这是代码:
/// <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);
}
回答by Christian Moser
this is the simplest implementation I know to format file sizes:
这是我所知道的格式化文件大小的最简单的实现:
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]);
}
}
Whereas Size is the unformatted file size in bytes.
而 Size 是以字节为单位的未格式化文件大小。
Greetings Christian
问候基督徒
回答by Michel Tomassini
Here is an extension with more precision:
这是一个更精确的扩展:
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]);
}