.NETを使用して、人間が読める形式のファイルサイズ(バイト単位)を取得するにはどうすればよいですか?


回答:


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
whileループを使用する代わりに、Math.Logを使用して順序を決定できると思います。
Francois Botha 2010年

12
また、KBは1000バイトです。1024バイトはKiBです。
コンスタンティン

12
@ConstantinはOSに依存していますか?Windowsは引き続き1024バイトを1 KBとしてカウントし、1 MB = 1024 KBです。個人的にはウィンドウからKiB
Peter

4
@Petoj OSに依存せず、定義はOSに依存しません。ウィキペディアから: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
高速に実行されるように思えるので、このコードを好みますが、小数点以下の桁数が異なるように少し変更しました。数値が小さいほど小数点以下2桁が適切に表示されます(例:1.38MB)。数値が大きいほど小数点以下が少なくなります(例:246kまたは23.5KB):
Myke Black

320

ログを使用して問題を解決する...

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#でも、変換するのは簡単です。また、読みやすいように小数点第1位で四捨五入しました。

基本的には、Base 1024の小数点以下の桁数を決定し、1024 ^ decimalplacesで割ります。

そして、使用と出力のいくつかのサンプル:

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バイトのサイズに関するコメントがいくつかあったため、これら2つのケースを処理するように更新しました。


7
この答えは確かに短いコードですが、最適化されているわけではないことを警告します。@humbadsが投稿したメソッドをご覧ください。私は両方の方法でランダムに生成された10 000 000のファイルサイズを送信してマイクロテストを実行しました。これにより、彼の方法が約30%高速であるという数値が表示されます。しかしながら、私は彼の方法のいくつかのさらなるクリーニングを行いました(unnesecary割り当て&キャスティング)。さらに、私は(ファイルを比較しているときに)負のサイズでテストを実行しましたが、humbadsのメソッドがこのLogメソッドを完全に処理すると、例外がスローされます!
IvanL 2012

1
うん、負のサイズのMath.Abs​​を追加する必要があります。サイズが正確に0である場合、さらに、コードは、ケースを処理しない
dasheddot

Math.Abs​​、Math.Floor、Math.Log、整数への変換、Math.Round、Math.Pow、Math.Sign、加算、乗算、除算?これだけの数の数学は、プロセッサに大きなスパイクをもたらすだけではありませんでした。これはおそらく@humbadsコードより遅い
ジェイソン・ラガサ2013

失敗double.MaxValue(場所= 102)
BrunoLM

よく働く!(少なくとも私のWindows 7 Ultimateでは)Windowsの動作を模倣するには、Math.RoundをMath.Ceilingに置き換えます。再度、感謝します。私はこのソリューションが好きです。
H_He 2014

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);どこにも使用していないので削除してください。もう1つ、キャストはredaundatです
Royi Namir

キャストを削除してコメントを追加し、マイナス記号の問題を修正しました。
humbads 2015年

すばらしい答えです。よろしくお願いしますMath.Abs
kspearrin 2017年

1
(i <0?-i:i)は、Math.Abs​​よりも約15%高速です。100万回の呼び出しでは、Math.Abs​​は私のマシンでは0.5ミリ秒遅くなります-3.2ミリ秒vs 3.7ミリ秒。
humbads 2017

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
私は初心者かもしれませんが、あのアヒルを殺すためのピンボークのような巨大な大砲を使うのは大きな誤用です。
Bart

27
これはエクスプローラーが使用するものですか?もしそうなら、人々にあなたがエクスプローラーが示すものとあなたが示すファイルサイズを一致させるのに非常に便利です。
Andrew Backer

8
そして、車輪を再発明しないもの
マシューロック

11文字は一定の制限ではなく、少し低いのではないですか?つまり、他の言語では、バイトサイズの頭字語や他のフォーマットスタイルにさらに多くの文字を使用する場合があります。
Ray

1
@Bart初心者がこのことについて知恵を習得するには、少し時間がかかります。「小さな効率を忘れる必要があります。つまり、時間の約97%を言います。時期尚早の最適化がすべての悪の根源です
マシューロック

22

それをスキン化するもう1つの方法は、ループの種類がなく、負のサイズのサポートを使用します(ファイルサイズの差分などの意味があります)。

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

私は次の方法を使用するのが好きです(これはテラバイトまでサポートしますが、ほとんどの場合これで十分ですが、簡単に拡張できます)。

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 14

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」はそれぞれキロ、メガ、ギガ、テラ、ペタ、エクサ、ゼッタ、ヨッタ

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
あり、なぜ誰もが疑問に思うためにo(そのフランス語:KMGTPE後にbyteあるoctetフランス語)。その他の言語の場合oは、をb
Max 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)。
TcKs

いいえ、64ビットの符号付き整数は、2 ^ 70を表すZB ...を超えることはできません。
bobwienholt 2008

7
では、なぜYBを使用するのでしょうか。
コンフィギュレー

私はこの答えが一番好きですが、ここにいる全員が本当に非効率的なソリューションを導入しています。「サイズ=サイズ>> 10」シフトを使用する必要があります。除算よりも非常に高速です...そしてギリシャ語の指定子がもう1つあります。近い将来、配置可能なDLR関数に「長いサイズ」は必要なくなるためです。128ビットのベクトルCPUまたはZB以上を保持できる何かを使用することができます;)
RandomNickName42

4
メタルでのCコーディングの時代には、ビットシフトは除算よりも効率的でした。.NETでパフォーマンステストを行って、ビットシフトが本当に効率的かどうかを確認しましたか?少し前まで、私はxor-swapの状態を調べましたが、.NETでは一時変数を使用するよりも実際には遅いことがわかりました。
ピート

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)] public static extern long StrFormatKBSize(long qdw、[MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszBuf、int cchBuf ); この解決策を最初に支持するのは私が最初です。ホイールがすでに発明されているのに、なぜホイールを再発明するのですか?これはすべてのC#プログラマーの典型的なアプローチですが、残念ながらC#はC ++が到達するすべてのターゲットに到達するわけではありません。
TarmoPikaro 2016

そしてもう1つのバグ修正:Int64.MaxValueは9,223,372,036,854,775,807に達し、25以上のバッファーサイズを割り当てる必要があります-念のため、32に丸めました(上記のデモコードのように11ではありません)。
TarmoPikaro 2016

@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のソリューションのように。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 '30

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年のまわり始めました。1024バイトの用語はキビバイトです。カルチャに基づいてそれらを切り替えるコードは、誤った情報を生成しています。
スーパーベスト2012

1

価値のあるもののために、もう1つのアプローチ。上記の@humbads最適化ソリューションが好きだったので、原則をコピーしましたが、少し異なる方法で実装しました。

それが拡張メソッドである必要があるかどうかは議論の余地があると思いますが(すべてのlongが必ずしもバイトサイズである必要はないため)、私はそれらを気に入っています。

単位については、これまで「キビバイト」や「メビバイト」と言ったことはないと思います。進化した標準ではなく、そのような強制された標準には懐疑的ですが、長期的には混乱を避けるでしょう。 。

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拡張メソッドを使用して、人間が読めるサイズの文字列に変換します。このメソッドは、ここでStack Overflowに投稿された同じ質問のJavaソリューションのC#実装です

/// <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";
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.