如何使用.NET获得可读的文件大小(以字节缩写为单位)?


Answers:


353

这不是最有效的方法,但是如果您不熟悉日志数学,则更容易阅读,并且对于大多数情况来说应该足够快。

string[] sizes = { "B", "KB", "MB", "GB", "TB" };
double len = new FileInfo(filename).Length;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1) {
    order++;
    len = len/1024;
}

// Adjust the format string to your preferences. For example "{0:0.#}{1}" would
// show a single decimal place, and no space.
string result = String.Format("{0:0.##} {1}", len, sizes[order]);

12
我相信您可以使用Math.Log来确定顺序,而不是使用while循环。
弗朗索瓦·博塔

12
另外,KB是1000个字节。1024字节是KiB
君士坦丁

12
@Constantin取决于操作系统吗?Windows仍将1024字节计为1 KB,而1 MB = 1024 KB,我个人想将KiB扔到窗外,只是使用1024来计数所有东西?...
Peter

4
@Petoj它不依赖于操作系统,其定义与操作系统无关。来自维基百科:The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
ANeves

3
我喜欢此代码,因为它似乎运行得更快,但我对其进行了少许修改,以允许使用不同数量的小数位。较小的数字最好显示两位小数,例如1.38MB,而较大的数字则需要较少的小数,例如246k或23.5KB:
Myke Black

321

使用日志来解决问题...。

static String BytesToString(long byteCount)
{
    string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB
    if (byteCount == 0)
        return "0" + suf[0];
    long bytes = Math.Abs(byteCount);
    int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
    double num = Math.Round(bytes / Math.Pow(1024, place), 1);
    return (Math.Sign(byteCount) * num).ToString() + suf[place];
}

同样在c#中,但应该可以快速转换。另外,为了便于阅读,我还将四舍五入到小数点后一位。

基本上确定以1024为基数的小数位数,然后除以1024 ^小数位。

以及一些使用和输出示例:

Console.WriteLine(BytesToString(9223372036854775807));  //Results in 8EB
Console.WriteLine(BytesToString(0));                    //Results in 0B
Console.WriteLine(BytesToString(1024));                 //Results in 1KB
Console.WriteLine(BytesToString(2000000));              //Results in 1.9MB
Console.WriteLine(BytesToString(-9023372036854775807)); //Results in -7.8EB

编辑:指出我错过了math.floor,所以我将其合并。(Convert.ToInt32使用四舍五入,而不是截断,这就是为什么需要Floor的原因。)感谢您的接见。

Edit2:关于负数大小和0字节大小,有几条评论,所以我更新为处理这两种情况。


7
我要警告的是,尽管这个答案确实是一小段代码,但它并不是最优化的。我希望您看看@humbads发布的方法。我进行了微测试,通过这两种方法发送了1亿个随机生成的文件大小,这表明他的方法快了30%。但是,我对他的方法做了进一步的清理(非作业和演员)。此外,我使用负数大小(当您比较文件时)进行了测试,而humbads方法完美地处理了此Log方法将引发异常!
IvanL

1
是的,您应该为负数添加Math.Abs​​。此外,如果大小正好为0的代码不处理的情况下
dasheddot

Math.Abs​​,Math.Floor,Math.Log,转换为整数,Math.Round,Math.Pow,Math.Sign,加法,乘法,除法?这不是大量的数学运算,只是在处理器上产生了巨大的冲击。这可能比@humbads代码慢
Jayson Ragasa 2013年

失败double.MaxValue(位置= 102)
BrunoLM 2013年

很棒!若要模拟Windows的工作方式(至少在Windows 7 Ultimate上如此),请用Math.Ceiling替换Math.Round。再次感谢。我喜欢这个解决方案。
H_He

101

在此处发布了所需功能的经过测试且经过优化的版本:

C#可读文件大小-优化功能

源代码:

// Returns the human-readable file size for an arbitrary, 64-bit file size 
// The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB"
public string GetBytesReadable(long i)
{
    // Get absolute value
    long absolute_i = (i < 0 ? -i : i);
    // Determine the suffix and readable value
    string suffix;
    double readable;
    if (absolute_i >= 0x1000000000000000) // Exabyte
    {
        suffix = "EB";
        readable = (i >> 50);
    }
    else if (absolute_i >= 0x4000000000000) // Petabyte
    {
        suffix = "PB";
        readable = (i >> 40);
    }
    else if (absolute_i >= 0x10000000000) // Terabyte
    {
        suffix = "TB";
        readable = (i >> 30);
    }
    else if (absolute_i >= 0x40000000) // Gigabyte
    {
        suffix = "GB";
        readable = (i >> 20);
    }
    else if (absolute_i >= 0x100000) // Megabyte
    {
        suffix = "MB";
        readable = (i >> 10);
    }
    else if (absolute_i >= 0x400) // Kilobyte
    {
        suffix = "KB";
        readable = i;
    }
    else
    {
        return i.ToString("0 B"); // Byte
    }
    // Divide by 1024 to get fractional value
    readable = (readable / 1024);
    // Return formatted number with suffix
    return readable.ToString("0.### ") + suffix;
}

1
+1!更简单直接!使处理器轻松,快速地进行数学运算!
Jayson Ragasa

仅供参考,您不会在double readable = (i < 0 ? -i : i);任何地方使用该值,因此请将其删除。还有一件事,演员表被裁掉了
Royi Namir

我删除了演员表,添加了评论,并修复了带有负号的问题。
humbads 2015年

好答案。谢谢,为什么不只是使用Math.Abs
kspearrin

1
(i <0?-i:i)比Math.Abs​​快15%。对于一百万个电话,我的计算机上Math.Abs​​慢了0.5毫秒-3.2毫秒对3.7毫秒。
humbads '17

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


/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="filelength">The numeric value to be converted.</param>
/// <returns>the converted string</returns>
public static string StrFormatByteSize (long filesize) {
     StringBuilder sb = new StringBuilder( 11 );
     StrFormatByteSize( filesize, sb, sb.Capacity );
     return sb.ToString();
}

来自:http : //www.pinvoke.net/default.aspx/shlwapi/StrFormatByteSize.html


36
我可能是一个菜鸟,但是使用像pinvoke这样的巨型大炮杀死那只鸭子是一个很大的误用。
巴特

27
这是资源管理器使用的吗?如果是这样,那么对于让人们匹配您向他们显示的文件大小与资源管理器显示的文件大小非常有用。
Andrew Backer

8
还有一个不会彻底改变方向盘的东西
马修·洛克

11个字符不是一个恒定的限制吗?我的意思是,其他语言可能会在字节大小首字母缩写或其他格式样式中使用更多字符。

1
@Bart菜鸟要花点时间来学习其中的智慧:“我们应该忘记效率低下,大约97%的时间说:过早的优化是万恶之源” ubiquity.acm.org/article.cfm? id = 1513451
马修·洛克

22

另一种皮肤外观的方法,没有任何形式的循环,并且具有负大小支持(对于文件大小增量这样的事情很有意义):

public static class Format
{
    static string[] sizeSuffixes = {
        "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };

    public static string ByteSize(long size)
    {
        Debug.Assert(sizeSuffixes.Length > 0);

        const string formatTemplate = "{0}{1:0.#} {2}";

        if (size == 0)
        {
            return string.Format(formatTemplate, null, 0, sizeSuffixes[0]);
        }

        var absSize = Math.Abs((double)size);
        var fpPower = Math.Log(absSize, 1000);
        var intPower = (int)fpPower;
        var iUnit = intPower >= sizeSuffixes.Length
            ? sizeSuffixes.Length - 1
            : intPower;
        var normSize = absSize / Math.Pow(1000, iUnit);

        return string.Format(
            formatTemplate,
            size < 0 ? "-" : null, normSize, sizeSuffixes[iUnit]);
    }
}

这是测试套件:

[TestFixture] public class ByteSize
{
    [TestCase(0, Result="0 B")]
    [TestCase(1, Result = "1 B")]
    [TestCase(1000, Result = "1 KB")]
    [TestCase(1500000, Result = "1.5 MB")]
    [TestCase(-1000, Result = "-1 KB")]
    [TestCase(int.MaxValue, Result = "2.1 GB")]
    [TestCase(int.MinValue, Result = "-2.1 GB")]
    [TestCase(long.MaxValue, Result = "9.2 EB")]
    [TestCase(long.MinValue, Result = "-9.2 EB")]
    public string Format_byte_size(long size)
    {
        return Format.ByteSize(size);
    }
}

19

检出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");

5
那是你自己的图书馆,不是吗?
拉森纳2014年

10
在这样的便捷库中不要感到羞耻。:-)
拉森纳2014年

13

我喜欢使用以下方法(它支持高达TB的数据,在大多数情况下就足够了,但可以轻松扩展):

private string GetSizeString(long length)
{
    long B = 0, KB = 1024, MB = KB * 1024, GB = MB * 1024, TB = GB * 1024;
    double size = length;
    string suffix = nameof(B);

    if (length >= TB) {
        size = Math.Round((double)length / TB, 2);
        suffix = nameof(TB);
    }
    else if (length >= GB) {
        size = Math.Round((double)length / GB, 2);
        suffix = nameof(GB);
    }
    else if (length >= MB) {
        size = Math.Round((double)length / MB, 2);
        suffix = nameof(MB);
    }
    else if (length >= KB) {
        size = Math.Round((double)length / KB, 2);
        suffix = nameof(KB);
    }

    return $"{size} {suffix}";
}

请记住,这是为C#6.0(2015)编写的,因此可能需要对早期版本进行一些编辑。


11
int size = new FileInfo( filePath ).Length / 1024;
string humanKBSize = string.Format( "{0} KB", size );
string humanMBSize = string.Format( "{0} MB", size / 1024 );
string humanGBSize = string.Format( "{0} GB", size / 1024 / 1024 );

好答案。文件大小太小时应该出现问题,在这种情况下/ 1024返回0。您可以使用小数类型和调用等Math.Ceiling
nawfal 2014年

10

这是一个简洁的答案,可以自动确定单位。

public static string ToBytesCount(this long bytes)
{
    int unit = 1024;
    string unitStr = "b";
    if (bytes < unit) return string.Format("{0} {1}", bytes, unitStr);
    else unitStr = unitStr.ToUpper();
    int exp = (int)(Math.Log(bytes) / Math.Log(unit));
    return string.Format("{0:##.##} {1}{2}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], unitStr);
}

“ b”代表位,“ B”代表字节,“ KMGTPEZY”分别代表公斤,兆,千兆,tera,peta,exa,zetta和yotta

可以扩展它以考虑到ISO / IEC80000

public static string ToBytesCount(this long bytes, bool isISO = true)
{
    int unit = 1024;
    string unitStr = "b";
    if (!isISO) unit = 1000;
    if (bytes < unit) return string.Format("{0} {1}", bytes, unitStr);
    else unitStr = unitStr.ToUpper();
    if (isISO) unitStr = "i" + unitStr;
    int exp = (int)(Math.Log(bytes) / Math.Log(unit));
    return string.Format("{0:##.##} {1}{2}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], unitStr);
}

1
大家想知道为什么有一个是oKMGTPE后:其法国(byteoctet法语)。对于任何其他语言只需更换ob
马克斯R.

7
string[] suffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
int s = 0;
long size = fileInfo.Length;

while (size >= 1024)
{
    s++;
    size /= 1024;
}

string humanReadable = String.Format("{0} {1}", size, suffixes[s]);

您应该检查:while(size> = 1024 && s <suffixes.Length)。
TcK

不,...一个64位带符号整数不能超过ZB ...,它表示2 ^ 70。
bobwienholt's

7
那么为什么要放入YB?
配置器

我本人最喜欢这个答案,但实际上每个人都提出了效率低下的解决方案,您应该使用“大小=大小>> 10”移位要快于除法运算...而且我认为拥有这是一个额外的希腊说明符,因为在不久的将来,可能的DLR函数将不需要“长尺寸..”您可以使用128位向量cpu或可以容纳ZB和更大尺寸的东西;)
RandomNickName42

4
在金属上进行C编码的日子里,位移位比除法更有效。您是否已在.NET中进行了性能测试,以查看位偏移是否确实更有效?不久之前,我查看了xor交换的状态,发现它在.NET中实际上比使用temp变量要慢。
皮特2010年

7

如果您要匹配Windows资源管理器的详细视图中显示的大小,这是您想要的代码:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern long StrFormatKBSize(
    long qdw,
    [MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszBuf,
    int cchBuf);

public static string BytesToString(long byteCount)
{
    var sb = new StringBuilder(32);
    StrFormatKBSize(byteCount, sb, sb.Capacity);
    return sb.ToString();
}

这不仅将完全匹配资源管理器,还将提供为您翻译的字符串并匹配Windows版本中的差异(例如,在Win10中,K = 1000与以前的版本K = 1024)。


此代码无法编译,您需要指定函数来源的dll。因此整个函数原型听起来像这样:[DllImport(“ shlwapi.dll”,CharSet = CharSet.Auto,SetLastError = true)] ); 让我成为第一个支持此解决方案的人。如果已经发明了车轮,为什么还要重新发明车轮呢?这是所有C#程序员的典型方法,但是不幸的是C#并没有达到C ++可以达到的所有目标。
TarmoPikaro '16

还有一个错误修正:Int64.MaxValue达到9,223,372,036,854,775,807,它需要分配25+的缓冲区大小-我已经将其舍入为32,以防万一(以防万一(不是上面演示代码中的11)。
TarmoPikaro '16

谢谢@TarmoPikaro。从工作代码中复制时,我错过了DllImport。还根据您的建议增加了缓冲区大小。接得好!
Metalogic

令人印象深刻的方法
tbhaxor

这仅显示KB单位。想法是根据值显示最大的单位。
jstuardo

5

所有溶液的混合物:-)

    /// <summary>
    /// Converts a numeric value into a string that represents the number expressed as a size value in bytes,
    /// kilobytes, megabytes, or gigabytes, depending on the size.
    /// </summary>
    /// <param name="fileSize">The numeric value to be converted.</param>
    /// <returns>The converted string.</returns>
    public static string FormatByteSize(double fileSize)
    {
        FileSizeUnit unit = FileSizeUnit.B;
        while (fileSize >= 1024 && unit < FileSizeUnit.YB)
        {
            fileSize = fileSize / 1024;
            unit++;
        }
        return string.Format("{0:0.##} {1}", fileSize, unit);
    }

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

public enum FileSizeUnit : byte
{
    B,
    KB,
    MB,
    GB,
    TB,
    PB,
    EB,
    ZB,
    YB
}


3

就像@ NET3的解决方案一样。使用shift代替除法来测试的范围bytes,因为除法会花费更多的CPU成本。

private static readonly string[] UNITS = new string[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" };

public static string FormatSize(ulong bytes)
{
    int c = 0;
    for (c = 0; c < UNITS.Length; c++)
    {
        ulong m = (ulong)1 << ((c + 1) * 10);
        if (bytes < m)
            break;
    }

    double n = bytes / (double)((ulong)1 << (c * 10));
    return string.Format("{0:0.##} {1}", n, UNITS[c]);
}


2

如何递归:

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

然后,您将其称为:

return ReturnSize(size, string.Empty);

不错,但是它会吃掉CPU
kamalpreet

1

我的2美分:

  • 千字节的前缀为kB(小写K)
  • 由于这些功能仅用于演示目的,因此应提供一种文化,例如: string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
  • 根据上下文,一千字节可以为1000或1024字节。MB,GB等也是如此。

3
千字节表示1000字节(wolframalpha.com/input/?i=kilobyte),它不取决于上下文。它在历史上取决于背景下,作为维基百科说,这是法律上在1998年发生变化,事实上的变化在2005年左右开始的时候TB的硬盘驱动器把它带到了公众的注意。1024字节的术语是kibibyte。根据区域性切换代码的​​代码会产生错误的信息。
最佳

1

值得一提的另一种方法。我喜欢上面引用的@humbads优化解决方案,因此已经复制了该原理,但是我在实现上有所不同。

我想是否应该使用扩展方法还是有争议的(因为并非所有long都一定是字节大小),但是我喜欢它们,并且在我下次需要它时可以在该方法中找到它!

关于单位,我认为我一生中从未说过“ Kibibyte”或“ Mebibyte”,虽然我对这种强制性标准而不是演进标准持怀疑态度,但我想从长远来看可以避免混淆。

public static class LongExtensions
{
    private static readonly long[] numberOfBytesInUnit;
    private static readonly Func<long, string>[] bytesToUnitConverters;

    static LongExtensions()
    {
        numberOfBytesInUnit = new long[6]    
        {
            1L << 10,    // Bytes in a Kibibyte
            1L << 20,    // Bytes in a Mebibyte
            1L << 30,    // Bytes in a Gibibyte
            1L << 40,    // Bytes in a Tebibyte
            1L << 50,    // Bytes in a Pebibyte
            1L << 60     // Bytes in a Exbibyte
        };

        // Shift the long (integer) down to 1024 times its number of units, convert to a double (real number), 
        // then divide to get the final number of units (units will be in the range 1 to 1023.999)
        Func<long, int, string> FormatAsProportionOfUnit = (bytes, shift) => (((double)(bytes >> shift)) / 1024).ToString("0.###");

        bytesToUnitConverters = new Func<long,string>[7]
        {
            bytes => bytes.ToString() + " B",
            bytes => FormatAsProportionOfUnit(bytes, 0) + " KiB",
            bytes => FormatAsProportionOfUnit(bytes, 10) + " MiB",
            bytes => FormatAsProportionOfUnit(bytes, 20) + " GiB",
            bytes => FormatAsProportionOfUnit(bytes, 30) + " TiB",
            bytes => FormatAsProportionOfUnit(bytes, 40) + " PiB",
            bytes => FormatAsProportionOfUnit(bytes, 50) + " EiB",
        };
    }

    public static string ToReadableByteSizeString(this long bytes)
    {
        if (bytes < 0)
            return "-" + Math.Abs(bytes).ToReadableByteSizeString();

        int counter = 0;
        while (counter < numberOfBytesInUnit.Length)
        {
            if (bytes < numberOfBytesInUnit[counter])
                return bytesToUnitConverters[counter](bytes);
            counter++;
        }
        return bytesToUnitConverters[counter](bytes);
    }
}

0

我使用下面的Long扩展方法将其转换为人类可读的大小字符串。此方法是此问题的Java解决方案的C#实现,发布在此处的 Stack Overflow上。

/// <summary>
/// Convert a byte count into a human readable size string.
/// </summary>
/// <param name="bytes">The byte count.</param>
/// <param name="si">Whether or not to use SI units.</param>
/// <returns>A human readable size string.</returns>
public static string ToHumanReadableByteCount(
    this long bytes
    , bool si
)
{
    var unit = si
        ? 1000
        : 1024;

    if (bytes < unit)
    {
        return $"{bytes} B";
    }

    var exp = (int) (Math.Log(bytes) / Math.Log(unit));

    return $"{bytes / Math.Pow(unit, exp):F2} " +
           $"{(si ? "kMGTPE" : "KMGTPE")[exp - 1] + (si ? string.Empty : "i")}B";
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.