{ }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.個数]);}