{ }codelper
1行読み込み累乗四捨五入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()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 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() を呼ぶと、一行目に残った改行だけを読んで空の文字列が返ります。
Craykon の コンソール.行読み() は行全体を返しますが、最後の空白を探すメンバーがないので、分ける(" ") で分けて最後のかけらをサイズにし、残りを 部分リスト と つなぐ(" ") でつなぎ直します(名前の中の空白は一つずつなので元の名前に戻ります)。
累乗:1024^k は Python・JS・TS 1024 ** k、Java・C# Math.pow/Math.Pow、C pow、C++ std::pow で、後ろの四つは double を返します。
2 の累乗なので値は正確ですが、整数で計算するなら 1024 を掛けていくほうがすっきりします(Craykon には累乗の演算子がないので掛けていきます)。
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・Craykon の実数の割り算でもこの商が正確に出ます。
最後の落とし穴は、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).つなぐ(" ");    コンソール.書く(이름 + ": " + 사람단위(크기));}