{ }codelper
한 줄 읽기거듭제곱반올림P0135

파일 크기를 읽기 쉽게

파일 목록의 크기(바이트)를 사람이 읽기 쉬운 단위로 바꾼다. 첫 줄에 파일 수 n이 주어진다. 다음 n줄에는 한 줄에 파일 이름, 빈칸 하나, 크기 s가 차례로 주어진다. 파일 이름에는 빈칸이 들어 있을 수 있다. 단위는 B, KiB, MiB, GiB, TiB이고, 1 KiB = 1024 B, 1 MiB = 1024 KiB, 1 GiB = 1024 MiB, 1 TiB = 1024 GiB다. s가 1024보다 작으면 "s B"로 쓴다(예: 1023 B). 그렇지 않으면 1 단위의 바이트 수가 s 이하인 가장 큰 단위를 고르고, s를 그 단위로 나눈 값을 소수 첫째 자리까지 반올림해 쓴다. 반올림은 나눈 정확한 값에 가장 가까운 소수 한 자리 수로 하고, 두 수의 정확히 가운데이면 큰 쪽으로 한다(1.25 → 1.3, 1.249… → 1.2). 반올림한 값이 1024.0이 되면 한 단계 큰 단위로 바꿔 1.0으로 쓴다(TiB는 더 큰 단위가 없지만 이 범위에서는 1024.0 TiB가 나오지 않는다). B가 아닌 단위에서는 값을 언제나 소수 한 자리로 쓴다(예: 1.0 KiB, 4.5 GiB). 파일마다 한 줄에 "이름: 크기"를 출력하라.

입력
1 ≤ n ≤ 1,000, 0 ≤ s ≤ 100,000,000,000,000 (10^14), 이름은 1~40글자로 영어 글자·숫자·점·밑줄·빼기표·빈칸으로 되어 있고, 빈칸으로 시작하거나 끝나지 않으며 빈칸이 둘 이어지지 않음
출력
파일마다 "이름: 크기" 한 줄
예시 1 · stdin
5
notes.txt 1023
icon.png 1024
My Photo 2024.jpg 1280
backup part 1.zip 1048575
movie.mkv 4831838208
stdout
notes.txt: 1023 B
icon.png: 1.0 KiB
My Photo 2024.jpg: 1.3 KiB
backup part 1.zip: 1.0 MiB
movie.mkv: 4.5 GiB
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

줄 읽기 함정부터 봐요.
이름에 빈칸이 있으니 낱말 단위로 읽으면 안 되고, 줄 전체를 읽은 뒤 마지막 빈칸에서 잘라요.
Python input().rsplit(" ", 1), JS·TS lastIndexOf(" "), Java BufferedReader.readLine()과 lastIndexOf, C# Console.ReadLine()과 LastIndexOf, C fgets와 strrchr(끝의 줄바꿈은 지워요), C++ std::getline과 rfind예요.
C++ std::cin >> name과 C scanf("%s")는 빈칸에서 멈춰 "My Photo 2024.jpg"가 "My"가 돼요.
Java Scanner로 nextInt() 뒤에 nextLine()을 부르면 첫 줄에 남은 줄바꿈만 읽어 빈 문자열이 나와요.
크레이콘 콘솔.줄읽기()는 줄 전체를 돌려주는데 마지막 빈칸을 찾는 멤버가 없어서, 나누기(" ")로 쪼갠 뒤 마지막 조각을 크기로 쓰고 나머지를 부분목록과 잇기(" ")로 다시 붙여요(이름 안의 빈칸이 하나씩이라 원래 이름이 돌아와요).
거듭제곱: 1024^k는 Python·JS·TS 1024 ** k, Java·C# Math.pow/Math.Pow, C pow, C++ std::pow인데 뒤의 넷은 double을 돌려줘요.
2의 거듭제곱이라 값은 정확하지만, 정수로 계산할 거라면 1024를 곱해 가는 편이 깔끔해요(크레이콘은 거듭제곱 연산자가 없어서 곱해 가요).
1024^4 = 2^40이라 32비트 int로는 넘쳐서 Java·C#은 long, C·C++은 long long이에요.
반올림 함정: 1280 B는 정확히 1.25 KiB예요.
Python f"{x:.1f}"·round(x, 1)과 C·C++ printf("%.1f")는 정확한 가운데를 짝수 쪽으로 보내 1.2를 내고, JS toFixed(1)은 1.3을 내요.
이렇게 언어마다 다르니 반올림을 정수로 직접 해요.
값 × 10을 반올림한 수는 (20 × s + d) ÷ (2 × d)의 몫(d = 1024^k)이고, 가운데는 늘 올라가요.
d가 2의 거듭제곱이라 JS·TS·크레이콘의 실수 나눗셈도 이 몫을 정확히 내요.
마지막 함정은 1048575 B처럼 1023.999… KiB가 반올림으로 1024.0이 되는 경우예요.
이때 단위를 하나 올려 1.0 MiB로 써야 해요.

Pythonmain.py · 23 줄
UNITS = ["B", "KiB", "MiB", "GiB", "TiB"]  def human(s):    if s < 1024:        return f"{s} B"    k = 1    while k < 4 and 1024 ** (k + 1) <= s:        k += 1    d = 1024 ** k  # 정수 거듭제곱이라 정확하다    t = (20 * s + d) // (2 * d)  # 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림)    if t >= 10240 and k < 4:  # 1024.0이 되면 다음 단위로        k += 1        d *= 1024        t = (20 * s + d) // (2 * d)    return f"{t // 10}.{t % 10} {UNITS[k]}"  n = int(input())for _ in range(n):    line = input()    name, size = line.rsplit(" ", 1)  # 이름에 빈칸이 있을 수 있어 마지막 빈칸에서 자른다    print(f"{name}: {human(int(size))}")
JavaScriptmain.js · 24 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const UNITS = ["B", "KiB", "MiB", "GiB", "TiB"];const human = (s) => {  if (s < 1024) return s + " B";  let k = 1;  while (k < 4 && 1024 ** (k + 1) <= s) k++;  let d = 1024 ** k; // 2의 거듭제곱이라 number로 정확하다  let t = Math.floor((20 * s + d) / (2 * d)); // 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림)  if (t >= 10240 && k < 4) {    // 1024.0이 되면 다음 단위로    k++;    d *= 1024;    t = Math.floor((20 * s + d) / (2 * d));  }  return Math.floor(t / 10) + "." + (t % 10) + " " + UNITS[k];};const n = Number(lines[0]);const out = [];for (let i = 1; i <= n; i++) {  const line = lines[i];  const cut = line.lastIndexOf(" "); // 이름에 빈칸이 있을 수 있어 마지막 빈칸에서 자른다  out.push(line.slice(0, cut) + ": " + human(Number(line.slice(cut + 1))));}console.log(out.join("\n"));
TypeScriptmain.ts · 25 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const UNITS: string[] = ["B", "KiB", "MiB", "GiB", "TiB"];const human = (s: number): string => {  if (s < 1024) return s + " B";  let k = 1;  while (k < 4 && 1024 ** (k + 1) <= s) k++;  let d: number = 1024 ** k; // 2의 거듭제곱이라 number로 정확하다  let t: number = Math.floor((20 * s + d) / (2 * d)); // 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림)  if (t >= 10240 && k < 4) {    // 1024.0이 되면 다음 단위로    k++;    d *= 1024;    t = Math.floor((20 * s + d) / (2 * d));  }  return Math.floor(t / 10) + "." + (t % 10) + " " + UNITS[k];};const n = Number(lines[0]);const out: string[] = [];for (let i = 1; i <= n; i++) {  const line: string = lines[i];  const cut: number = line.lastIndexOf(" "); // 이름에 빈칸이 있을 수 있어 마지막 빈칸에서 자른다  out.push(line.slice(0, cut) + ": " + human(Number(line.slice(cut + 1))));}console.log(out.join("\n"));
JavaMain.java · 35 줄
import java.io.BufferedReader;import java.io.InputStreamReader; public class Main {    static final String[] UNITS = {"B", "KiB", "MiB", "GiB", "TiB"};     static String human(long s) {        if (s < 1024) return s + " B";        int k = 1;        long d = 1024;        while (k < 4 && d * 1024 <= s) { // 1024를 곱해 가며 거듭제곱(long이라 넘치지 않는다)            d *= 1024;            k++;        }        long t = (20 * s + d) / (2 * d); // 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림)        if (t >= 10240 && k < 4) { // 1024.0이 되면 다음 단위로            k++;            d *= 1024;            t = (20 * s + d) / (2 * d);        }        return t / 10 + "." + t % 10 + " " + UNITS[k];    }     public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            String line = in.readLine(); // 줄 전체를 읽는다            int cut = line.lastIndexOf(' '); // 이름에 빈칸이 있을 수 있어 마지막 빈칸에서 자른다            out.append(line, 0, cut).append(": ").append(human(Long.parseLong(line.substring(cut + 1)))).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 40 줄
using System;using System.Text; class Program{    static readonly string[] Units = { "B", "KiB", "MiB", "GiB", "TiB" };     static string Human(long s)    {        if (s < 1024) return s + " B";        int k = 1;        long d = 1024;        while (k < 4 && d * 1024 <= s) // 1024를 곱해 가며 거듭제곱(long이라 넘치지 않는다)        {            d *= 1024;            k++;        }        long t = (20 * s + d) / (2 * d); // 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림)        if (t >= 10240 && k < 4) // 1024.0이 되면 다음 단위로        {            k++;            d *= 1024;            t = (20 * s + d) / (2 * d);        }        return $"{t / 10}.{t % 10} {Units[k]}";    }     static void Main()    {        int n = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            string line = Console.ReadLine(); // 줄 전체를 읽는다            int cut = line.LastIndexOf(' '); // 이름에 빈칸이 있을 수 있어 마지막 빈칸에서 자른다            sb.Append(line, 0, cut).Append(": ").Append(Human(long.Parse(line.Substring(cut + 1)))).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 32 줄
#include <iostream>#include <string> const std::string UNITS[] = {"B", "KiB", "MiB", "GiB", "TiB"}; std::string human(long long s) {    if (s < 1024) return std::to_string(s) + " B";    int k = 1;    long long d = 1024;    while (k < 4 && d * 1024 <= s) { // 1024를 곱해 가며 거듭제곱(long long이라 넘치지 않는다)        d *= 1024;        k++;    }    long long t = (20 * s + d) / (2 * d); // 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림)    if (t >= 10240 && k < 4) {             // 1024.0이 되면 다음 단위로        k++;        d *= 1024;        t = (20 * s + d) / (2 * d);    }    return std::to_string(t / 10) + "." + std::to_string(t % 10) + " " + UNITS[k];} int main() {    std::string line;    std::getline(std::cin, line);    int n = std::stoi(line);    for (int i = 0; i < n; i++) {        std::getline(std::cin, line); // 줄 전체를 읽는다(>>는 빈칸에서 멈춘다)        auto cut = line.rfind(' ');   // 이름에 빈칸이 있을 수 있어 마지막 빈칸에서 자른다        std::cout << line.substr(0, cut) << ": " << human(std::stoll(line.substr(cut + 1))) << '\n';    }}
Cmain.c · 40 줄
#include <stdio.h>#include <stdlib.h>#include <string.h> static const char* UNITS[] = {"B", "KiB", "MiB", "GiB", "TiB"}; void print_human(long long s) {    if (s < 1024) {        printf("%lld B\n", s);        return;    }    int k = 1;    long long d = 1024;    while (k < 4 && d * 1024 <= s) { /* 1024를 곱해 가며 거듭제곱(long long이라 넘치지 않는다) */        d *= 1024;        k++;    }    long long t = (20 * s + d) / (2 * d); /* 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림) */    if (t >= 10240 && k < 4) {             /* 1024.0이 되면 다음 단위로 */        k++;        d *= 1024;        t = (20 * s + d) / (2 * d);    }    printf("%lld.%lld %s\n", t / 10, t % 10, UNITS[k]);} int main(void) {    char line[128];    fgets(line, sizeof line, stdin);    int n = atoi(line);    for (int i = 0; i < n; i++) {        fgets(line, sizeof line, stdin); /* 줄 전체를 읽는다(끝의 줄바꿈까지) */        line[strcspn(line, "\n")] = '\0';        char* cut = strrchr(line, ' '); /* 이름에 빈칸이 있을 수 있어 마지막 빈칸에서 자른다 */        *cut = '\0';        printf("%s: ", line);        print_human(atoll(cut + 1));    }    return 0;}
Craykonmain.cray · 40 줄
UNITS = ["B", "KiB", "MiB", "GiB", "TiB"]; 반올림열배(s, d){    // 소수 첫째 자리까지 반올림한 값 × 10 (0.5는 올림). d가 2의 거듭제곱이라 나눗셈이 정확하다    돌려주기 수학.몫(20 * s + d, 2 * d);} 사람단위(s){    만약 (s < 1024)    {        돌려주기 s + " B";    }    k = 1;    d = 1024;    동안 (k < 4 && d * 1024 <= s)    {        d *= 1024; // 거듭제곱 연산자가 없으니 1024를 곱해 간다        k += 1;    }    t = 반올림열배(s, d);    만약 (t >= 10240 && k < 4)    {        k += 1; // 1024.0이 되면 다음 단위로        d *= 1024;        t = 반올림열배(s, d);    }    돌려주기 수학.몫(t, 10) + "." + t % 10 + " " + UNITS[k];} n = 콘솔.줄읽기().수로변환();반복 (i = 0; i < n; i++){    // 이름에 빈칸이 있을 수 있어 줄 전체를 읽고 마지막 조각을 크기로 쓴다(이름 안의 빈칸은 하나씩)    조각 = 콘솔.줄읽기().나누기(" ");    크기 = 조각[조각.개수 - 1].수로변환();    이름 = 조각.부분목록(0, 조각.개수 - 1).잇기(" ");    콘솔.쓰기(이름 + ": " + 사람단위(크기));}