{ }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()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 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).连接(" ");    控制台.输出(이름 + ": " + 사람단위(크기));}