Nie jest to najskuteczniejszy sposób na zrobienie tego, ale łatwiej jest go przeczytać, jeśli nie znasz matematyki logów, i powinien być wystarczająco szybki dla większości scenariuszy.
string[] sizes ={"B","KB","MB","GB","TB"};double len =newFileInfo(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]);
@Constantin dobrze, że zależy od systemu operacyjnego? Windows nadal liczy 1024 bajty jako 1 KB i 1 MB = 1024 KB, Osobiście chcę wyrzucić KiB przez okno i po prostu policzyć każdą rzecz przy użyciu 1024? ...
Peter
4
@Petoj to nie zależy od systemu operacyjnego, definicja jest zależna od systemu operacyjnego. Z Wikipedii:The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
ANeves
3
Wolę ten kod, ponieważ wydaje się on działać szybciej, ale nieco go zmodyfikowałem, aby uwzględnić inną liczbę miejsc dziesiętnych. Mniejsze liczby lepiej pokazują 2 miejsca po przecinku, np. 1,38 MB, podczas gdy większe liczby wymagają mniejszej liczby miejsc po przecinku, np. 246k lub 23,5KB:
Myke Black
321
za pomocą dziennika, aby rozwiązać problem ....
staticStringBytesToString(long byteCount){string[] suf ={"B","KB","MB","GB","TB","PB","EB"};//Longs run out around EBif(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];}
Również w języku c #, ale powinno to być szybkie przekształcenie. Również zaokrągliłem do 1 miejsca po przecinku dla czytelności.
Zasadniczo określ liczbę miejsc dziesiętnych w podstawie 1024, a następnie podziel przez 1024 ^ miejsca dziesiętne.
I kilka przykładów użycia i wyników:
Console.WriteLine(BytesToString(9223372036854775807));//Results in 8EBConsole.WriteLine(BytesToString(0));//Results in 0BConsole.WriteLine(BytesToString(1024));//Results in 1KBConsole.WriteLine(BytesToString(2000000));//Results in 1.9MBConsole.WriteLine(BytesToString(-9023372036854775807));//Results in -7.8EB
Edycja: Zwrócono uwagę, że spóźniłem się na obliczenie matematyki, więc włączyłem ją. (Convert.ToInt32 używa zaokrąglania, a nie obcinania i dlatego Floor jest konieczny.) Dzięki za haczyk.
Edycja2: Było kilka komentarzy na temat rozmiarów ujemnych i 0 bajtów, więc zaktualizowałem, aby obsługiwać te 2 przypadki.
Chcę ostrzec, że chociaż ta odpowiedź jest rzeczywiście krótkim fragmentem kodu, nie jest najbardziej zoptymalizowana. Chciałbym, abyś spojrzał na metodę opublikowaną przez @humbads. Uruchomiłem mikrotestowanie, wysyłając 10 000 000 losowo wygenerowanych rozmiarów plików za pomocą obu metod, co powoduje, że jego metoda jest ~ 30% szybsza. Zrobiłem jednak pewne dalsze oczyszczenie jego metody (nieprzyzwoite zadania i casting). Ponadto przeprowadziłem test z ujemnym rozmiarem (podczas porównywania plików), podczas gdy metoda humbads bezbłędnie przetwarza tę metodę Loga, zgłasza wyjątek!
IvanL,
1
Tak, powinieneś dodać Math.Abs dla ujemnych rozmiarów. Ponadto kod nie obsługuje przypadku, jeśli rozmiar wynosi dokładnie 0
dasheddot
Math.Abs, Math.Floor, Math.Log, Konwersja na liczbę całkowitą, Math.Round, Math.Pow, Math.Sign, Dodawanie, mnożenie, dzielenie? Czy to nie mnóstwo matematyki po prostu zrobiłoby ogromny skok na procesorze. Jest to prawdopodobnie wolniejsze niż kod @humbads
Jayson Ragasa,
Nie odpowiada double.MaxValue(miejsce = 102)
BrunoLM,
Działa świetnie! Aby naśladować sposób działania systemu Windows (przynajmniej w moim Windows 7 Ultimate), zamień Math.Round na Math.Ceiling. Dzięki jeszcze raz. Podoba mi się to rozwiązanie.
H_He
101
Testowana i znacznie zoptymalizowana wersja żądanej funkcji jest opublikowana tutaj:
+1! Prostsze i proste! Sprawia, że procesor robi matematykę łatwo i szybciej!
Jayson Ragasa,
Do twojej wiadomości, double readable = (i < 0 ? -i : i);nigdzie nie używasz tej wartości, więc ją usuń. jeszcze jedno, obsada jest redaundat
Royi Namir
Usunąłem obsadę, dodałem komentarze i naprawiłem problem ze znakiem ujemnym.
humbads
Świetna odpowiedź. Dzięki, dlaczego nie po prostu użyć Math.Abs?
kspearrin
1
(i <0? -i: i) jest około 15% szybszy niż Math.Abs. W przypadku miliona połączeń Math.Abs działa wolniej o 0,5 milisekundy na mojej maszynie - 3,2 ms w porównaniu z 3,7 ms.
humbads
72
[DllImport("Shlwapi.dll",CharSet=CharSet.Auto)]publicstaticexternlongStrFormatByteSize(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>publicstaticstringStrFormatByteSize(long filesize){StringBuilder sb =newStringBuilder(11);StrFormatByteSize( filesize, sb, sb.Capacity);return sb.ToString();}
Mógłbym być noobem, ale użycie tak gigantycznej armaty jak pinvoke do zabicia tej kaczki jest dużym nadużyciem.
Bart
27
Czy tego używa Eksplorator? Jeśli tak, to niezwykle przydatne, aby pozwolić ludziom dopasować rozmiar pliku, który im pokazujesz, z tym, co pokazuje Eksplorator.
Andrew Backer,
8
I taki, który nie wymyśli na nowo koła
Matthew Lock
Czy 11 znaków nie jest dla tego stałym limitem i jest trochę za niskie? To znaczy, inne języki mogą używać więcej znaków dla akronimu rozmiaru bajtu lub innych stylów formatowania.
Ray
1
@Bart zajmuje trochę czasu, zanim nooby nauczyły się mądrości: „Powinniśmy zapomnieć o małej wydajności, powiedzmy w około 97% przypadków: przedwczesna optymalizacja jest źródłem wszelkiego zła” ubiquity.acm.org/article.cfm? id = 1513451
Matthew Lock
22
Jest jeszcze jeden sposób na skórowanie go, bez żadnych pętli i z obsługą ujemnego rozmiaru (ma sens w przypadku delt rozmiaru pliku):
Dobra odpowiedź. Problem powinien występować, gdy rozmiar pliku jest zbyt mały, w takim przypadku / 1024 zwraca 0. Możesz użyć typu ułamkowego i wywołania Math.Ceilinglub czegoś takiego.
nawfal
10
Oto zwięzła odpowiedź, która automatycznie określa jednostkę.
za każdy zastanawiać, dlaczego nie ma opo KMGTPE: francuskiego ( bytejest octetw języku francuskim). W przypadku każdego innego języka po prostu zamień onab
Powinieneś sprawdzić: while (rozmiar> = 1024 && s <sufiksy. Długość).
TcKs,
nie ... 64-bitowa liczba całkowita ze znakiem nie może wykraczać poza ZB ... która reprezentuje liczby 2 ^ 70.
bobwienholt
7
Po co więc wkładać YB?
konfigurator
Najbardziej podoba mi się ta odpowiedź, ale wszyscy tutaj naprawdę wprowadzają naprawdę nieefektywne rozwiązania, powinieneś użyć zmiany „rozmiar = rozmiar >> 10” jest o wiele szybszy niż podział ... i myślę, że dobrze jest mieć dodatkowe greckie specyfikatory są dostępne, ponieważ w najbliższej przyszłości możliwa funkcja DLR nie będzie potrzebować „długiego rozmiaru ..” możesz być na 128-bitowym procesorze wektorowym lub czymś, co może pomieścić ZB i większe;)
RandomNickName42
4
Bitshifting był bardziej wydajny niż podział w czasach kodowania C na metalu. Czy wykonałeś test wydajności w .NET, aby sprawdzić, czy zmiana bitów naprawdę jest bardziej wydajna? Nie tak dawno temu spojrzałem na stan zamiany xor i stwierdziłem, że w rzeczywistości .NET jest wolniejszy niż przy użyciu zmiennej temp.
Pete,
7
Jeśli próbujesz dopasować rozmiar, jak pokazano w widoku szczegółów Eksploratora Windows, jest to odpowiedni kod:
Spowoduje to nie tylko dokładne dopasowanie Eksploratora, ale także zapewni przetłumaczone ciągi i różnice w wersjach systemu Windows (na przykład w Win10, K = 1000 w porównaniu do poprzednich wersji K = 1024).
Ten kod nie kompiluje się, musisz podać dll, z którego pochodzi funkcja. Prototyp całej funkcji brzmi następująco: [DllImport ("shlwapi.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern long StrFormatKBSize (long qdw, [MarshalAs (UnmanagedType.LPTStr)] StringBuilder pszBuf, int cchB ); Pozwól mi być pierwszym, który faworyzuje to rozwiązanie. Po co wymyślać koło, jeśli koło zostało już wynalezione? Jest to typowe podejście wszystkich programistów C #, ale niestety C # nie osiąga wszystkich celów, które osiąga C ++.
TarmoPikaro
I jeszcze jedna poprawka: Int64.MaxValue osiąga 9,223,372,036,854,775,807, co wymaga przydzielenia wielkości bufora 25+ - na wszelki wypadek zaokrągliłem ją do 32 (nie 11 jak w powyższym kodzie demonstracyjnym).
TarmoPikaro
Dzięki @TarmoPikaro. Kiedy skopiowałem z mojego działającego kodu, przegapiłem DllImport. Zwiększono także rozmiar bufora zgodnie z zaleceniami. Dobry chwyt!
Metalogiczny
imponujące podejście
tbhaxor
Pokazuje tylko jednostkę KB. Chodzi o to, aby pokazać największą jednostkę w zależności od wartości.
jstuardo
5
Mieszanka wszystkich rozwiązań :-)
/// <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>publicstaticstringFormatByteSize(double fileSize){FileSizeUnit unit =FileSizeUnit.B;while(fileSize >=1024&& unit <FileSizeUnit.YB){
fileSize = fileSize /1024;
unit++;}returnstring.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>publicstaticstringFormatByteSize(FileInfo fileInfo){returnFormatByteSize(fileInfo.Length);}}publicenumFileSizeUnit:byte{
B,
KB,
MB,
GB,
TB,
PB,
EB,
ZB,
YB
}
Jak rozwiązanie @ NET3. Użyj shift zamiast podziału, aby przetestować zakres bytes, ponieważ podział wymaga więcej kosztów procesora.
privatestaticreadonlystring[] UNITS =newstring[]{"B","KB","MB","GB","TB","PB","EB"};publicstaticstringFormatSize(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));returnstring.Format("{0:0.##} {1}", n, UNITS[c]);}
Ponieważ funkcje te służą celom prezentacji, należy podać kulturę, na przykład: string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
W zależności od kontekstu kilobajt może mieć 1000 lub 1024 bajtów . To samo dotyczy MB, GB itp.
Kilobajt oznacza 1000 bajtów ( wolframalpha.com/input/?i=kilobyte ), nie zależy od kontekstu. To historycznie zależała od kontekstu, jak mówi wikipedia, i to było zmieniane de jure w 1998 roku i zmiana de facto rozpoczęła się około 2005 roku, kiedy terabajt dyski twarde przyniósł go do publicznej wiadomości. Termin 1024 bajtów to kibibajt. Kod przełączający je w oparciu o kulturę generuje nieprawidłowe informacje.
Superbest
1
Jeszcze jedno podejście, za które warto. Podobało mi się zoptymalizowane rozwiązanie @humbads, o którym mowa powyżej, więc skopiowałem zasadę, ale wdrożyłem ją nieco inaczej.
Podejrzewam, że jest dyskusyjne, czy powinna to być metoda rozszerzenia (ponieważ nie wszystkie długości są koniecznie rozmiarami bajtów), ale lubię je i gdzieś mogę znaleźć metodę, gdy będę jej potrzebować!
Jeśli chodzi o jednostki, wydaje mi się, że nigdy w życiu nie mówiłem „Kibibyte” lub „Mebibyte” i chociaż jestem sceptyczny wobec tak egzekwowanych, a nie ewoluowanych standardów, przypuszczam, że pozwoli to uniknąć zamieszania w perspektywie długoterminowej .
publicstaticclassLongExtensions{privatestaticreadonlylong[] numberOfBytesInUnit;privatestaticreadonlyFunc<long,string>[] bytesToUnitConverters;staticLongExtensions(){
numberOfBytesInUnit =newlong[6]{1L<<10,// Bytes in a Kibibyte1L<<20,// Bytes in a Mebibyte1L<<30,// Bytes in a Gibibyte1L<<40,// Bytes in a Tebibyte1L<<50,// Bytes in a Pebibyte1L<<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 =newFunc<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",};}publicstaticstringToReadableByteSizeString(thislong 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);}}
Używam metody Długie rozszerzenie poniżej, aby przekonwertować na ciąg rozmiaru czytelny dla człowieka. Ta metoda jest implementacją w języku C # rozwiązania Java dla tego samego pytania opublikowanego na stosie przepełnienia, tutaj .
/// <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>publicstaticstringToHumanReadableByteCount(thislong 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";}
Odpowiedzi:
Nie jest to najskuteczniejszy sposób na zrobienie tego, ale łatwiej jest go przeczytać, jeśli nie znasz matematyki logów, i powinien być wystarczająco szybki dla większości scenariuszy.
źródło
The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
za pomocą dziennika, aby rozwiązać problem ....
Również w języku c #, ale powinno to być szybkie przekształcenie. Również zaokrągliłem do 1 miejsca po przecinku dla czytelności.
Zasadniczo określ liczbę miejsc dziesiętnych w podstawie 1024, a następnie podziel przez 1024 ^ miejsca dziesiętne.
I kilka przykładów użycia i wyników:
Edycja: Zwrócono uwagę, że spóźniłem się na obliczenie matematyki, więc włączyłem ją. (Convert.ToInt32 używa zaokrąglania, a nie obcinania i dlatego Floor jest konieczny.) Dzięki za haczyk.
Edycja2: Było kilka komentarzy na temat rozmiarów ujemnych i 0 bajtów, więc zaktualizowałem, aby obsługiwać te 2 przypadki.
źródło
double.MaxValue
(miejsce = 102)Testowana i znacznie zoptymalizowana wersja żądanej funkcji jest opublikowana tutaj:
C # Rozmiar pliku czytelny dla człowieka - funkcja zoptymalizowana
Kod źródłowy:
źródło
double readable = (i < 0 ? -i : i);
nigdzie nie używasz tej wartości, więc ją usuń. jeszcze jedno, obsada jest redaundatMath.Abs
?Od: http://www.pinvoke.net/default.aspx/shlwapi/StrFormatByteSize.html
źródło
Jest jeszcze jeden sposób na skórowanie go, bez żadnych pętli i z obsługą ujemnego rozmiaru (ma sens w przypadku delt rozmiaru pliku):
A oto pakiet testowy:
źródło
Checkout ByteSize bibliotekę. To
System.TimeSpan
bajty!Obsługuje konwersję i formatowanie.
Wykonuje również reprezentację ciągów i parsowanie.
źródło
Lubię używać następującej metody (obsługuje do terabajtów, co wystarcza w większości przypadków, ale można ją łatwo rozszerzyć):
Należy pamiętać, że jest to napisane dla C # 6.0 (2015), więc może wymagać niewielkiej edycji we wcześniejszych wersjach.
źródło
źródło
Math.Ceiling
lub czegoś takiego.Oto zwięzła odpowiedź, która automatycznie określa jednostkę.
„b” oznacza bit, „B” oznacza bajt, a „KMGTPEZY” odpowiednio dla kilograma, mega, giga, tera, peta, exa, zetta i yotta
Można go rozszerzyć, aby uwzględnić ISO / IEC80000 :
źródło
o
po KMGTPE: francuskiego (byte
jestoctet
w języku francuskim). W przypadku każdego innego języka po prostu zamieńo
nab
źródło
Jeśli próbujesz dopasować rozmiar, jak pokazano w widoku szczegółów Eksploratora Windows, jest to odpowiedni kod:
Spowoduje to nie tylko dokładne dopasowanie Eksploratora, ale także zapewni przetłumaczone ciągi i różnice w wersjach systemu Windows (na przykład w Win10, K = 1000 w porównaniu do poprzednich wersji K = 1024).
źródło
Mieszanka wszystkich rozwiązań :-)
źródło
Jest jeden projekt open source, który może to zrobić i wiele więcej.
http://humanizr.net/#bytesize
https://github.com/MehdiK/Humanizer
źródło
Jak rozwiązanie @ NET3. Użyj shift zamiast podziału, aby przetestować zakres
bytes
, ponieważ podział wymaga więcej kosztów procesora.źródło
Zakładam, że szukasz „1,4 MB” zamiast „1468006 bajtów”?
Nie sądzę, że istnieje wbudowany sposób, aby to zrobić w .NET. Musisz tylko dowiedzieć się, która jednostka jest odpowiednia i sformatować ją.
Edycja: Oto przykładowy kod, aby to zrobić:
http://www.codeproject.com/KB/cpp/formatsize.aspx
źródło
Co powiesz na rekurencję:
Następnie nazywacie to:
źródło
Moje 2 centy:
string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
źródło
Jeszcze jedno podejście, za które warto. Podobało mi się zoptymalizowane rozwiązanie @humbads, o którym mowa powyżej, więc skopiowałem zasadę, ale wdrożyłem ją nieco inaczej.
Podejrzewam, że jest dyskusyjne, czy powinna to być metoda rozszerzenia (ponieważ nie wszystkie długości są koniecznie rozmiarami bajtów), ale lubię je i gdzieś mogę znaleźć metodę, gdy będę jej potrzebować!
Jeśli chodzi o jednostki, wydaje mi się, że nigdy w życiu nie mówiłem „Kibibyte” lub „Mebibyte” i chociaż jestem sceptyczny wobec tak egzekwowanych, a nie ewoluowanych standardów, przypuszczam, że pozwoli to uniknąć zamieszania w perspektywie długoterminowej .
źródło
Używam metody Długie rozszerzenie poniżej, aby przekonwertować na ciąg rozmiaru czytelny dla człowieka. Ta metoda jest implementacją w języku C # rozwiązania Java dla tego samego pytania opublikowanego na stosie przepełnienia, tutaj .
źródło