Je me demande simplement si .NET fournit un moyen propre de le faire:
int64 x = 1000000;
string y = null;
if (x / 1024 == 0) {
y = x + " bytes";
}
else if (x / (1024 * 1024) == 0) {
y = string.Format("{0:n1} KB", x / 1024f);
}
etc...
Je me demande simplement si .NET fournit un moyen propre de le faire:
int64 x = 1000000;
string y = null;
if (x / 1024 == 0) {
y = x + " bytes";
}
else if (x / (1024 * 1024) == 0) {
y = string.Format("{0:n1} KB", x / 1024f);
}
etc...
Réponses:
Voici une façon assez concise de procéder:
static readonly string[] SizeSuffixes =
{ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(Int64 value, int decimalPlaces = 1)
{
if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
if (value < 0) { return "-" + SizeSuffix(-value); }
if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
// mag is 0 for bytes, 1 for KB, 2, for MB, etc.
int mag = (int)Math.Log(value, 1024);
// 1L << (mag * 10) == 2 ^ (10 * mag)
// [i.e. the number of bytes in the unit corresponding to mag]
decimal adjustedSize = (decimal)value / (1L << (mag * 10));
// make adjustment when the value is large enough that
// it would round up to 1000 or more
if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
{
mag += 1;
adjustedSize /= 1024;
}
return string.Format("{0:n" + decimalPlaces + "} {1}",
adjustedSize,
SizeSuffixes[mag]);
}
Et voici l'implémentation originale que j'ai suggérée, qui peut être légèrement plus lente, mais un peu plus facile à suivre:
static readonly string[] SizeSuffixes =
{ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(Int64 value, int decimalPlaces = 1)
{
if (value < 0) { return "-" + SizeSuffix(-value); }
int i = 0;
decimal dValue = (decimal)value;
while (Math.Round(dValue, decimalPlaces) >= 1000)
{
dValue /= 1024;
i++;
}
return string.Format("{0:n" + decimalPlaces + "} {1}", dValue, SizeSuffixes[i]);
}
Console.WriteLine(SizeSuffix(100005000L));
Une chose à garder à l'esprit - en notation SI, "kilo" utilise généralement un k minuscule tandis que toutes les unités plus grandes utilisent une lettre majuscule. Windows utilise KB, MB, GB, donc j'ai utilisé KB ci-dessus, mais vous pouvez envisager Ko à la place.
if (value == 0) { return "0"; }
vérification à l'intérieur de la fonction.
La caisse ByteSize bibliothèque. C'est leSystem.TimeSpan
for bytes!
Il gère la conversion et le formatage pour vous.
var maxFileSize = ByteSize.FromKiloBytes(10);
maxFileSize.Bytes;
maxFileSize.MegaBytes;
maxFileSize.GigaBytes;
Il effectue également la représentation et l'analyse de chaînes.
// 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");
Puisque tout le monde publie leurs méthodes, je me suis dit que je publierais la méthode d'extension que j'utilise habituellement pour cela:
EDIT: ajout de variantes int / long ... et correction d'une typo copypasta ...
public static class Ext
{
private const long OneKb = 1024;
private const long OneMb = OneKb * 1024;
private const long OneGb = OneMb * 1024;
private const long OneTb = OneGb * 1024;
public static string ToPrettySize(this int value, int decimalPlaces = 0)
{
return ((long)value).ToPrettySize(decimalPlaces);
}
public static string ToPrettySize(this long value, int decimalPlaces = 0)
{
var asTb = Math.Round((double)value / OneTb, decimalPlaces);
var asGb = Math.Round((double)value / OneGb, decimalPlaces);
var asMb = Math.Round((double)value / OneMb, decimalPlaces);
var asKb = Math.Round((double)value / OneKb, decimalPlaces);
string chosenValue = asTb > 1 ? string.Format("{0}Tb",asTb)
: asGb > 1 ? string.Format("{0}Gb",asGb)
: asMb > 1 ? string.Format("{0}Mb",asMb)
: asKb > 1 ? string.Format("{0}Kb",asKb)
: string.Format("{0}B", Math.Round((double)value, decimalPlaces));
return chosenValue;
}
}
Je le résoudrais en utilisant Extension methods
, Math.Pow
fonction et Enums
:
public static class MyExtension
{
public enum SizeUnits
{
Byte, KB, MB, GB, TB, PB, EB, ZB, YB
}
public static string ToSize(this Int64 value, SizeUnits unit)
{
return (value / (double)Math.Pow(1024, (Int64)unit)).ToString("0.00");
}
}
et utilisez-le comme:
string h = x.ToSize(MyExtension.SizeUnits.KB);
La version courte de la réponse la plus votée a des problèmes avec les valeurs TB.
Je l'ai ajusté de manière appropriée pour gérer également les valeurs tb et toujours sans boucle et j'ai également ajouté une petite vérification d'erreur pour les valeurs négatives. Voici ma solution:
static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(long value, int decimalPlaces = 0)
{
if (value < 0)
{
throw new ArgumentException("Bytes should not be negative", "value");
}
var mag = (int)Math.Max(0, Math.Log(value, 1024));
var adjustedSize = Math.Round(value / Math.Pow(1024, mag), decimalPlaces);
return String.Format("{0} {1}", adjustedSize, SizeSuffixes[mag]);
}
Non. Surtout parce qu'il s'agit d'un besoin plutôt de niche et qu'il y a trop de variations possibles. (S'agit-il de "Ko", "Kb" ou "Ko"? Un mégaoctet est-il de 1024 * 1024 octets ou 1024 * 1000 octets? - oui, certains endroits l'utilisent!)
Voici une option plus facile à étendre que la vôtre, mais non, aucune n'est intégrée à la bibliothèque elle-même.
private static List<string> suffixes = new List<string> { " B", " KB", " MB", " GB", " TB", " PB" };
public static string Foo(int number)
{
for (int i = 0; i < suffixes.Count; i++)
{
int temp = number / (int)Math.Pow(1024, i + 1);
if (temp == 0)
return (number / (int)Math.Pow(1024, i)) + suffixes[i];
}
return number.ToString();
}
private string GetFileSize(double byteCount)
{
string size = "0 Bytes";
if (byteCount >= 1073741824.0)
size = String.Format("{0:##.##}", byteCount / 1073741824.0) + " GB";
else if (byteCount >= 1048576.0)
size = String.Format("{0:##.##}", byteCount / 1048576.0) + " MB";
else if (byteCount >= 1024.0)
size = String.Format("{0:##.##}", byteCount / 1024.0) + " KB";
else if (byteCount > 0 && byteCount < 1024.0)
size = byteCount.ToString() + " Bytes";
return size;
}
private void btnBrowse_Click(object sender, EventArgs e)
{
if (openFile1.ShowDialog() == DialogResult.OK)
{
FileInfo thisFile = new FileInfo(openFile1.FileName);
string info = "";
info += "File: " + Path.GetFileName(openFile1.FileName);
info += Environment.NewLine;
info += "File Size: " + GetFileSize((int)thisFile.Length);
label1.Text = info;
}
}
C'est une façon de le faire également (le numéro 1073741824.0 est de 1024 * 1024 * 1024 aka GB)
La réponse de @ Servy était belle et succincte. Je pense que cela peut être encore plus simple?
private static string[] suffixes = new [] { " B", " KB", " MB", " GB", " TB", " PB" };
public static string ToSize(double number, int precision = 2)
{
// unit's number of bytes
const double unit = 1024;
// suffix counter
int i = 0;
// as long as we're bigger than a unit, keep going
while(number > unit)
{
number /= unit;
i++;
}
// apply precision and current suffix
return Math.Round(number, precision) + suffixes[i];
}
Basé sur la solution élégante de NeverHopeless:
private static readonly KeyValuePair<long, string>[] Thresholds =
{
// new KeyValuePair<long, string>(0, " Bytes"), // Don't devide by Zero!
new KeyValuePair<long, string>(1, " Byte"),
new KeyValuePair<long, string>(2, " Bytes"),
new KeyValuePair<long, string>(1024, " KB"),
new KeyValuePair<long, string>(1048576, " MB"), // Note: 1024 ^ 2 = 1026 (xor operator)
new KeyValuePair<long, string>(1073741824, " GB"),
new KeyValuePair<long, string>(1099511627776, " TB"),
new KeyValuePair<long, string>(1125899906842620, " PB"),
new KeyValuePair<long, string>(1152921504606850000, " EB"),
// These don't fit into a int64
// new KeyValuePair<long, string>(1180591620717410000000, " ZB"),
// new KeyValuePair<long, string>(1208925819614630000000000, " YB")
};
/// <summary>
/// Returns x Bytes, kB, Mb, etc...
/// </summary>
public static string ToByteSize(this long value)
{
if (value == 0) return "0 Bytes"; // zero is plural
for (int t = Thresholds.Length - 1; t > 0; t--)
if (value >= Thresholds[t].Key) return ((double)value / Thresholds[t].Key).ToString("0.00") + Thresholds[t].Value;
return "-" + ToByteSize(-value); // negative bytes (common case optimised to the end of this routine)
}
Peut-être qu'il y a des commentaires excessifs, mais j'ai tendance à les laisser pour m'empêcher de faire les mêmes erreurs lors de futures visites ...
Non.
Mais vous pouvez implémenter comme ceci;
static double ConvertBytesToMegabytes(long bytes)
{
return (bytes / 1024f) / 1024f;
}
static double ConvertKilobytesToMegabytes(long kilobytes)
{
return kilobytes / 1024f;
}
Consultez également Comment convertir correctement la taille des fichiers en octets en méga ou gigaoctets?
J'ai combiné certaines des réponses ici en deux méthodes qui fonctionnent très bien. La deuxième méthode ci-dessous convertira une chaîne d'octets (comme 1,5,1 Go) en octets (comme 1621350140) en tant que valeur de type long. J'espère que cela sera utile à ceux qui recherchent une solution pour convertir des octets en une chaîne et les remettre en octets.
public static string BytesAsString(float bytes)
{
string[] suffix = { "B", "KB", "MB", "GB", "TB" };
int i;
double doubleBytes = 0;
for (i = 0; (int)(bytes / 1024) > 0; i++, bytes /= 1024)
{
doubleBytes = bytes / 1024.0;
}
return string.Format("{0:0.00} {1}", doubleBytes, suffix[i]);
}
public static long StringAsBytes(string bytesString)
{
if (string.IsNullOrEmpty(bytesString))
{
return 0;
}
const long OneKb = 1024;
const long OneMb = OneKb * 1024;
const long OneGb = OneMb * 1024;
const long OneTb = OneGb * 1024;
double returnValue;
string suffix = string.Empty;
if (bytesString.IndexOf(" ") > 0)
{
returnValue = float.Parse(bytesString.Substring(0, bytesString.IndexOf(" ")));
suffix = bytesString.Substring(bytesString.IndexOf(" ") + 1).ToUpperInvariant();
}
else
{
returnValue = float.Parse(bytesString.Substring(0, bytesString.Length - 2));
suffix = bytesString.ToUpperInvariant().Substring(bytesString.Length - 2);
}
switch (suffix)
{
case "KB":
{
returnValue *= OneKb;
break;
}
case "MB":
{
returnValue *= OneMb;
break;
}
case "GB":
{
returnValue *= OneGb;
break;
}
case "TB":
{
returnValue *= OneTb;
break;
}
default:
{
break;
}
}
return Convert.ToInt64(returnValue);
}
float.Parse
pour double
?
Je sais que c'est déjà un vieux fil. mais peut-être que quelqu'un cherchera une solution. Et voici ce que j'utilise et le moyen le plus simple
public static string FormatFileSize(long bytes)
{
var unit = 1024;
if (bytes < unit)
{
return $"{bytes} B";
}
var exp = (int)(Math.Log(bytes) / Math.Log(unit));
return $"{bytes / Math.Pow(unit, exp):F2} " +
$"{("KMGTPE")[exp - 1]}B";
}
Je suis allé chercher la solution JerKimballs, et je suis d'accord avec ça. Cependant, je voudrais ajouter / souligner que c'est effectivement une question de controverse dans son ensemble. Dans mes recherches (pour d'autres raisons), j'ai trouvé les informations suivantes.
Quand les gens normaux (j'ai entendu dire qu'ils existent) parlent de gigaoctets, ils se réfèrent au système métrique dans lequel 1000 à la puissance de 3 à partir du nombre original d'octets == le nombre de gigaoctets. Cependant, bien sûr, il y a les normes IEC / JEDEC qui se résument bien dans wikipedia, qui au lieu de 1000 à la puissance de x, elles en ont 1024. Ce qui pour les périphériques de stockage physiques (et je suppose logique comme amazon et autres) signifie un différence toujours croissante entre métrique et CEI. Ainsi, par exemple, 1 To == 1 téraoctet métrique équivaut à 1000 à la puissance de 4, mais la CEI qualifie officiellement le même nombre de 1 Tio, le tebibyte à 1024 à la puissance de 4. Mais, hélas, dans les applications non techniques (je voudrais aller par public) la norme est métrique, et dans ma propre application à usage interne, j'explique actuellement la différence dans la documentation. Mais à des fins d'affichage, je n'offre même rien d'autre que la métrique. En interne, même si ce n'est pas pertinent dans mon application, je ne stocke que les octets et je fais le calcul pour l'affichage.
En passant, je trouve quelque peu terne que le framework .Net AFAIK (et je me trompe souvent, remercie les pouvoirs en place) même dans son incarnation 4.5 ne contient rien à ce sujet dans aucune bibliothèque en interne. On pourrait s'attendre à ce qu'une bibliothèque open source quelconque soit NuGettable à un moment donné, mais j'admets que c'est une petite bête noire. D'autre part, System.IO.DriveInfo et d'autres n'ont également que des octets (aussi longs), ce qui est plutôt clair.
public static class MyExtension
{
public static string ToPrettySize(this float Size)
{
return ConvertToPrettySize(Size, 0);
}
public static string ToPrettySize(this int Size)
{
return ConvertToPrettySize(Size, 0);
}
private static string ConvertToPrettySize(float Size, int R)
{
float F = Size / 1024f;
if (F < 1)
{
switch (R)
{
case 0:
return string.Format("{0:0.00} byte", Size);
case 1:
return string.Format("{0:0.00} kb", Size);
case 2:
return string.Format("{0:0.00} mb", Size);
case 3:
return string.Format("{0:0.00} gb", Size);
}
}
return ConvertToPrettySize(F, ++R);
}
}
Que diriez-vous d'une récursivité:
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");
}
}
Ensuite, vous pouvez l'appeler:
ReturnSize(size, string.Empty);
Comme indiqué ci-dessus, la récursivité est la méthode préférée, à l'aide du logarithme.
La fonction suivante a 3 arguments: l'entrée, la contrainte de dimension de la sortie, c'est-à-dire le troisième argument.
int ByteReDim(unsigned long ival, int constraint, unsigned long *oval)
{
int base = 1 + (int) log10(ival);
(*oval) = ival;
if (base > constraint) {
(*oval) = (*oval) >> 10;
return(1 + ByteReDim((*oval), constraint, oval));
} else
return(0);
}
Convertissons maintenant 12 Go de RAM en plusieurs unités:
int main(void)
{
unsigned long RAM;
int unit; // index of below symbols array
char symbol[5] = {'B', 'K', 'M', 'G', 'T'};
unit = ByteReDim(12884901888, 12, &RAM);
printf("%lu%c\n", RAM, symbol[unit]); // output is 12884901888B
unit = ByteReDim(12884901888, 9, &RAM);
printf("%lu%c\n", RAM, symbol[unit]); // output is 12582912K
unit = ByteReDim(12884901888, 6, &RAM);
printf("%lu%c\n", RAM, symbol[unit]); // output is 12288M
unit = ByteReDim(12884901888, 3, &RAM);
printf("%lu%c\n", RAM, symbol[unit]); // output is 12G
}
J'utilise ceci pour Windows (préfixes binaires):
static readonly string[] BinaryPrefix = { "bytes", "KB", "MB", "GB", "TB" }; // , "PB", "EB", "ZB", "YB"
string GetMemoryString(double bytes)
{
int counter = 0;
double value = bytes;
string text = "";
do
{
text = value.ToString("0.0") + " " + BinaryPrefix[counter];
value /= 1024;
counter++;
}
while (Math.Floor(value) > 0 && counter < BinaryPrefix.Length);
return text;
}
J'ai incorporé ceci (avec peu ou pas de modification) dans un convertisseur UWP DataBinding pour mon projet et j'ai pensé que cela pourrait également être utile à d'autres.
Le code est:
using System;
using System.Text;
using Windows.UI.Xaml.Data;
namespace MyApp.Converters
{
public class ByteSizeConverter : IValueConverter
{
static readonly string[] sSizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
// The number of decimal places the formatter should include in the scaled output - default 1dp
public int DecimalPlaces { get; set; } = 1;
public object Convert(object value, Type targetType, object parameter, string language)
{
Int64 intVal = System.Convert.ToInt64(value);
return SizeSuffix(intVal);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
// TODO: Parse string into number and suffix
// Scale number by suffix multiplier to get bytes
throw new NotImplementedException();
}
string SizeSuffix(Int64 value)
{
if (this.DecimalPlaces < 0) { throw new ArgumentOutOfRangeException(String.Format("DecimalPlaces = {0}", this.DecimalPlaces)); }
if (value < 0) { return "-" + SizeSuffix(-value); }
if (value == 0) { return string.Format("{0:n" + this.DecimalPlaces + "} bytes", 0); }
// magnitude is 0 for bytes, 1 for KB, 2, for MB, etc.
int magnitude = (int)Math.Log(value, 1024);
// clip magnitude - only 8 values currently supported, this prevents out-of-bounds exception
magnitude = Math.Min(magnitude, 8);
// 1L << (magnitude * 10) == 2 ^ (10 * magnitude) [i.e. the number of bytes in the unit corresponding to magnitude]
decimal adjustedSize = (decimal)value / (1L << (magnitude * 10));
// make adjustment when the value is large enough that it would round up to 1000 or more
if (Math.Round(adjustedSize, this.DecimalPlaces) >= 1000)
{
magnitude += 1;
adjustedSize /= 1024;
}
return String.Format("{0:n" + this.DecimalPlaces + "} {1}", adjustedSize, sSizeSuffixes[magnitude]);
}
}
}
Pour l'utiliser, ajoutez une ressource locale à votre UserControl ou Page XAML:
<UserControl.Resources>
<converters:ByteSizeConverter x:Key="ByteFormat" DecimalPlaces="3" />
</UserControl.Resources>
Référencez-le dans un modèle de liaison de données ou une instance de liaison de données:
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{x:Bind MyItem.FileSize_bytes, Mode=OneWay, Converter={StaticResource ByteFormat}}" />
Et hé hop. La magie opère.
https://github.com/logary/logary/blob/master/src/Logary/DataModel.fs#L832-L837
let scaleBytes (value : float) : float * string =
let log2 x = log x / log 2.
let prefixes = [| ""; "Ki"; "Mi"; "Gi"; "Ti"; "Pi" |] // note the capital K and the 'i'
let index = int (log2 value) / 10
1. / 2.**(float index * 10.),
sprintf "%s%s" prefixes.[index] (Units.symbol Bytes)
(DISCLAIMER: j'ai écrit ce code, même le code dans le lien!)