Відформатуйте задану кількість байтів у читаному для людини форматі


16

Виклик та походження

На стеку Overflow популярним питанням є: як перетворити розмір байтів у читаний для людей формат у Java? Відповідь, яка найбільше голосує, має досить приємний метод зробити це, але це кодовий гольф, і ми можемо зробити краще, чи не так?

Ваше завдання полягає в тому, щоб написати метод або програму, яка покриває задану кількість байтів у правильному для людини форматі і друкує результат у стандарті з вашої мови. *

* Дивіться правила для більш детального роз'яснення!

Вхідні дані

Вхід завжди буде додатною кількістю байтів, максимум (2 ^ 31) -1.

Вихідні дані

Ви можете вибрати, якщо ви віддаєте перевагу Міжнародній системі одиниць або двійковій нотації як вихід (позначення SI, ймовірно, заощаджує вам кілька байт).

SI:      B, kB,  MB,  GB  
Binary:  B, KiB, MiB, GiB

Примітка: Більш високі одиниці, ніж GB або GiB, неможливі через обмежений вхідний діапазон.

Приклад виведення

Міжнародна система одиниць:

Input       Output
0           0.0     B
999         999.0   B
1000        1.0     kB
1023        1.0     kB
1024        1.0     kB
1601        1.6     kB
160581      160.6   kB
4066888     4.1     MB
634000000   634.0   MB
2147483647  2.1     GB

Двійковий:

Input       Output
0           0.0     B
999         999.0   B
1000        1000.0  B
1023        1023.0  B
1024        1.0     KiB
1601        1.6     KiB
160581      156.8   KiB
4066888     3.9     MiB
634000000   604.6   MiB
2147483647  2.0     GiB

Правила

  • Вбудовані функції для форматування байтів заборонені!
  • Вихідні дані повинні завжди відповідати одному стандарту позначень, ви не можете змішувати SI або двійкові;
  • Вихід завжди повинен знаходитись у найбільшій можливій одиниці, коли отримане число все ще вище або дорівнює одиниці;
  • Вихід завжди повинен мати одне десяткове число, але ви можете вибрати друкувати ціле число, коли отриманий результат знаходиться в байтах (B);
  • Ви можете вибрати, чи хочете ви додати пробіл, вкладку чи нічого між номером та одиницею;
  • Вхід приймається через STDIN або функціональні параметри;
  • Виведення друкується на консоль або повертається у вигляді рядка (або подібного символьного контейнера);
  • Це кодовий гольф, тому найкоротша відповідь виграє. Веселіться!

Редагувати: Ще більше роз'яснення

Деякі номери мають цікаву поведінку округлення, наприклад, число 999950. Більшість реалізацій коду повертають 1000,0 кБ замість 1,0 Мб. Чому? Тому що 999950/1000 оцінює до 999.950, що ефективно округляється до 1000.0 при використанні String.format на Java (також у більшості інших мов). Хенч потребує додаткових перевірок для розгляду подібних випадків.

Для цього завдання приймаються обидва стилі, 1000,0 кБ та 1,0 МБ, хоча останній стиль є кращим.

Псевдокод / ​​код тесту Java:


public static String bytesToSI(long bytes){
      if (bytes < 1000){
          return bytes + ".0 B";
      }
      //Without this rounding check:
      //999950    would be 1000.0 kB instead of 1.0 MB
      //999950000 would be 1000.0 MB instead of 1.0 GB
      int p = (int) Math.ceil(Math.log(bytes) / Math.log(1000));
      if(bytes/Math.pow(1000, p) < 0.99995){
          p--;
      }
      //Format
      return String.format("%.1f %sB", bytes/Math.pow(1000, p), "kMGTPE".charAt(p-1));
}


1
Технічно кілобайт СІ повинен використовувати kB(зверніть увагу на
малий

Добрий момент, фіксований!
Рольф ツ

1
Я не хочу обмежуватися дуже багато, тому я б сказав, що інтервал може бути невідповідним. Але з цим правилом: Різниця в просторі та символах вкладок для різних дійсних входів не може перевищувати 10. (Щоб це все було трохи "читабельним для людини")
Рольф ツ

2
Який очікуваний результат для 999999та 1000000? 160581виставляє округлення, так воно повинно бути 1000.0kBі 1.0MB?
Sp3000

3
@ Sp3000 Це гарне запитання, найкращим рішенням було б 999999 відобразити 1,0 Мб. Але для цього завдання я б сказав, що 1000,0 Кб і подібні випадки округлення теж чудово.
Рольф ツ

Відповіді:


10

TI-BASIC, 44

Був би правильним інструментом для роботи, якби TI-BASIC провів на півдорозі пристойну маніпуляцію з рядком (мені довелося вдатися до перезапису експонента числа, відображеного в інженерній нотації, з одиницею). Насправді він округляє та виводить правильно, але навіть не близький до виграшних записів. Можливо, інша мова калькулятора могла виграти цю?

Fix 1
Eng
ClrHome
Disp Ans
Output(1,15,sub(" kMG",1+iPart(log(Ans+.5)/3),1)+"B

Введіть форму [number]:[program name]на головному екрані.

Наведені тестові випадки:

Input       Output (leading spaces intentional; screen clear before each output)
0                      0.0 B
999                  999.0 B
1000                   1.0kB
1023                   1.0kB
1024                   1.0kB
1601                   1.6kB
160581               160.6kB
4066888                4.1MB
634000000            634.0MB
2147483647             2.1GB

Я абсолютно не здогадувався, що TI-BASIC настільки універсальний ха-ха
бета-розпад

1
TI-BASIC не є універсальним, але часто існують дивні шляхи вирішення кількох його недоліків.
lirtosiast

6

CJam, 35 27 байт

ri{_e-3_i}g;1mOo]," kMG"='B

Дякую Деннісу, що він видалив 8 байт.

Це не друкується .0в онлайн-перекладачі . Але, як зазначив Денніс , він прекрасно працює в інтерпретаторі Java.

Пояснення

ri         e# Read the input as an integer.
{          e# Do:
    _e-3   e#   Make a copy and divide by 1000.
           e#   This will generate one more item in the stack for each iteration.
    _i     e#   Make a copy and truncate to integer.
}g         e# until the integer part is 0.
;          e# Discard the final value with integer part 0.
1mOo       e# Output the number before it with the correct format.
],         e# Count the number of iterations - 1.
" kMG"=    e# Select a character according to the number of iterations.
'B         e# Output B.

ri{_e-3XmO_i}g;o]," kMG"='B(27 байт)
Денніс

@ Денніс Дякую за 1mO. Але цей код не працює для 1149999...
jimmy23013

ri{_e-3_i}g;1mOo]," kMG"='Bповинен.
Денніс

Подряпи, що є інші помилки.
Денніс

999999стає 1000kB. Читаючи ще раз запитання, я не впевнений, чи 1000kBнасправді було б неправильно.
Денніс

5

Pyth, 29 27 байт

p@" kMG"Js.lQK^T3.RcQ^KJ1\B

Демонстрація. Тестовий джгут.

Пояснення:

p@" kMG"Js.lQK^T3.RcQ^KJ1\B
                                 Implicit: Q = eval(input())
p                                print, in the order 2nd arg then 1st arg:
             K^T3                K = 10^3 = 1000
          .lQK                   log of Q base K
         s                       Floored
        J                        Store to J
 @" kMG"J                        The Jth character of ' kMG'
                     ^KJ         K^J
                   cQ            Q/K^J (Floating point division)
                 .R     1        Round to 1 decimal place.
                         \B      Print a trailing 'B'.

3

CJam, 28

r_dA@,(3/:X3*#/1mO" kMG"X='B

Спробуйте в Інтернеті

Примітка: він не відображає ".0" з онлайн-перекладачем, але це робиться з офіційним перекладачем Java .

Пояснення:

r_          read and duplicate
dA          convert to double and push 10
@           bring the initial string to the top
,(          get the length and decrement
3/          divide by 3 (for thousands)
:X3*        store in X and multiply by 3 again
#           raise 10 to that power
/           divide the original number by it
1mO         round to 1 decimal
" kMG"X=    convert X from 0/1/2/3 to space/k/M/G
'B          add a 'B'

Для чого потрібен беккет?
Денніс

@Dennis показує .0 в онлайн-перекладачі
aditsu

Він прекрасно працює в Java-інтерпретаторі без зворотного вибору, тому я не думаю, що вам це потрібно.
Денніс

3

Python 2 - 76 байт

Використовує міжнародну систему одиниць, просто тому, що це простіше зробити в голові;)

n=input();m=0;f=1e3
while n>=f:n/=f;m+=2
print"%.1f%s"%(n,'B kBMBGB'[m:m+2])

мені здається, що це не нормально, воно не поважає запитуване форматування, наприклад, якщо я подаю "2147483647" я отримую "2.000000GB" - питання задає один десятковий знак, а може, і пробіл.
дітер

1
Також це 79 байт відповідно до цього . Це 75 байт. Я не вірю, що було вказано, що між номером та одиницею повинен бути пробіл.
Каде

ви можете зберегти один байт за допомогоюf=1e3
mbomb007

@ mbomb007 Насправді це врятувало 2 байти, тому що 1e3 є плаваючою
Beta Decay

Я знав, що це поплавок. Напевно, я просто не можу порахувати ...
mbomb007

2

POWERSHELL, 190

$x=Read-Host
function f($a,$b){"$x`t"+[math]::Round($x/$a,1).ToString("F1")+"`t$b"}
if(1KB-gt$x){f 1 "B"}elseif(1MB-gt$x){f 1KB KiB}
elseif(1GB-gt$x){f 1MB MiB}elseif(1TB-gt$x){f 1GB GiB}

використання

PS C:\> .\makehum.ps1
1601
1601    1.6     KiB
PS C:\> .\makehum.ps1
4066888
4066888 3.9     MiB
PS C:\> .\makehum.ps1
160581
160581  156.8   KiB
PS C:\> .\makehum.ps1
634000000
634000000       604.6   MiB
PS C:\> .\makehum.ps1
2147483647
2147483647      2.0     GiB
PS C:\>

2

Хаскелл, 119

Я, на жаль, не знайшов коротшого способу в Haskell, щоб забезпечити 1 десяткове місце у поплавках, але я розміщую повідомлення для нащадків.

import Text.Printf
a#n|p>=1=(a+1)#p|1<2=(a,n)where p=n/1000
m n=let(a,b)=0#n in printf"%.1f"b++["B","kB","MB","GB"]!!a

Використання:

> m 160581
"160.6kB"

Помірно менш гольф-версія:

import Text.Printf

countThousands :: Int -> Float -> (Int, Float)
countThousands count num
 |nextNum >= 1 = countThousands (count+1) nextNum
 |otherwise    = (count,num)
 where nextNum = num/1000

printHuman :: Float -> String
printHuman n = let (a,b) = countThousands 0 n in 
  (printf "%.1f" b) ++ (["B","kB","MB","GB"]!!a)

2

Java, 106 байт

Це метод, який приймає число і повертає рядок.

String f(int n){int k=0;for(;n>1e3;k++)n/=1e3;return(int)(10*n)/10.0+new String[]{"","k","M","G"}[k]+"B";}

Вам дозволено запрограмувати функцію, що повертає рядок замість повної програми, це може заощадити кілька байтів;)
Рольф ツ

Три речі: Якщо ви все одно перетворюєте на подвійний (я не знаю, чи потрібно), ви можете використовувати 1e3для 1000; ви можете перетворити це while()в a for()і використовувати вільні крапки з комою; і я не знаю, чи це працює, оскільки, здається, відображаються всі десяткові цифри, а не лише одна минула після десяткової коми.
lirtosiast

@ThomasKwa: Востаннє я перевірив, начебто, питання явно не вказувало цього. Але я здогадуюсь це і зараз.
SuperJedi224

1

Python 2, 127 байт

Використання ISU. Фрагмент оголошує функцію "C", яка приймає число для перетворення в якості аргументу.

C=lambda v:min(['%.1f %sB'%(x,u)for x,u in[(v/1000.0**i,'bkMG'[i])for i in range(4)]if x>=1]).replace('.0 b',' ')if v else'0 B'

Деякі тестові коди:

    print 'Input\tOutput'
for v in [0,999,1000,1023,1023,1601,160581,4066888,634000000,2147483647]:
 print v,C(v)

Ви можете використовувати 1e3замість1000.0
mbomb007

1

JavaScript ( ES6 ), 71

Використання одиниць SI - функція, що повертає запитуваний рядок.

f=(a,b=3)=>+(r=eval('a/1e'+b*3).toFixed(1))[0]?r+' kMG'[b]+'B':f(a,b-1)

Цей коротший дотримується правил, особливо 3 та 4

  • Вихід завжди повинен знаходитися в найбільшій можливій одиниці, коли отримане число все ще вище або дорівнює одиниці ніж 995 => 1,0 кБ
  • Вихід повинен завжди мати одне десяткове число, але ви можете вибрати друк цілого числа, коли отриманий результат знаходиться в байтах (B), я не вибираю, тому 10 => 10,0 B

На жаль, таким чином, результати не відповідають прикладам.

Щоб відповідати прикладам, ось довший, спеціальний кейс для невеликих чисел (82 байти)

f=(a,b=3)=>a<1e3?a+'B':+(r=eval('a/1e'+b--*3).toFixed(1))[0]?r+'kMG'[b]+'B':f(a,b)

Запустіть фрагмент для тестування (це лише EcmaScript 6, Firefox)


1

Пітон, 61 байт

f=lambda n,i=0:"%.1f%cB"%(n," kMG"[i])*(n<1e3)or f(n/1e3,i+1)

Телефонуйте як f(999) . Зауважте, що 1e3це поплавок, тому це працює як з Python 2, так і з Python 3.


1

PHP4.1, 63 62 байти

Не найкращий гольф, але, безумовно, досить короткий.

<?for($S=kMG;$B>1e3;$I++)$B/=1e3;printf("%.1f{$S[$I-1]}B",$B);

Щоб скористатися ним, перейдіть до POST / GET або встановіть значення в SESSION, на клавіші B .

Залиште ключ Iвідключеним!


1

SpecBAS - 100 байт

Використання конвенції ISU.

Я зрозумів, що встановивши змінну на 1e3 (для її призначення потрібний оператор LET), а потім, використовуючи цю змінну при розробці, фактично використано більше символів, ніж просто жорстке кодування 1e3 там, де це було потрібно.

1 INPUT n: LET i=1
2 DO WHILE n>1e3: LET n=n/1e3: INC i: LOOP 
3 PRINT USING$("&.*0#",n);" kMG"(i);"B"

1

Ruby, 128 байт

c=->i{p i.to_s+'B'if i<1e3;p (i/1e3).to_s+'kB'if i>=1e3&&i<1e6;p (i/1e6).to_s+'MB'if i>=1e6&&i<1e9;p (i/1e9).to_s+'GB'if i>=1e9}

Я зробив це довгий шлях, це досить погано.

Вихідні дані

c[0] # => "0B"
c[999] # => "999B"
c[1000] # => "1.0kB" 
c[1023] # => "1.023kB"
c[1024] # => "1.024kB"
c[1601] # => "1.601kB"
c[160581] # => "160.581kB"
c[4066888] # => "4.066888MB"
c[634000000] # => "634.0MB"
c[2147483647] # => "2.147483647GB"

Редагувати

Додано туберкульоз для додаткових 39 байт

c=->i{p i.to_s+'B'if i<1e3;p (i/1e3).to_s+'kB'if i>=1e3&&i<1e6;p (i/1e6).to_s+'MB'if i>=1e6&&i<1e9;p (i/1e9).to_s+'GB'if i>=1e9&&i<1e12;p (i/1e12).to_s+'TB'if i>=1e12}

Вихід:

c[1000000000000] # => "1.0TB"

1

Сід -r, 218 + 1

Я використовую одиниці СІ; Я думаю, що вибір бінарних одиниць був би сміливою політикою . ;-)

s/(.)((...)+)$/\1z\2/;h;s/[^z]*z?//;s/.../k/g;s/kk/M/;s/Mk/G/;x;s/(z.)[5-9].*/\1c/;s/(z.c?).*/\1/;:;s/9c/c0/;s/zc/cz/;t;s/(^|0)c/1/;s/1c/2/;s/2c/3/;s/3c/4/;s/4c/5/;s/5c/6/;s/6c/7/;s/7c/8/;s/8c/9/;G;s/\n//;s/$/B/;y/z/./

Переформатоване:

#!/bin/sed -rf

# Place decimal point (use z as shorthand for \.)
s/(.)((...)+)$/\1z\2/
h

# count thousands into hold space
s/[^z]*z?//
s/.../k/g
s/kk/M/;s/Mk/G/
x

# truncate to 1 decimal place
s/(z.)[5-9].*/\1c/
s/(z.c?).*/\1/

# propagate carry
:
s/9c/c0/
s/zc/cz/
t
s/(^|0)c/1/
s/1c/2/
s/2c/3/
s/3c/4/
s/4c/5/
s/5c/6/
s/6c/7/
s/7c/8/
s/8c/9/

# Append units
G;s/\n//
s/$/B/
y/z/./

Вихідні дані

1 => 1B
9 => 9B
99 => 99B
999 => 999B
1000 => 1.0kB
9999 => 10.0kB
99949 => 99.9kB
99950 => 100.0kB
99999 => 100.0kB
999999 => 1000.0kB
9999999 => 10.0MB
9999999999 => 10.0GB
1000 => 1.0kB
10000 => 10.0kB
10005 => 10.0kB
10440 => 10.4kB
10450 => 10.5kB
10950 => 11.0kB

Варіації

Правила, мабуть, передбачають, що округлення до найближчого, але для людського відображення я вважаю, що округлення вниз є прийнятною альтернативою і економить 123 байти (краще, ніж 50%):

s/(.)((...)+)$/\1.\2/;h;s/[^\.]*\.?//;s/.../k/g;s/kk/M/;s/Mk/G/;x;s/(\..).*/\1/;G;s/\n//;s/$/B/

Природне розширення до більших одиниць (все ще округлення вниз, 130 + 1 байт):

s/(.)((...)+)$/\1.\2/;h;s/[^\.]*\.?//;s/.../k/g;s/kk/M/g;s/Mk/G/;s/MM/T/g;s/TT/Y/;s/TM/E/;s/TG/Z/;x;s/(\..).*/\1/;G;s/\n//;s/$/B/

Варіаційний вихід:

1 => 1B
9 => 9B
99 => 99B
999 => 999B
1000 => 1.0kB
9999 => 9.9kB
99949 => 99.9kB
99950 => 99.9kB
99999 => 99.9kB
999999 => 999.9kB
9999999 => 9.9MB
9999999999 => 9.9GB
1000 => 1.0kB
10000 => 10.0kB
10005 => 10.0kB
10440 => 10.4kB
10450 => 10.4kB
10950 => 10.9kB
1000000000 => 1.0GB
1000000000000 => 1.0TB
1000000000000000 => 1.0MGB
1000000000000000000 => 1.0EB
1000000000000000000000 => 1.0ZB
1000000000000000000000000 => 1.0YB
999999999999999999999999999 => 999.9YB

Чудова робота! Мені подобається, що ви думали про всі різні варіанти!
Рольф ツ

1

C, 77 75

f(float l){char*u=" kMG";while((l/=1e3)>=1)++u;printf("%.1f%cB",l*1e3,*u);}

Для цього використовуються одиниці SI та використовується опція 1000,0 КБ для округлення.

Розширений код:

f(float l)
{
    char *u = " kMG";
    while ((l/=1000) >= 1)
        ++u;
    printf("%.1f%cB", l*1000, *u);
}

Вихідні дані

9 => 9.0 B
9999 => 10.0kB
1023 => 1.0kB
1024 => 1.0kB
999990 => 1000.0kB
1048575 => 1.0MB
1048576 => 1.0MB
2147483647 => 2.1GB

Варіанти

Щоб отримати двійкові одиниці, змініть 1000на 1024і додайте iдо рядка формату, якщо є множник. Щоб уникнути чотиризначного округлення, порівняйте >=.95замість >=1. Щоб прийняти великі одиниці, продовжте uрядок. Поєднуючи всі ці варіанти, ми отримуємо:

f(float l)
{
    char*u=" kMGTPEZY";
    while((l/=1024)>=.95)++u;
    printf(*u-' '?"%.1f%ciB":"%.0fB",l*1024,*u);
}

Варіантний вихід

9 => 9B
9999 => 9.8kiB
1023 => 1.0kiB
1024 => 1.0kiB
999990 => 1.0MiB
1048575 => 1.0MiB
1048576 => 1.0MiB
2147483647 => 2.0GiB
1000000000 => 953.7MiB
1000000000000 => 931.3GiB
1000000000000000 => 909.5TiB
1000000000000000000 => 888.2PiB
1000000000000000000000 => 867.4EiB
1000000000000000000000000 => 847.0ZiB
999999999999999999999999999 => 827.2YiB
1176043059457204080886151645 => 972.8YiB

Тестова програма

Передайте будь-яку кількість входів як аргументів командного рядка:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    while (*++argv) {
        printf("%s => ", *argv);
        f(strtod(*argv, 0));
        puts("");
    }
    return 0;
}

Гарний;) Добре виконаний!
Рольф ツ

0

Рубін, 91 байт

n=gets.to_i;i=0;while n>1023;n/=1024.0;i+=1;end;puts "#{n.round 1} #{%w[B KiB MiB GiB][i]}"

Я, мабуть, міг би зробити трохи краще, якби постарався більше, але ось що у мене поки що.


Використовуйте 1024.замість 1024.0.
mbomb007

0

Javascript ES5, 69 байт

Це використовує інший спосіб досягти кінцевої мети, на яку відповідь @ edc65 .
Насправді це досить близько до моєї відповіді PHP .

for(i=+prompt(z=0);i>1e3;z++)i/=1e3;alert(i.toFixed(1)+' kMG'[z]+'B')

Просто запустіть фрагмент стека або вставте його на консоль.


0

Рубін, 90 байт

proc{|n|q=((1..3).find{|i|n<(1<<i*10)}||4)-1;[n*10/(1<<q*10)/10.0,%w[B kB MB GB][q]].join}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.