Holger Stehle

Softwareentwicklung und -architektur

« Byte-Angaben formatieren

Mit gewisser Regelmäßigkeit kommt man als Softwareentwickler in die Verlegenheit, Dateigrößen statt in der Form 10110 Byte in der "üblichen" Schreibweise mit größeren Einheiten (in diesem Fall 9.87 KiB) darzustellen.

Zwar gäbe es hier wieder einmal die Möglichkeit per DllImport und beispielsweise StrFormatByteSizeW zu arbeiten, jedoch möchten wir in diesem Fall die Implementierung selbst vornehmen, um die Basis (Stichwort: Binärpräfixe) konsistent bestimmen zu können. Ich stelle dazu zwei mögliche Implementierungen vor:

public static class Helper {
    private static readonly IReadOnlyList<string> unitList = new string[] {
        "Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"
    };


    public static string FormatBytes(double bytes) {
        int index = 0;

        while(bytes >= 1024 && index < Helper.unitList.Count) {
            bytes /= 1024;
            index++;
        }

        return $"{Math.Round(bytes, 2).ToString("N2")} {Helper.unitList[index]}";
    }
}

Alternativ lässt sich das Problem jedoch auch ohne Schleife – und daher mathematisch etwas eleganter – durch ein ähnliches Vorgehen wie bei der Ermittlung der Anzahl der Ziffern einer Zahl mittels Logarithmen lösen:

public static string FormatBytes(double bytes) {
    int index = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
    bytes /= Math.Pow(1024, index);

    return $"{Math.Round(bytes, 2).ToString("N2")} {Helper.unitList[index]}";
}

Die Verwendung gestaltet sich nun in jedem Fall recht einfach:

string formattedBytes = Helper.FormatBytes(1024);         // 1,00 KiB
formattedBytes = Helper.FormatBytes(1024 * 1024);         // 1,00 MiB
formattedBytes = Helper.FormatBytes(1024 * 1024 * 1024);  // 1,00 GiB

Abschließend möchte ich auch noch kurz eine beispielhafte Implementierung zur Verwendung im Rahmen einer benutzerdefinierten Formatierung per String.Format skizzieren:

public class FileSizeFormatProvider : ICustomFormatter, IFormatProvider {
    public string Format(string format, object arg, IFormatProvider formatProvider) {
        return Helper.FormatBytes(Convert.ToDouble(arg));
    }

    public object GetFormat(Type formatType) {
        return this;
    }
}

Bitte beachtet jedoch, dass diese Implementierung noch nicht vollständig ist. Weiterführende Informationen zur korrekten Implementierung finden sich im MSDN per ICustomFormatter und IFormatProvider. Verwendung:

formattedBytes = String.Format(new FileSizeFormatProvider(), "{0}", (1024 * 1024));  // 1,00 MiB