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

收据打印机的格式字符串

商店的收据打印机用一个格式字符串逐行打印金额。
第一行给出格式 T。
T 中用 { 和 } 括起来的部分是占位符,其余字符原样写出。
第二行给出行数 n,接下来 n 行每行给出与占位符个数相同的整数,用一个空格隔开。
对每一行,输出把 T 的占位符从左到右依次换成该行整数后的字符串。
占位符 { } 内的格式依次为 [对齐][+][0][宽度][,],任何部分都可以没有。
全都没有时就是 {}。
对齐是 < (左)、> (右)、^ (居中)之一。
没有时右对齐。
有 + 时,在 0 和正数前加 +。
负数前总是加 -,没有 + 时 0 和正数不加符号。
有 , 时,把数字部分从末尾起每三位用逗号隔开(1234567 → 1,234,567,-123 → -123)。
符号不计入三位。
宽度是 1 到 30 的数,不以 0 开头。
符号、数字、逗号合起来的字符数小于宽度时,用空格补足到宽度。
等于或大于宽度时既不截断也不补。
没有宽度时也不补。
左对齐在右边补空格,右对齐在左边补空格。
居中对齐把要补的空格数的一半(向下取整)补在左边,其余补在右边。
宽度前有 0 时,用 0 代替空格补在符号和数字之间(-42 用格式 {06} → -00042)。
0 只在没有对齐和逗号、并且有宽度时出现。
输出的数除了逗号和符号外只用数字书写。

输入
T 长 4~200 个字符,占位符有 1~10 个。
占位符外的字符是除 { 和 } 以外的 ASCII 字符(编码 32~126)。
T 的第一个和最后一个字符既不是空格也不是 { 或 }。
格式总是按上述规则给出。
1 ≤ n ≤ 100。
整数在 -10^15 到 10^15 之间
输出
n 行
样例 1 · stdin
[{<6}|{>6}|{^6}|{06}|{+}|{,}]
2
42 42 42 -42 0 -1234567
-7 123456 12345 5 9 123
stdout
[42    |    42|  42  |-00042|+0|-1,234,567]
[-7    |123456|12345 |000005|+9|123]
样例 2 · stdin
Total:{>+12,}won
3
1500000
-25000
0
stdout
Total:  +1,500,000won
Total:     -25,000won
Total:          +0won
main.py
输入(stdin)

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

$ 就绪

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

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

格式化输出能做的事各语言不同。
本题的格式是 Python 格式规则(format spec)的一部分,所以 Python 用 format(v, ">+12,") 或 f 字符串 f"{v:>+12,}" 就能直接处理。
C++20 std::format("{:^+8}", v) 规则相同,但没有逗号 ,(L 跟随区域设置)。
C printf("%+08lld", v) 有 -(左)、+、0 和宽度,但没有居中和逗号(' 标志是 POSIX 扩展,跟随区域设置)。
Java String.format("%+,8d", v) 有 -、+、0、,,但没有居中,逗号的样子跟随区域设置,所以传 Locale.ROOT 更安全。
C# 像 {0,8:N0} 那样有宽度(负数表示左对齐)和 N0(三位分组),但没有居中,+ 只能用自定义格式(+#;-#;+0)加上(用 CultureInfo.InvariantCulture 固定逗号)。
JS・TS 用 padStart/padEnd,Craykon 用 左填充/右填充 来补,逗号自己加。
因此逐部分读格式,分别做出符号和数字后再补齐,是所有语言都通用的方法。
陷阱 ① 把 0 补在最前面会让 -42 变成 00-42。
0 要补在符号后面。
② 把符号也计入再加逗号会让 -123 变成 -,123。
逗号只加在绝对值的数字里。
③ 居中时多出的一个空格在右边。
④ + 对 0 也加(+0)。
⑤ 比宽度长的值不截断。
⑥ 值可达 ±10^15,所以 C・C++ 要用 long long,Java・C# 要用 long(int 只到约 21 亿)。
⑦ 在 C 中像 printf(lit) 那样把原样文字放在格式位置,% 会被当作格式读。
要用 fputs 或 printf("%s", lit)。

Pythonmain.py · 12 行
import reimport sys lines = sys.stdin.read().split("\n")template = lines[0]n = int(lines[1])# 홀수 칸이 자리 표시의 서식, 짝수 칸이 그대로 쓸 글자: "a{>5}b" → ["a", ">5", "b"]parts = re.split(r"\{([^}]*)\}", template)for row in lines[2:2 + n]:    values = iter(map(int, row.split()))    # 이 문제의 서식은 Python 서식 규칙(format spec)의 일부라서 format이 그대로 처리한다    print("".join(format(next(values), p) if i % 2 else p for i, p in enumerate(parts)))
JavaScriptmain.js · 39 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const template = lines[0];const n = Number(lines[1]);// 홀수 칸이 자리 표시의 서식, 짝수 칸이 그대로 쓸 글자const parts = template.split(/\{([^}]*)\}/); function format(v, spec) {  let i = 0;  let align = "";  if (i < spec.length && "<>^".includes(spec[i])) align = spec[i++];  let plus = false, zero = false;  if (spec[i] === "+") { plus = true; i++; }  if (spec[i] === "0") { zero = true; i++; }  let width = 0;  while (i < spec.length && spec[i] >= "0" && spec[i] <= "9") width = width * 10 + Number(spec[i++]);  const comma = spec[i] === ",";   let digits = String(Math.abs(v));  if (comma) digits = digits.replace(/\B(?=(\d{3})+$)/g, ","); // 끝에서부터 세 자리마다  const sign = v < 0 ? "-" : plus ? "+" : "";  const s = sign + digits;  const gap = width - s.length;  if (gap <= 0) return s; // 너비보다 길면 자르지 않는다  if (zero) return sign + "0".repeat(gap) + digits; // 0은 부호 뒤에 채운다  if (align === "<") return s.padEnd(width);  if (align === "^") {    const left = Math.floor(gap / 2);    return " ".repeat(left) + s + " ".repeat(gap - left);  }  return s.padStart(width);} const out = [];for (const row of lines.slice(2, 2 + n)) {  const values = row.trim().split(/\s+/).map(Number);  let k = 0;  out.push(parts.map((p, i) => (i % 2 === 1 ? format(values[k++], p) : p)).join(""));}console.log(out.join("\n"));
TypeScriptmain.ts · 41 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const template = lines[0];const n = Number(lines[1]);// 홀수 칸이 자리 표시의 서식, 짝수 칸이 그대로 쓸 글자const parts: string[] = template.split(/\{([^}]*)\}/); function format(v: number, spec: string): string {  let i = 0;  let align = "";  if (i < spec.length && "<>^".includes(spec[i])) align = spec[i++];  let plus = false, zero = false;  if (spec[i] === "+") { plus = true; i++; }  if (spec[i] === "0") { zero = true; i++; }  let width = 0;  while (i < spec.length && spec[i] >= "0" && spec[i] <= "9") width = width * 10 + Number(spec[i++]);  const comma = spec[i] === ",";   let digits = String(Math.abs(v));  if (comma) digits = digits.replace(/\B(?=(\d{3})+$)/g, ","); // 끝에서부터 세 자리마다  const sign = v < 0 ? "-" : plus ? "+" : "";  const s = sign + digits;  const gap = width - s.length;  if (gap <= 0) return s; // 너비보다 길면 자르지 않는다  if (zero) return sign + "0".repeat(gap) + digits; // 0은 부호 뒤에 채운다  if (align === "<") return s.padEnd(width);  if (align === "^") {    const left = Math.floor(gap / 2);    return " ".repeat(left) + s + " ".repeat(gap - left);  }  return s.padStart(width);} const out: string[] = [];for (const row of lines.slice(2, 2 + n)) {  const values = row.trim().split(/\s+/).map(Number);  let k = 0;  out.push(parts.map((p, i) => (i % 2 === 1 ? format(values[k++], p) : p)).join(""));}console.log(out.join("\n"));
JavaMain.java · 57 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.IOException;import java.util.ArrayList;import java.util.List; public class Main {    static String format(long v, String spec) {        int i = 0;        char align = ' ';        if (i < spec.length() && "<>^".indexOf(spec.charAt(i)) >= 0) align = spec.charAt(i++);        boolean plus = false, zero = false;        if (i < spec.length() && spec.charAt(i) == '+') { plus = true; i++; }        if (i < spec.length() && spec.charAt(i) == '0') { zero = true; i++; }        int width = 0;        while (i < spec.length() && Character.isDigit(spec.charAt(i))) width = width * 10 + (spec.charAt(i++) - '0');        boolean comma = i < spec.length() && spec.charAt(i) == ',';         // %,d는 세 자리마다 쉼표를 넣는다(Locale.ROOT로 쉼표를 고정). 부호는 따로 붙인다        String digits = String.format(java.util.Locale.ROOT, comma ? "%,d" : "%d", Math.abs(v));        String sign = v < 0 ? "-" : plus ? "+" : "";        String s = sign + digits;        int gap = width - s.length();        if (gap <= 0) return s; // 너비보다 길면 자르지 않는다        if (zero) return sign + "0".repeat(gap) + digits; // 0은 부호 뒤에 채운다        if (align == '<') return s + " ".repeat(gap);        if (align == '^') return " ".repeat(gap / 2) + s + " ".repeat(gap - gap / 2);        return " ".repeat(gap) + s;    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String template = in.readLine();        // 자리 표시와 그 사이 글자로 나눈다        List<String> lits = new ArrayList<>(), specs = new ArrayList<>();        int from = 0;        while (true) {            int open = template.indexOf('{', from);            if (open < 0) break;            int close = template.indexOf('}', open);            lits.add(template.substring(from, open));            specs.add(template.substring(open + 1, close));            from = close + 1;        }        lits.add(template.substring(from));        int n = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int r = 0; r < n; r++) {            String[] vs = in.readLine().trim().split("\\s+");            for (int k = 0; k < specs.size(); k++) {                out.append(lits.get(k)).append(format(Long.parseLong(vs[k]), specs.get(k)));            }            out.append(lits.get(specs.size())).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 60 行
using System;using System.Collections.Generic;using System.Globalization;using System.Text; class Program{    static string Format(long v, string spec)    {        int i = 0;        char align = ' ';        if (i < spec.Length && "<>^".IndexOf(spec[i]) >= 0) align = spec[i++];        bool plus = false, zero = false;        if (i < spec.Length && spec[i] == '+') { plus = true; i++; }        if (i < spec.Length && spec[i] == '0') { zero = true; i++; }        int width = 0;        while (i < spec.Length && char.IsDigit(spec[i])) width = width * 10 + (spec[i++] - '0');        bool comma = i < spec.Length && spec[i] == ',';         // "N0"은 세 자리마다 묶는다(InvariantCulture라야 쉼표). 부호는 따로 붙인다        long a = Math.Abs(v);        string digits = comma ? a.ToString("N0", CultureInfo.InvariantCulture) : a.ToString(CultureInfo.InvariantCulture);        string sign = v < 0 ? "-" : plus ? "+" : "";        string s = sign + digits;        int gap = width - s.Length;        if (gap <= 0) return s; // 너비보다 길면 자르지 않는다        if (zero) return sign + new string('0', gap) + digits; // 0은 부호 뒤에 채운다        if (align == '<') return s.PadRight(width);        if (align == '^') return new string(' ', gap / 2) + s + new string(' ', gap - gap / 2);        return s.PadLeft(width);    }     static void Main()    {        string template = Console.ReadLine();        var lits = new List<string>();        var specs = new List<string>();        int from = 0;        while (true)        {            int open = template.IndexOf('{', from);            if (open < 0) break;            int close = template.IndexOf('}', open);            lits.Add(template.Substring(from, open - from));            specs.Add(template.Substring(open + 1, close - open - 1));            from = close + 1;        }        lits.Add(template.Substring(from));        int n = int.Parse(Console.ReadLine().Trim());        var sb = new StringBuilder();        for (int r = 0; r < n; r++)        {            string[] vs = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            for (int k = 0; k < specs.Count; k++)                sb.Append(lits[k]).Append(Format(long.Parse(vs[k]), specs[k]));            sb.Append(lits[specs.Count]).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 57 行
#include <cstdlib>#include <iostream>#include <string>#include <vector> // 서식 spec으로 v를 문자열로 만든다std::string format(long long v, const std::string &spec) {    size_t i = 0;    char align = ' ';    if (i < spec.size() && std::string("<>^").find(spec[i]) != std::string::npos) align = spec[i++];    bool plus = false, zero = false;    if (i < spec.size() && spec[i] == '+') { plus = true; i++; }    if (i < spec.size() && spec[i] == '0') { zero = true; i++; }    int width = 0;    while (i < spec.size() && std::isdigit(static_cast<unsigned char>(spec[i]))) width = width * 10 + (spec[i++] - '0');    bool comma = i < spec.size() && spec[i] == ',';     std::string raw = std::to_string(std::llabs(v)), digits;    for (size_t j = 0; j < raw.size(); j++) {        if (comma && j > 0 && (raw.size() - j) % 3 == 0) digits += ','; // 끝에서 세 자리마다        digits += raw[j];    }    std::string sign = v < 0 ? "-" : plus ? "+" : "";    std::string s = sign + digits;    int gap = width - static_cast<int>(s.size());    if (gap <= 0) return s; // 너비보다 길면 자르지 않는다    if (zero) return sign + std::string(gap, '0') + digits; // 0은 부호 뒤에 채운다    if (align == '<') return s + std::string(gap, ' ');    if (align == '^') return std::string(gap / 2, ' ') + s + std::string(gap - gap / 2, ' ');    return std::string(gap, ' ') + s;} int main() {    std::string tmpl;    std::getline(std::cin, tmpl);    std::vector<std::string> lits, specs;    size_t from = 0;    for (size_t open; (open = tmpl.find('{', from)) != std::string::npos;) {        size_t close = tmpl.find('}', open);        lits.push_back(tmpl.substr(from, open - from));        specs.push_back(tmpl.substr(open + 1, close - open - 1));        from = close + 1;    }    lits.push_back(tmpl.substr(from));    int n;    std::cin >> n;    std::string out;    for (int r = 0; r < n; r++) {        for (size_t k = 0; k < specs.size(); k++) {            long long v;            std::cin >> v;            out += lits[k] + format(v, specs[k]);        }        out += lits.back() + '\n';    }    std::cout << out;}
Cmain.c · 61 行
#include <stdio.h>#include <string.h> static char tmpl[256];static const char ZEROS[] = "000000000000000000000000000000"; /* 너비는 30 이하 */ /* 서식 spec으로 v를 out에 쓴다 */static void format(long long v, const char *spec, char *out) {    int i = 0, width = 0, plus = 0, zero = 0, comma = 0;    char align = ' ';    if (spec[i] && strchr("<>^", spec[i])) align = spec[i++];    if (spec[i] == '+') { plus = 1; i++; }    if (spec[i] == '0') { zero = 1; i++; }    while (spec[i] >= '0' && spec[i] <= '9') width = width * 10 + (spec[i++] - '0');    if (spec[i] == ',') comma = 1;     /* 절댓값의 숫자들. v는 ±10^15 이하라 -v가 넘치지 않는다 */    char raw[24], digits[32];    snprintf(raw, sizeof raw, "%lld", v < 0 ? -v : v);    int len = (int)strlen(raw), d = 0;    for (int j = 0; j < len; j++) {        if (comma && j > 0 && (len - j) % 3 == 0) digits[d++] = ','; /* 끝에서 세 자리마다 */        digits[d++] = raw[j];    }    digits[d] = '\0';    const char *sign = v < 0 ? "-" : plus ? "+" : "";    int slen = (int)strlen(sign) + d;    int gap = width - slen;    if (gap < 0) gap = 0; /* 너비보다 길면 자르지 않는다 */    if (zero) sprintf(out, "%s%.*s%s", sign, gap, ZEROS, digits); /* 0은 부호 뒤에 채운다 */    else if (align == '<') sprintf(out, "%s%s%*s", sign, digits, gap, "");    else if (align == '^') sprintf(out, "%*s%s%s%*s", gap / 2, "", sign, digits, gap - gap / 2, "");    else sprintf(out, "%*s%s%s", gap, "", sign, digits);} int main(void) {    fgets(tmpl, sizeof tmpl, stdin);    tmpl[strcspn(tmpl, "\r\n")] = '\0';    int n;    scanf("%d", &n);    for (int r = 0; r < n; r++) {        for (char *p = tmpl; *p;) {            if (*p == '{') {                char spec[16], cell[64];                char *close = strchr(p, '}');                int sl = (int)(close - p - 1);                memcpy(spec, p + 1, sl);                spec[sl] = '\0';                long long v;                scanf("%lld", &v);                format(v, spec, cell);                fputs(cell, stdout);                p = close + 1;            } else {                putchar(*p++); /* 그대로 쓸 글자는 printf의 서식으로 쓰지 않는다(% 때문에) */            }        }        putchar('\n');    }    return 0;}
Craykonmain.cray · 95 行
// 서식 spec으로 v를 문자열로 만든다서식(v, spec){    i = 0;    align = "";    如果 (i < spec.长度 && "<>^".包含(spec[i]))    {        align = spec[i];        i++;    }    plus = 假;    zero = 假;    如果 (i < spec.长度 && spec[i] == "+")    {        plus = 真;        i++;    }    如果 (i < spec.长度 && spec[i] == "0")    {        zero = 真;        i++;    }    width = 0;    当 (i < spec.长度 && "0123456789".包含(spec[i]))    {        width = width * 10 + spec[i].转数字();        i++;    }    comma = i < spec.长度 && spec[i] == ",";     raw = "" + 数学.绝对值(v);    digits = "";    循环 (j = 0; j < raw.长度; j++)    {        如果 (comma && j > 0 && (raw.长度 - j) % 3 == 0)        {            digits = digits + ","; // 끝에서 세 자리마다        }        digits = digits + raw[j];    }    sign = "";    如果 (v < 0)    {        sign = "-";    }    否则 如果 (plus)    {        sign = "+";    }    s = sign + digits;    如果 (s.长度 >= width)    {        返回 s; // 너비보다 길면 자르지 않는다    }    如果 (zero)    {        返回 sign + digits.左填充(width - sign.长度, "0"); // 0은 부호 뒤에 채운다    }    如果 (align == "<")    {        返回 s.右填充(width);    }    如果 (align == "^")    {        left = 数学.商(width - s.长度, 2);        返回 (" ".重复(left) + s).右填充(width);    }    返回 s.左填充(width);} tmpl = 控制台.读行();lits = [];specs = [];rest = tmpl;open = rest.查找位置("{");当 (open >= 0){    close = rest.查找位置("}");    lits.添加(rest.截取(0, open));    specs.添加(rest.截取(open + 1, close - open - 1));    rest = rest.截取(close + 1);    open = rest.查找位置("{");}lits.添加(rest);n = 控制台.读行().转数字();循环 (r = 0; r < n; r++){    vs = 控制台.读数字();    line = "";    循环 (k = 0; k < specs.数量; k++)    {        line = line + lits[k] + 서식(vs[k], specs[k]);    }    控制台.输出(line + lits[specs.数量]);}