{ }codelper
补齐对齐格式化输出P0096

制作成绩表

第一行给出学生人数 n,接下来 n 行每行给出“姓名 语文 英语 数学”。
输出一个有 NAME、KOR、ENG、MATH、AVG 五列的表。
AVG 是三个分数的平均值,四舍五入到小数点后一位(275 / 3 → 91.7,171 / 3 → 57.0)。
- 每列的宽度是该列标题和该列所有值中最长者的字符数。
- 第一行是标题;第二行把每列宽度个 "-" 用 "-+-" 连起来;之后每个学生一行(按输入顺序)。
- 标题行和学生行在列与列之间放 " | "(空格、竖线、空格)。
NAME 列左对齐(后面补空格),其他列右对齐(前面补空格)。
标题也遵循同样的规则。

输入
1 ≤ n ≤ 20,姓名为 1~15 个英文字母,0 ≤ 每个分数 ≤ 100
输出
n + 2 行的表
样例 1 · stdin
3
Kim 90 85 100
Alexander 7 100 64
Bo 100 100 100
stdout
NAME      | KOR | ENG | MATH |   AVG
----------+-----+-----+------+------
Kim       |  90 |  85 |  100 |  91.7
Alexander |   7 | 100 |   64 |  57.0
Bo        | 100 | 100 |  100 | 100.0
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

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

其他语言怎么写?查看各语言题解 · 8 种语言题解

先量出每列的宽度(包括标题),再对齐输出。
漏掉标题的话,所有姓名都很短时 NAME 会超出列宽。
在运行时设定宽度的方法因语言而异:Python f"{s:<{w}}"/f"{s:>{w}}",C printf("%-*s", w, s)/"%*s"(在 * 处传入宽度),C++ std::left/std::right 加 std::setw(w),Java String.format("%-" + w + "s", s),JS/TS padEnd/padStart,C# PadRight/PadLeft(C# 的 {0,-5} 要求宽度是常量)。
平均值必须像 / 3.0 这样按实数除。
在 C、C++、Java、C# 中 (a + b + c) / 3 是整数除法,91.7 会变成 91。
Craykon 用 右填充(宽度)/左填充(宽度) 对齐,用 字符.保留小数(合计 / 3, 1) 保留一位小数,用 "-".重复(w) 和 连接("-+-") 做出横线。

Pythonmain.py · 22 行
n = int(input())headers = ["NAME", "KOR", "ENG", "MATH", "AVG"]rows = []for _ in range(n):    name, *scores = input().split()    a, b, c = map(int, scores)    rows.append([name, str(a), str(b), str(c), f"{(a + b + c) / 3:.1f}"]) # 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것widths = [max(len(r[i]) for r in [headers] + rows) for i in range(5)]  def line(cells):    # 이름은 왼쪽 맞춤, 나머지는 오른쪽 맞춤    parts = [f"{cells[0]:<{widths[0]}}"] + [f"{cells[i]:>{widths[i]}}" for i in range(1, 5)]    return " | ".join(parts)  print(line(headers))print("-+-".join("-" * w for w in widths))for r in rows:    print(line(r))
JavaScriptmain.js · 15 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const headers = ["NAME", "KOR", "ENG", "MATH", "AVG"];const rows = [];for (let i = 1; i <= n; i++) {  const [name, ...rest] = lines[i].trim().split(/\s+/);  const [a, b, c] = rest.map(Number);  rows.push([name, String(a), String(b), String(c), ((a + b + c) / 3).toFixed(1)]);}// 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것const widths = headers.map((_, i) => Math.max(...[headers, ...rows].map((r) => r[i].length)));// 이름은 왼쪽 맞춤(padEnd), 나머지는 오른쪽 맞춤(padStart)const line = (cells) => cells.map((s, i) => (i === 0 ? s.padEnd(widths[i]) : s.padStart(widths[i]))).join(" | ");const out = [line(headers), widths.map((w) => "-".repeat(w)).join("-+-"), ...rows.map(line)];console.log(out.join("\n"));
TypeScriptmain.ts · 16 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);const headers: string[] = ["NAME", "KOR", "ENG", "MATH", "AVG"];const rows: string[][] = [];for (let i = 1; i <= n; i++) {  const [name, ...rest] = lines[i].trim().split(/\s+/);  const [a, b, c] = rest.map(Number);  rows.push([name, String(a), String(b), String(c), ((a + b + c) / 3).toFixed(1)]);}// 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것const widths: number[] = headers.map((_, i) => Math.max(...[headers, ...rows].map((r) => r[i].length)));// 이름은 왼쪽 맞춤(padEnd), 나머지는 오른쪽 맞춤(padStart)const line = (cells: string[]): string => cells.map((s, i) => (i === 0 ? s.padEnd(widths[i]) : s.padStart(widths[i]))).join(" | ");const out: string[] = [line(headers), widths.map((w) => "-".repeat(w)).join("-+-"), ...rows.map(line)];console.log(out.join("\n"));
JavaMain.java · 47 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import java.util.Locale; public class Main {    static int[] widths = new int[5];     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(in.readLine().trim());        String[] headers = {"NAME", "KOR", "ENG", "MATH", "AVG"};        List<String[]> rows = new ArrayList<>();        rows.add(headers);        for (int i = 0; i < n; i++) {            String[] t = in.readLine().trim().split("\\s+");            int a = Integer.parseInt(t[1]), b = Integer.parseInt(t[2]), c = Integer.parseInt(t[3]);            // Locale.ROOT: 소수점이 쉼표인 지역 설정에서도 91.7로 찍게            String avg = String.format(Locale.ROOT, "%.1f", (a + b + c) / 3.0);            rows.add(new String[] {t[0], t[1], t[2], t[3], avg});        }        // 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것        for (String[] r : rows)            for (int i = 0; i < 5; i++) widths[i] = Math.max(widths[i], r[i].length());        StringBuilder out = new StringBuilder();        for (int k = 0; k < rows.size(); k++) {            out.append(line(rows.get(k))).append('\n');            if (k == 0) {                for (int i = 0; i < 5; i++) {                    if (i > 0) out.append("-+-");                    out.append("-".repeat(widths[i]));                }                out.append('\n');            }        }        System.out.print(out);    }     // 이름은 왼쪽 맞춤(%-Ns), 나머지는 오른쪽 맞춤(%Ns)    static String line(String[] cells) {        StringBuilder sb = new StringBuilder(String.format("%-" + widths[0] + "s", cells[0]));        for (int i = 1; i < 5; i++) sb.append(" | ").append(String.format("%" + widths[i] + "s", cells[i]));        return sb.toString();    }}
C#Main.cs · 30 行
using System;using System.Collections.Generic;using System.Globalization;using System.Linq; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine());        string[] headers = { "NAME", "KOR", "ENG", "MATH", "AVG" };        var rows = new List<string[]> { headers };        for (int i = 0; i < n; i++)        {            string[] t = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            int a = int.Parse(t[1]), b = int.Parse(t[2]), c = int.Parse(t[3]);            // InvariantCulture: 소수점이 쉼표인 문화권 설정에서도 91.7로 찍게            string avg = ((a + b + c) / 3.0).ToString("F1", CultureInfo.InvariantCulture);            rows.Add(new[] { t[0], t[1], t[2], t[3], avg });        }        // 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것        int[] widths = Enumerable.Range(0, 5).Select(i => rows.Max(r => r[i].Length)).ToArray();        // 이름은 왼쪽 맞춤(PadRight), 나머지는 오른쪽 맞춤(PadLeft)        string Line(string[] cells) =>            string.Join(" | ", cells.Select((s, i) => i == 0 ? s.PadRight(widths[i]) : s.PadLeft(widths[i])));        Console.WriteLine(Line(headers));        Console.WriteLine(string.Join("-+-", widths.Select(w => new string('-', w))));        foreach (var r in rows.Skip(1)) Console.WriteLine(Line(r));    }}
C++main.cpp · 34 行
#include <algorithm>#include <iomanip>#include <iostream>#include <sstream>#include <string>#include <vector> int main() {    int n;    std::cin >> n;    std::vector<std::vector<std::string>> rows = {{"NAME", "KOR", "ENG", "MATH", "AVG"}};    for (int i = 0; i < n; i++) {        std::string name;        int a, b, c;        std::cin >> name >> a >> b >> c;        std::ostringstream avg;        avg << std::fixed << std::setprecision(1) << (a + b + c) / 3.0;        rows.push_back({name, std::to_string(a), std::to_string(b), std::to_string(c), avg.str()});    }    // 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것    std::vector<std::size_t> widths(5, 0);    for (const auto& r : rows)        for (int i = 0; i < 5; i++) widths[i] = std::max(widths[i], r[i].size());    auto print_row = [&](const std::vector<std::string>& r) {        // 이름은 왼쪽 맞춤(std::left), 나머지는 오른쪽 맞춤(std::right)        std::cout << std::left << std::setw(widths[0]) << r[0];        for (int i = 1; i < 5; i++) std::cout << " | " << std::right << std::setw(widths[i]) << r[i];        std::cout << '\n';    };    print_row(rows[0]);    for (int i = 0; i < 5; i++) std::cout << (i > 0 ? "-+-" : "") << std::string(widths[i], '-');    std::cout << '\n';    for (std::size_t k = 1; k < rows.size(); k++) print_row(rows[k]);}
Cmain.c · 40 行
#include <stdio.h>#include <string.h> static char cells[21][5][16];static int widths[5]; // 이름은 왼쪽 맞춤(%-*s), 나머지는 오른쪽 맞춤(%*s) — *에 너비를 넘긴다static void print_row(int r) {    printf("%-*s", widths[0], cells[r][0]);    for (int i = 1; i < 5; i++) printf(" | %*s", widths[i], cells[r][i]);    printf("\n");} int main(void) {    int n, a, b, c;    const char *headers[5] = {"NAME", "KOR", "ENG", "MATH", "AVG"};    for (int i = 0; i < 5; i++) strcpy(cells[0][i], headers[i]);    scanf("%d", &n);    for (int r = 1; r <= n; r++) {        scanf("%15s %d %d %d", cells[r][0], &a, &b, &c);        sprintf(cells[r][1], "%d", a);        sprintf(cells[r][2], "%d", b);        sprintf(cells[r][3], "%d", c);        sprintf(cells[r][4], "%.1f", (a + b + c) / 3.0);    }    // 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것    for (int r = 0; r <= n; r++)        for (int i = 0; i < 5; i++) {            int len = strlen(cells[r][i]);            if (len > widths[i]) widths[i] = len;        }    print_row(0);    for (int i = 0; i < 5; i++) {        if (i > 0) printf("-+-");        for (int k = 0; k < widths[i]; k++) putchar('-');    }    printf("\n");    for (int r = 1; r <= n; r++) print_row(r);    return 0;}
Craykonmain.cray · 39 行
// 이름은 왼쪽 맞춤(뒤채우기), 나머지는 오른쪽 맞춤(앞채우기)줄만들기(cells){    line = cells[0].右填充(widths[0]);    循环 (i = 1; i < 5; i++)    {        line += " | " + cells[i].左填充(widths[i]);    }    返回 line;} n = 控制台.读行().转数字();rows = [["NAME", "KOR", "ENG", "MATH", "AVG"]];循环 (r = 0; r < n; r++){    t = 控制台.读行().分割();    합 = t[1].转数字() + t[2].转数字() + t[3].转数字();    rows.添加([t[0], t[1], t[2], t[3], 字符.保留小数(합 / 3, 1)]);}// 칸 너비 = 머리글과 그 칸 값들 가운데 가장 긴 것widths = 新列表(5, 0);遍历 (row 在 rows){    循环 (i = 0; i < 5; i++)    {        widths[i] = 数学.较大值(widths[i], row[i].长度);    }}dashes = [];遍历 (w 在 widths){    dashes.添加("-".重复(w));}控制台.输出(줄만들기(rows[0]));控制台.输出(dashes.连接("-+-"));循环 (r = 1; r < rows.数量; r++){    控制台.输出(줄만들기(rows[r]));}