Обеспечивает ли.NET простой способ преобразования байтов в КБ, МБ, ГБ и т. Д.?
Просто интересно, если.NET предоставляет чистый способ сделать это:
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);
}
так далее...
30 ответов
Вот довольно краткий способ сделать это:
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]);
}
И вот оригинальная реализация, которую я предложил, которая может быть немного медленнее, но немного легче следовать:
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));
Оформить заказ на библиотеку 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");
Я бы решил это используя Extension methods
, Math.Pow
функция и 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");
}
}
и используйте это как:
string h = x.ToSize(MyExtension.SizeUnits.KB);
Поскольку все остальные публикуют свои методы, я решил опубликовать метод расширения, который я обычно использую для этого:
РЕДАКТИРОВАТЬ: добавил int/long варианты... и исправил опечатку 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;
}
}
Я знаю, что это уже старая ветка. но может кто поищет решение. А вот что я использую и самый простой способ
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";
}
Обновлено для C# 9.0реляционных шаблонов
public static string BytesToHumanReadable(ulong bytes)
{
return bytes switch
{
(< OneKB) => $"{bytes}B",
(>= OneKB) and (< OneMB) => $"{bytes / OneKB}KB",
(>= OneMB) and (< OneGB) => $"{bytes / OneMB}MB",
(>= OneGB) and (< OneTB) => $"{bytes / OneMB}GB",
(>= OneTB) => $"{bytes / OneTB}"
//...
};
}
Короткая версия ответа с наибольшим количеством голосов имеет проблемы со значениями ТБ.
Я настроил его соответствующим образом, чтобы обрабатывать также значения tb и все еще без цикла, а также добавил небольшую проверку ошибок для отрицательных значений. Вот мое решение:
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]);
}
@ Ответ Servy был приятным и лаконичным. Я думаю, что это может быть еще проще?
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];
}
Нет. Главным образом потому, что это довольно нишевая потребность, и есть слишком много возможных вариантов. (Это "КБ", "КБ" или "Ко"? Мегабайт 1024 * 1024 байта или 1024 * 1000 байтов? - да, в некоторых местах это используется!)
Вот вариант, который легче расширить, чем ваш, но нет, в самой библиотеке нет встроенного.
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;
}
}
Это один из способов сделать это также (номер 1073741824.0 от 1024*1024*1024 или ГБ)
На основе элегантного решения 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)
}
Возможно, есть чрезмерные комментарии, но я склонен оставлять их, чтобы не допустить повторения тех же ошибок в будущих посещениях...
Нет.
Но вы можете реализовать так;
static double ConvertBytesToMegabytes(long bytes)
{
return (bytes / 1024f) / 1024f;
}
static double ConvertKilobytesToMegabytes(long kilobytes)
{
return kilobytes / 1024f;
}
Также посмотрите Как правильно преобразовать размер файла в байтах в мегабайты или гигабайты?
Поскольку люди сейчас в моде добавлять свои собственные алгоритмы в качестве ответов на этот вопрос, вот мой. (Хотя, по общему признанию, моя основная мотивация — опубликовать это где-нибудь, где я смогу найти его снова, когда он мне понадобится!)
Это кроссплатформенный метод расширения C#, реализующий возможности Windows.функция, которая выдает результаты с тремя или четырьмя значащими цифрами и до двух десятичных знаков. Он отличается от нескольких незначительных особенностей (см. ниже). Совместим с .NET 7 и более поздними версиями, хотя не составит труда перенести его на предыдущие версии.
ЕслиincludeTotalBytes
являетсяtrue
, к строке добавляется общее количество байтов, аналогично тому, что показано в диалоговом окне свойств проводника Windows.
Код
using System;
using System.Numerics;
internal static class BinaryIntegerExtensions
{
private static readonly string[] sizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB" };
/// <summary>Converts the numeric value of this instance to a string that represents the number expressed as a size-dependent value in bytes, kilobytes, megabytes, etc, up to quettabytes.</summary>
/// <remarks>The string expression units are based on powers of 2, represented by the colloquially-understood KB, MB, GB, etc, instead of the technically correct KiB, MiB, GiB, etc.</remarks>
/// <param name="includeTotalBytes"><c>true</c> to append the total number of bytes to the string; otherwise, <c>false</c>.</param>
/// <returns>The string representation of the value, expressed in bytes, kilobytes, megabytes, etc, up to quettabytes.</returns>
/// <exception cref="OverflowException">The numeric value of this instance is out of range and cannot be converted.</exception>
internal static string ToByteSizeString<T>(this T value, bool includeTotalBytes) where T : IBinaryInteger<T>
{
string result;
if (T.IsZero(value))
result = $"0 {sizeSuffixes[0]}";
else
{
int magnitude, decimalPlaces;
double absolute, fullResult, roundedResult;
string bytesPart = string.Empty;
absolute = double.CreateChecked(T.Abs(value));
magnitude = Math.Min((int)Math.Floor(Math.Log(absolute, 1024)), sizeSuffixes.Length - 1);
fullResult = T.Sign(value) * (absolute / Math.Pow(1024, magnitude));
decimalPlaces = Math.Max(0, 2 - (int)Math.Floor(Math.Log10(fullResult)));
roundedResult = Math.Round(fullResult, decimalPlaces, MidpointRounding.AwayFromZero);
if (includeTotalBytes && (magnitude > 0))
bytesPart = $" ({value:N0} {sizeSuffixes[0]})";
result = $"{roundedResult:#,#.##} {sizeSuffixes[magnitude]}{bytesPart}";
}
return (result);
}
}
Примеры использования
int test1 = 46432131;
string result1 = test1.ToByteSizeString(false);
// result1 == "44.3 MB"
ulong test2 = 1748413218964;
string result2 = test2.ToByteSizeString(false);
// result2 == "1.59 TB"
long test3 = 56431242;
string result3 = test3.ToByteSizeString(true);
// result3 == "53.8 MB (56,431,242 bytes)"
short test4 = 512;
string result4 = test4.ToByteSizeString(true);
// result4 == "512 bytes"
UInt128 test5 = UInt128.MaxValue;
string result5 = test5.ToByteSizeString(true);
// result5 = "268,435,456 QB (340,282,366,920,938,463,463,374,607,431,768,211,455 bytes)"
Отличия отStrFormatByteSize
- Числа в результирующей строке группируются там, где это необходимо.
- Результат округляется в большую или меньшую сторону по мере необходимости, тогда как StrFormatByteSize усекается после любого количества отображаемых десятичных знаков.
- Отрицательные значения работают должным образом, тогда как StrFormatByteSize предоставляет только общее количество байтов для отрицательных значений.
Как насчет рекурсии:
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");
}
}
Тогда вы можете назвать это:
ReturnSize(size, string.Empty);
Я объединил некоторые ответы здесь в два метода, которые прекрасно работают. Второй метод, приведенный ниже, преобразует из байтовой строки (например, 1,5,1 ГБ) обратно в байты (например, 1621350140) как значение типа long. Я надеюсь, что это полезно для других, ищущих решение для преобразования байтов в строку и обратно в байты.
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);
}
Недавно мне это понадобилось, и мне потребовалось преобразовать байты в число в long.
Применение: Byte.Kb.ToLong(1)
должен дать 1024.
public enum Byte
{
Kb,
Mb,
Gb,
Tb
}
public static class ByteSize
{
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 long ToLong(this Byte size, int value)
{
return size switch
{
Byte.Kb => value * OneKb,
Byte.Mb => value * OneMb,
Byte.Gb => value * OneGb,
Byte.Tb => value * OneTb,
_ => throw new NotImplementedException("This should never be hit.")
};
}
}
Тесты с использованием xunit:
[Theory]
[InlineData(Byte.Kb, 1, 1024)]
[InlineData(Byte.Kb, 2, 2048)]
[InlineData(Byte.Mb, 1, 1048576)]
[InlineData(Byte.Mb, 2, 2097152)]
[InlineData(Byte.Gb, 1, 1073741824)]
[InlineData(Byte.Gb, 2, 2147483648)]
[InlineData(Byte.Tb, 1, 1099511627776)]
[InlineData(Byte.Tb, 2, 2199023255552)]
public void ToLong_WhenConverting_ShouldMatchExpected(Byte size, int value, long expected)
{
var result = size.ToLong(value);
result.Should().Be(expected);
}
Я пошел на решение JerKimballs, и большие пальцы до этого. Тем не менее, я хотел бы добавить / указать, что это действительно вопрос спора в целом. В своем исследовании (по другим причинам) я нашел следующую информацию.
Когда нормальные люди (я слышал, что они существуют) говорят о гигабайтах, они ссылаются на метрическую систему, где 1000 в степени 3 от исходного числа байтов == число гигабайт. Тем не менее, конечно, есть стандарты IEC / JEDEC, которые хорошо подытожены в википедии, которые вместо 1000 к степени x имеют 1024. Что для физических запоминающих устройств (и я думаю, логично, таких как amazon и другие) означает постоянно увеличивающаяся разница между метрикой и IEC. Так, например, 1 ТБ == 1 терабайтная метрика равна 1000 в степени 4, но МЭК официально называет такое же число как 1 ТиБ, тэбибайт как 1024 в степени 4. Но, увы, в нетехнических приложениях (я бы по аудитории) норма является метрической, и в моем собственном приложении для внутреннего использования в настоящее время я объясняю разницу в документации. Но для демонстрации я даже не предлагаю ничего, кроме метрики. Внутренне, даже если это не имеет отношения к моему приложению, я сохраняю только байты и выполняю вычисления для отображения.
В качестве дополнительного примечания я нахожу несколько тусклым, что.Net-инфраструктура AFAIK (и я часто ошибаюсь, спасибо всем возможностям) даже в ее 4,5-ой версии не содержит ничего об этом ни в одной внутренней библиотеке. Можно было бы ожидать, что какая-то библиотека с открытым исходным кодом станет NuGettable в какой-то момент, но я признаю, что это небольшая мозоль. С другой стороны, System.IO.DriveInfo и другие также имеют только байты (длинные), что довольно понятно.
Как отмечалось выше, рекурсия является любимым способом с помощью логарифма.
Следующая функция имеет 3 аргумента: вход, ограничение размера выходных данных, то есть третий аргумент.
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);
}
Теперь давайте конвертируем 12 ГБ ОЗУ в несколько единиц:
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
}
Хорошо... Давайте воспользуемся тем, что предоставляет Windows...
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern long StrFormatByteSizeW(long qdw,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszBuf,
int cchBuf);
public static string GetFileSize(this long number)
{
var sb = new StringBuilder(32);
StrFormatByteSizeW(number, sb, sb.Capacity);
return sb.ToString();
}
Я объединил код zackmark15 в универсальный подход к измерению файлов или каталогов:
public static string PathSize(string path)
{
if (String.IsNullOrEmpty(path))
throw new ArgumentNullException(nameof(path));
long bytes;
if (File.Exists(path))
bytes = new FileInfo(path).Length;
else if (Directory.Exists(path))
bytes = new DirectoryInfo(path).EnumerateFiles("*", SearchOption.AllDirectories).Sum(fileInfo => fileInfo.Length);
else
throw new ArgumentException("Path does not exist.", nameof(path));
const long UNIT = 1024L;
if (bytes < UNIT)
return $"{bytes} bytes";
var exp = (int)(Math.Log(bytes) / Math.Log(UNIT));
return $"{bytes / Math.Pow(UNIT, exp):F2} {("KMGTPE")[exp - 1]}B";
}
static string convert(float bytes)
{
string[] Group = { "Bytes", "KB", "MB", "GB", "TB"};
float B = bytes; int G = 0;
while (B >= 1024 && G < 5)
{
B /= 1024;
G += 1;
}
float truncated = (float)(Math.Truncate((double)B * 100.0) / 100.0);
string load = (truncated + " " + Group[G]);
return load;
}
Я использую это для Windows (бинарные префиксы):
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;
}
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);
}
}
Вот мой ответ на @drzaus. Я изменил его, чтобы использовать ошибки округления в наших интересах и правильно управлять проблемами вокруг границ единиц. Он также обрабатывает отрицательные значения.
Брось это C# Program
в LinqPad:
// Kudos: https://stackru.com/a/48467634/117797
void Main()
{
0.ToFriendly().Dump(); // 0 B
857.ToFriendly().Dump(); // 857 B
(173*1024).ToFriendly().Dump(); // 173 KB
(9541*1024).ToFriendly().Dump(); // 9.32 MB
(5261890L*1024).ToFriendly().Dump(); // 5.02 GB
1.ToFriendly().Dump(); // 1 B
1024.ToFriendly().Dump(); // 1 KB
1048576.ToFriendly().Dump(); // 1 MB
1073741824.ToFriendly().Dump(); // 1 GB
1099511627776.ToFriendly().Dump(); // 1 TB
1125899906842620.ToFriendly().Dump(); // 1 PB
1152921504606850000.ToFriendly().Dump(); // 1 EB
}
public static class Extensions
{
static string[] _byteUnits = new[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
public static string ToFriendly(this int number, int decimals = 2)
{
return ((double)number).ToFriendly(decimals);
}
public static string ToFriendly(this long number, int decimals = 2)
{
return ((double)number).ToFriendly(decimals);
}
public static string ToFriendly(this double number, int decimals = 2)
{
const double divisor = 1024;
int unitIndex = 0;
var sign = number < 0 ? "-" : string.Empty;
var value = Math.Abs(number);
double lastValue = number;
while (value > 1)
{
lastValue = value;
// NOTE
// The following introduces ever increasing rounding errors, but at these scales we don't care.
// It also means we don't have to deal with problematic rounding errors due to dividing doubles.
value = Math.Round(value / divisor, decimals);
unitIndex++;
}
if (value < 1 && number != 0)
{
value = lastValue;
unitIndex--;
}
return $"{sign}{value} {_byteUnits[unitIndex]}";
}
}
Выход:
0 B
857 B
173 KB
9.32 MB
1.34 MB
5.02 GB
1 B
1 KB
1 MB
1 GB
1 TB
1 PB
1 EB
Как насчет:
public void printMB(uint sizekB)
{
double sizeMB = (double) sizekB / 1024;
Console.WriteLine("Size is " + sizeMB.ToString("0.00") + "MB");
}
Например, позвонить, как
printMB(123456);
Приведет к выводу
"Size is 120,56 MB"
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)
Искал более чистое решение, но, на мой взгляд, не могу его найти, так что вот.
Это предназначено для получения длинного значения размера файла с помощью File.Length, а затем преобразования его в тип размера и округления до двух десятичных знаков:
10 байт становятся 10Б
1030 КБ становится 1,01 МБ
16384 КБ становится 16 МБ
2 097152 КБ становятся 2 ГБ
13064424 КБ становится 12,46 ГБ
public static string FormatFileSize(long fileSizeBytes)
{
const int KB = 1024;
string[] sizeLabels = { "B", "KB", "MB", "GB", "TB" };
double fileSize = fileSizeBytes;
int labelIndex = 0;
while (fileSize >= KB && labelIndex < sizeLabels.Length - 1)
{
fileSize /= KB;
labelIndex++;
}
fileSize = Math.Round(fileSize, 2);
return $"{fileSize} {sizeLabels[labelIndex]}";
}
Я включил это (практически без изменений) в UWP DataBinding Converter для своего проекта и подумал, что он также может быть полезен другим.
Код такой:
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]);
}
}
}
Чтобы использовать его, добавьте локальный ресурс в свой UserControl или XAML страницы:
<UserControl.Resources>
<converters:ByteSizeConverter x:Key="ByteFormat" DecimalPlaces="3" />
</UserControl.Resources>
Ссылка на него в шаблоне привязки данных или экземпляре привязки данных:
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{x:Bind MyItem.FileSize_bytes, Mode=OneWay, Converter={StaticResource ByteFormat}}" />
И привет. Волшебство случается.
Вот как я это делаю.
Console.Write(FileSizeInBytes > 1048576 ? FileSizeInBytes / 1048576f + " MB" : FileSizeInBytes / 1024f + " KB"); //1048576 = 1024 * 1024