Как получить удобочитаемый размер файла в байтах, используя .NET?
Пример : взять ввод 7,326,629 и отобразить 6,98 МБ
Как получить удобочитаемый размер файла в байтах, используя .NET?
Пример : взять ввод 7,326,629 и отобразить 6,98 МБ
Ответы:
Это не самый эффективный способ сделать это, но его легче читать, если вы не знакомы с математикой журнала, и он должен быть достаточно быстрым для большинства сценариев.
string[] sizes = { "B", "KB", "MB", "GB", "TB" };
double len = new FileInfo(filename).Length;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1) {
order++;
len = len/1024;
}
// Adjust the format string to your preferences. For example "{0:0.#}{1}" would
// show a single decimal place, and no space.
string result = String.Format("{0:0.##} {1}", len, sizes[order]);
The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
используя журнал для решения проблемы ....
static String BytesToString(long byteCount)
{
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB
if (byteCount == 0)
return "0" + suf[0];
long bytes = Math.Abs(byteCount);
int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num = Math.Round(bytes / Math.Pow(1024, place), 1);
return (Math.Sign(byteCount) * num).ToString() + suf[place];
}
Также в C #, но должно быть легко конвертировать. Также я округлил до 1 десятичного знака для удобства чтения.
В основном определите количество десятичных знаков в базе 1024, а затем разделите на 1024 ^ десятичных знаков.
И некоторые примеры использования и вывода:
Console.WriteLine(BytesToString(9223372036854775807)); //Results in 8EB
Console.WriteLine(BytesToString(0)); //Results in 0B
Console.WriteLine(BytesToString(1024)); //Results in 1KB
Console.WriteLine(BytesToString(2000000)); //Results in 1.9MB
Console.WriteLine(BytesToString(-9023372036854775807)); //Results in -7.8EB
Изменить: было указано, что я пропустил math.floor, поэтому я включил его. (Convert.ToInt32 использует округление, а не усечение, и поэтому необходим Floor.) Спасибо за улов.
Edit2: было несколько комментариев об отрицательных размерах и 0 байтных размерах, поэтому я обновил, чтобы обработать эти 2 случая.
double.MaxValue
(место = 102)
Тестовая и значительно оптимизированная версия запрашиваемой функции размещена здесь:
C # Удобочитаемый размер файла - оптимизированная функция
Исходный код:
// Returns the human-readable file size for an arbitrary, 64-bit file size
// The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB"
public string GetBytesReadable(long i)
{
// Get absolute value
long absolute_i = (i < 0 ? -i : i);
// Determine the suffix and readable value
string suffix;
double readable;
if (absolute_i >= 0x1000000000000000) // Exabyte
{
suffix = "EB";
readable = (i >> 50);
}
else if (absolute_i >= 0x4000000000000) // Petabyte
{
suffix = "PB";
readable = (i >> 40);
}
else if (absolute_i >= 0x10000000000) // Terabyte
{
suffix = "TB";
readable = (i >> 30);
}
else if (absolute_i >= 0x40000000) // Gigabyte
{
suffix = "GB";
readable = (i >> 20);
}
else if (absolute_i >= 0x100000) // Megabyte
{
suffix = "MB";
readable = (i >> 10);
}
else if (absolute_i >= 0x400) // Kilobyte
{
suffix = "KB";
readable = i;
}
else
{
return i.ToString("0 B"); // Byte
}
// Divide by 1024 to get fractional value
readable = (readable / 1024);
// Return formatted number with suffix
return readable.ToString("0.### ") + suffix;
}
double readable = (i < 0 ? -i : i);
поэтому удалите его. еще один актерский состав - редунат
Math.Abs
?
[DllImport ( "Shlwapi.dll", CharSet = CharSet.Auto )]
public static extern long StrFormatByteSize (
long fileSize
, [MarshalAs ( UnmanagedType.LPTStr )] StringBuilder buffer
, int bufferSize );
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="filelength">The numeric value to be converted.</param>
/// <returns>the converted string</returns>
public static string StrFormatByteSize (long filesize) {
StringBuilder sb = new StringBuilder( 11 );
StrFormatByteSize( filesize, sb, sb.Capacity );
return sb.ToString();
}
От: http://www.pinvoke.net/default.aspx/shlwapi/StrFormatByteSize.html
Еще один способ обработать его, без каких-либо циклов и с поддержкой отрицательного размера (имеет смысл для таких вещей, как дельта размера файла):
public static class Format
{
static string[] sizeSuffixes = {
"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
public static string ByteSize(long size)
{
Debug.Assert(sizeSuffixes.Length > 0);
const string formatTemplate = "{0}{1:0.#} {2}";
if (size == 0)
{
return string.Format(formatTemplate, null, 0, sizeSuffixes[0]);
}
var absSize = Math.Abs((double)size);
var fpPower = Math.Log(absSize, 1000);
var intPower = (int)fpPower;
var iUnit = intPower >= sizeSuffixes.Length
? sizeSuffixes.Length - 1
: intPower;
var normSize = absSize / Math.Pow(1000, iUnit);
return string.Format(
formatTemplate,
size < 0 ? "-" : null, normSize, sizeSuffixes[iUnit]);
}
}
А вот и тестовый набор:
[TestFixture] public class ByteSize
{
[TestCase(0, Result="0 B")]
[TestCase(1, Result = "1 B")]
[TestCase(1000, Result = "1 KB")]
[TestCase(1500000, Result = "1.5 MB")]
[TestCase(-1000, Result = "-1 KB")]
[TestCase(int.MaxValue, Result = "2.1 GB")]
[TestCase(int.MinValue, Result = "-2.1 GB")]
[TestCase(long.MaxValue, Result = "9.2 EB")]
[TestCase(long.MinValue, Result = "-9.2 EB")]
public string Format_byte_size(long size)
{
return Format.ByteSize(size);
}
}
Оформить заказ на библиотеку 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");
Мне нравится использовать следующий метод (он поддерживает до терабайтов, что достаточно для большинства случаев, но его легко расширить):
private string GetSizeString(long length)
{
long B = 0, KB = 1024, MB = KB * 1024, GB = MB * 1024, TB = GB * 1024;
double size = length;
string suffix = nameof(B);
if (length >= TB) {
size = Math.Round((double)length / TB, 2);
suffix = nameof(TB);
}
else if (length >= GB) {
size = Math.Round((double)length / GB, 2);
suffix = nameof(GB);
}
else if (length >= MB) {
size = Math.Round((double)length / MB, 2);
suffix = nameof(MB);
}
else if (length >= KB) {
size = Math.Round((double)length / KB, 2);
suffix = nameof(KB);
}
return $"{size} {suffix}";
}
Помните, что это написано для C # 6.0 (2015), поэтому для более ранних версий может потребоваться небольшое редактирование.
int size = new FileInfo( filePath ).Length / 1024;
string humanKBSize = string.Format( "{0} KB", size );
string humanMBSize = string.Format( "{0} MB", size / 1024 );
string humanGBSize = string.Format( "{0} GB", size / 1024 / 1024 );
Math.Ceiling
или что-то еще.
Вот краткий ответ, который определяет единицу автоматически.
public static string ToBytesCount(this long bytes)
{
int unit = 1024;
string unitStr = "b";
if (bytes < unit) return string.Format("{0} {1}", bytes, unitStr);
else unitStr = unitStr.ToUpper();
int exp = (int)(Math.Log(bytes) / Math.Log(unit));
return string.Format("{0:##.##} {1}{2}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], unitStr);
}
«b» для бита, «B» для байта и «KMGTPEZY» соответственно для кило, мега, гига, тера, пета, exa, zetta и yotta
Можно расширить его, чтобы принять во внимание ISO / IEC80000 :
public static string ToBytesCount(this long bytes, bool isISO = true)
{
int unit = 1024;
string unitStr = "b";
if (!isISO) unit = 1000;
if (bytes < unit) return string.Format("{0} {1}", bytes, unitStr);
else unitStr = unitStr.ToUpper();
if (isISO) unitStr = "i" + unitStr;
int exp = (int)(Math.Log(bytes) / Math.Log(unit));
return string.Format("{0:##.##} {1}{2}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], unitStr);
}
o
после того, как KMGTPE: Его французский ( byte
это octet
на французском языке). Для любого другого языка просто заменить o
сb
string[] suffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
int s = 0;
long size = fileInfo.Length;
while (size >= 1024)
{
s++;
size /= 1024;
}
string humanReadable = String.Format("{0} {1}", size, suffixes[s]);
Если вы пытаетесь соответствовать размеру, как показано в подробном представлении Windows Explorer, вам нужен следующий код:
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern long StrFormatKBSize(
long qdw,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszBuf,
int cchBuf);
public static string BytesToString(long byteCount)
{
var sb = new StringBuilder(32);
StrFormatKBSize(byteCount, sb, sb.Capacity);
return sb.ToString();
}
Это будет не только точно соответствовать Explorer, но также предоставит переведенные для вас строки и совпадет с различиями в версиях Windows (например, в Win10, K = 1000 против предыдущих версий K = 1024).
Смесь всех решений :-)
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes,
/// kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="fileSize">The numeric value to be converted.</param>
/// <returns>The converted string.</returns>
public static string FormatByteSize(double fileSize)
{
FileSizeUnit unit = FileSizeUnit.B;
while (fileSize >= 1024 && unit < FileSizeUnit.YB)
{
fileSize = fileSize / 1024;
unit++;
}
return string.Format("{0:0.##} {1}", fileSize, unit);
}
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes,
/// kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="fileInfo"></param>
/// <returns>The converted string.</returns>
public static string FormatByteSize(FileInfo fileInfo)
{
return FormatByteSize(fileInfo.Length);
}
}
public enum FileSizeUnit : byte
{
B,
KB,
MB,
GB,
TB,
PB,
EB,
ZB,
YB
}
Есть один проект с открытым исходным кодом, который может сделать это и многое другое.
7.Bits().ToString(); // 7 b
8.Bits().ToString(); // 1 B
(.5).Kilobytes().Humanize(); // 512 B
(1000).Kilobytes().ToString(); // 1000 KB
(1024).Kilobytes().Humanize(); // 1 MB
(.5).Gigabytes().Humanize(); // 512 MB
(1024).Gigabytes().ToString(); // 1 TB
Как и решение @ NET3. Используйте сдвиг вместо деления для проверки диапазона bytes
, потому что деление требует больше ресурсов процессора.
private static readonly string[] UNITS = new string[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
public static string FormatSize(ulong bytes)
{
int c = 0;
for (c = 0; c < UNITS.Length; c++)
{
ulong m = (ulong)1 << ((c + 1) * 10);
if (bytes < m)
break;
}
double n = bytes / (double)((ulong)1 << (c * 10));
return string.Format("{0:0.##} {1}", n, UNITS[c]);
}
Я полагаю, вы ищете "1,4 МБ" вместо "1468006 байт"?
Я не думаю, что есть встроенный способ сделать это в .NET. Вам нужно просто выяснить, какой блок подходит, и отформатировать его.
Изменить: Вот пример кода, чтобы сделать это:
Как насчет рекурсии:
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");
}
}
Тогда вы называете это:
return ReturnSize(size, string.Empty);
Мои 2 цента:
string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
Еще один подход, для чего это стоит. Мне понравилось оптимизированное решение @humbads, упомянутое выше, поэтому я скопировал этот принцип, но я реализовал его немного по-другому.
Я предполагаю, что это спорный вопрос о том, должен ли это быть метод расширения (поскольку не все длинные значения обязательно имеют размер в байтах), но они мне нравятся, и где-то я могу найти метод, когда он мне понадобится в следующий раз!
Что касается юнитов, я не думаю, что когда-либо говорил «Kibibyte» или «Mebibyte» в своей жизни, и, хотя я скептически отношусь к таким неукоснительным, а не к развитым стандартам, я полагаю, что в долгосрочной перспективе это позволит избежать путаницы ,
public static class LongExtensions
{
private static readonly long[] numberOfBytesInUnit;
private static readonly Func<long, string>[] bytesToUnitConverters;
static LongExtensions()
{
numberOfBytesInUnit = new long[6]
{
1L << 10, // Bytes in a Kibibyte
1L << 20, // Bytes in a Mebibyte
1L << 30, // Bytes in a Gibibyte
1L << 40, // Bytes in a Tebibyte
1L << 50, // Bytes in a Pebibyte
1L << 60 // Bytes in a Exbibyte
};
// Shift the long (integer) down to 1024 times its number of units, convert to a double (real number),
// then divide to get the final number of units (units will be in the range 1 to 1023.999)
Func<long, int, string> FormatAsProportionOfUnit = (bytes, shift) => (((double)(bytes >> shift)) / 1024).ToString("0.###");
bytesToUnitConverters = new Func<long,string>[7]
{
bytes => bytes.ToString() + " B",
bytes => FormatAsProportionOfUnit(bytes, 0) + " KiB",
bytes => FormatAsProportionOfUnit(bytes, 10) + " MiB",
bytes => FormatAsProportionOfUnit(bytes, 20) + " GiB",
bytes => FormatAsProportionOfUnit(bytes, 30) + " TiB",
bytes => FormatAsProportionOfUnit(bytes, 40) + " PiB",
bytes => FormatAsProportionOfUnit(bytes, 50) + " EiB",
};
}
public static string ToReadableByteSizeString(this long bytes)
{
if (bytes < 0)
return "-" + Math.Abs(bytes).ToReadableByteSizeString();
int counter = 0;
while (counter < numberOfBytesInUnit.Length)
{
if (bytes < numberOfBytesInUnit[counter])
return bytesToUnitConverters[counter](bytes);
counter++;
}
return bytesToUnitConverters[counter](bytes);
}
}
Я использую метод длинного расширения, приведенный ниже, чтобы преобразовать его в удобочитаемую строку размера. Этот метод является реализацией C # решения Java этого же вопроса, опубликованного в Stack Overflow, здесь .
/// <summary>
/// Convert a byte count into a human readable size string.
/// </summary>
/// <param name="bytes">The byte count.</param>
/// <param name="si">Whether or not to use SI units.</param>
/// <returns>A human readable size string.</returns>
public static string ToHumanReadableByteCount(
this long bytes
, bool si
)
{
var unit = si
? 1000
: 1024;
if (bytes < unit)
{
return $"{bytes} B";
}
var exp = (int) (Math.Log(bytes) / Math.Log(unit));
return $"{bytes / Math.Pow(unit, exp):F2} " +
$"{(si ? "kMGTPE" : "KMGTPE")[exp - 1] + (si ? string.Empty : "i")}B";
}