Existe-t-il un moyen facile de créer une classe qui utilise 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 formatées comme " 2,5 Mo ", " 3,9 Go "," 670 octets "et ainsi de suite.
J'utilise celui-ci, je l'obtiens sur le 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));
Crédits pour http://flimflan.com/blog/FileSizeFormatProvider.aspx
Il y a un problème avec ToString (), il attend un type NumberFormatInfo qui implémente IFormatProvider mais la classe NumberFormatInfo est scellée :(
Si vous utilisez C # 3.0, vous pouvez utiliser une méthode d'extension pour obtenir le résultat souhaité:
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());
J'espère que cela t'aides.
OK, je ne vais pas conclure en tant que fournisseur de format, mais plutôt que de réinventer la roue, il y a un appel API Win32 pour formater une chaîne de taille en fonction des octets fournis que j'ai utilisé plusieurs fois dans diverses applications.
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize( long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize );
J'imagine donc que vous devriez pouvoir mettre en place un fournisseur utilisant cela comme code de conversion principal.
Voici un lien vers la spécification MSDN pour StrFormatByteSize.
Je me rends compte maintenant que vous demandiez réellement quelque chose qui fonctionnerait avec String.Format () - Je suppose que j'aurais dû lire la question deux fois avant de poster ;-)
Je n'aime pas la solution où vous devez passer explicitement dans un fournisseur de format à chaque fois - d'après ce que j'ai pu recueillir de cet article , la meilleure façon d'aborder cela, est d'implémenter un type FileSize, implémenter l'interface IFormattable.
Je suis allé de l'avant et j'ai implémenté une structure qui prend en charge cette interface et qui peut être convertie à partir d'un entier. Dans mes propres API liées aux fichiers, mes propriétés .FileSize renverront une instance FileSize.
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, le type FileSize peut désormais être formaté correctement, et il est également possible de spécifier le nombre de décimales, ainsi que d'appliquer une mise en forme numérique régulière si nécessaire.
Je suppose que vous pourriez aller beaucoup plus loin, par exemple en permettant une sélection de format explicite, par exemple "{0: KB}" pour forcer le formatage en kilo-octets. Mais je vais en rester là.
Je laisse également mon post initial ci-dessous car ces deux-là préfèrent ne pas utiliser l'API de formatage ...
100 façons de dépecer un chat, mais voici mon approche - ajouter une méthode d'extension au 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 assembly, pour formater une taille de fichier, utilisez simplement une instruction comme (1234567). ToBytes ()
Le test MbUnit suivant 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 en fonction de vos besoins :-)
c'est l'implémentation la plus simple que je connaisse pour formater les tailles de fichiers:
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 Size est la taille du fichier non formaté en octets.
Salutations chrétiennes
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();
}
car le déplacement est une opération très bon marché
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";
}
}
Une approche par domaine peut être trouvée ici: https://github.com/Corniel/Qowaiv/blob/master/src/Qowaiv/IO/StreamSize.cs
La structure StreamSize est une représentation d'une taille de flux, vous permet à la fois de formater automatiquement avec l'extension appropriée, mais également de spécifier que vous la souhaitez en Ko/Mo ou autre. Cela présente de nombreux avantages, non seulement parce que vous obtenez le formatage de la boîte, mais cela vous aide également à créer de meilleurs modèles, car il est évident que la propriété ou le résultat d'une méthode représente une taille de flux. Il a également une extension sur la taille du fichier: GetStreamSize (ce fichier FileInfo).
Il existe un package NuGet, vous pouvez donc simplement l'utiliser: https://www.nuget.org/packages/Qowaiv
Voici une extension avec plus de précision:
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]);
}
J'avais besoin d'une version qui peut être localisée pour différentes cultures (séparateur décimal, traduction "octet") et prise en charge de tous les possibles préfixes binaires (jusqu'à Exa). Voici un exemple qui montre comment l'utiliser:
// 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)));
Et voici le 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);
}
J'ai pris la réponse d'Eduardo et l'ai combinée avec un exemple similaire d'ailleurs pour fournir des options supplémentaires pour le formatage.
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();
}
}
Si vous changez:
if (String.IsNullOrEmpty(precision))
{
precision = "2";
}
dans
if (String.IsNullOrEmpty(precision))
{
if (size < 10)
{
precision = "2";
}
else if (size < 100)
{
precision = "1";
}
else
{
precision = "0";
}
}
les résultats sans spécificateur de précision supplémentaire (donc juste 0: fs au lieu de 0: fs3) commenceront à imiter StrFormatByteSize () de Win32 en ajustant la précision à la taille.