72 votes

C #: fournisseur de format de fichier

Existe-t-il un moyen simple de créer une classe utilisant IFormatProvider qui écrit une taille de fichier conviviale?

 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...
}
 

Il devrait en résulter des chaînes au format « 2,5 Mo », « 3,9 Go », « 670 octets », etc.

111voto

Eduardo Campañó Points 4801

J'utilise celui-ci, je l'obtenir à partir du 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();
    }

}

un exemple d'utilisation serait:

Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 100));
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 10000));

Les crédits pour les http://flimflan.com/blog/FileSizeFormatProvider.aspx

Il y a un problème avec ToString(), il s'attend à un NumberFormatInfo type qui implémente IFormatProvider mais la classe NumberFormatInfo est scellé :(

Si vous êtes à l'aide de C# 3.0, vous pouvez utiliser une méthode d'extension pour obtenir le résultat que vous souhaitez:

public static class ExtensionMethods
{
    public static string ToFileSize(this long l)
    {
        return String.Format(new FileSizeFormatProvider(), "{0:fs}", l);
    }
}

Vous pouvez l'utiliser comme ça.

long l = 100000000;
Console.WriteLine(l.ToFileSize());

Espérons que cette aide.

46voto

Shaun Austin Points 2512

OK je ne vais pas à l'envelopper comme un Format de fournisseur, mais plutôt que de réinventer la roue, il y a un appel Win32 api pour formater une taille de chaîne en fonction de sur fourni octets que j'ai utilisé à de nombreuses reprises dans diverses applications.

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

Donc, j'imagine que vous devriez être en mesure de mettre sur pied un fournisseur en utilisant ce que la base de code de conversion.

Voici un lien pour le MSDN spec pour StrFormatByteSize.

27voto

mindplay.dk Points 2555

Je me rends compte maintenant que vous étiez en train de demander à quelque chose qui pourrait fonctionner avec de la Ficelle.Format() - je suppose que je devrais avoir lu la question deux fois avant de poster ;-)

Je n'aime pas la solution où vous avez à transmettre explicitement dans un format fournisseur de tous les temps - de ce que j'ai pu recueillir à partir de cet article, la meilleure façon d'aborder cette, est de mettre en place un Fichier de type, la mise en œuvre de la IFormattable interface.

Je suis allé de l'avant et mis en place une structure qui prend en charge cette interface, et qui peut être coulé à partir d'un entier. Dans mon propre fichier Api, je vais avoir mon .La taille du fichier de propriétés retour d'une Taille de l'instance.

Voici le 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];
    }
}

Et un Test Unitaire simple qui montre comment cela fonctionne:

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

Comme vous pouvez le voir, la Taille du fichier de type peut maintenant être correctement mis en forme, et il est également possible de spécifier le nombre de décimales, ainsi que l'application régulière de mise en forme numérique si nécessaire.

Je suppose que vous pourriez prendre ce beaucoup plus loin, par exemple en autorisant explicite de sélection de format, par exemple "{0:KO}" pour forcer le formatage en kilo-octets. Mais je vais vous laisser à présent.

Je suis aussi en laissant mon post initial ci-dessous pour les deux préférez ne pas utiliser la mise en forme de l'API...


100 façons à la peau d'un chat, mais voici mon approche - ajout d'une méthode d'extension pour le type 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];
    }
}

Avec cette extension dans votre assemblée, pour le format d'un fichier, il suffit d'utiliser un énoncé comme (1234567).ToBytes()

La suite MbUnit test clarifie précisément à quoi ressemble la sortie:

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

Et vous pouvez facilement changer les unités et la précision de ce qui convient à vos besoins :-)

11voto

Christian Moser Points 204

c'est l'implémentation la plus simple je sais que pour le format de fichier de tailles:

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

Alors que la Taille est non formaté la taille du fichier en octets.

Salutations Christian

http://www.wpftutorial.net

5voto

ariso Points 455

Mon code... merci pour 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();
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X