{ }codelper
数の読み込みEOFまで読む64ビット整数P0392

カンマとコメントが混ざった測定記録を読む

実験室の装置が測定値を 1 回につき 1 行ずつ書いた記録が、入力の終わりまで与えられる。
行数も、1 行にある数の個数も、前もっては与えられない。
1 行の中で最初に '#' が現れるところからその行の終わりまではコメントであり、コメントの中に書かれた数は数えない。
コメントを除いた残りには整数が 0 個以上ある。
整数と整数の間には空白、タブ、カンマが 1 つ以上混ざっており、行の先頭と末尾にもこれらの文字があることがある。
カンマが重なっていても、その間に値があるという意味ではない。
整数は 10 進数で、先頭に符号 '+' か '-' が付くことがあり、0 で始まることもある。
したがって 007 は 7、-012 は −12、+5 は 5 である。
整数が 1 つでもある行だけが記録である。
空行、空白・タブ・カンマだけの行、コメントだけの行は記録ではないので読み飛ばし、その後にも記録が続くことがある。
記録ごとに入力に現れた順に 1 行で、その行の整数の個数と合計を空白 1 つで区切って出力せよ。
最後の行には、記録の数、すべての記録の整数の個数の合計、すべての整数の合計を空白 1 つずつで区切って出力せよ。
記録が 1 つもなければ "0 0 0" の 1 行だけを出力する。

入力
入力は 1〜1,000 行で、1 行は改行を除いて 200 文字以下である。
整数は符号を除いた数字が 1〜12 個で、値は −100,000,000,000 以上 100,000,000,000 以下である。
符号は 1 つの整数に多くても 1 つで、その整数の先頭にだけ現れ、符号と数字の間には何の文字もない。
最後の行を含むすべての行は改行で終わる。
コメント以外のところには数字、'+'、'-'、空白、タブ、カンマだけが現れ、コメントには表示可能な ASCII 文字が現れる
出力
記録ごとに「個数 合計」を 1 行、最後の行に「記録の数 全体の個数 全体の合計」。
数はカンマなしの数字だけで書く
例 1 · stdin
# run 7 (2 sensors)
12, 007 +5
  -3,,4 # 99 is ignored

100000000000 100000000000,
,,
08 010
stdout
3 24
2 1
2 200000000000
2 18
4 9 200000000043
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

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

ほかの言語では?言語別の解答を見る · 8 言語の解答

1 行に数がいくつあるか分からないので、行の終わりがどこかを知る必要がある問題です。
C scanf("%lld")、C++ std::cin >> x、Java Scanner.nextLong() は空白も改行も同じように読み飛ばすので、行の終わりが分かりません(カンマやコメントでも止まります)。
そこでまず 1 行を読み(Python for line in sys.stdin、Java readLine()、C# Console.ReadLine()、C fgets、C++ getline、Craykon コンソール.行読み()、JS・TS はまとめて読んで split("\n"))、その行を切り分けます。
切り分ける道具ごとに空の切れ端の扱いが違います。
Python split() と Craykon 分ける() は引数がなければ空白・タブのかたまりを一度に飛ばすので、カンマを空白に変えておくだけで済みます(replace(",", " ")、置き換え(",", " "))。
C strtok(line, " \t,\r\n") と Java StringTokenizer(data, " \t,") も空の切れ端を作らず、C# は Split(seps, StringSplitOptions.RemoveEmptyEntries) と書く必要があります。
C++ はカンマを空白に変えてから std::istringstream で >> を使って読みます。
落とし穴その一:JS・TS split(/[ \t,]+/) は行の先頭・末尾の区切り文字の隣に空文字列 "" を残し、Number("") はエラーなしで 0 です。
取り除かないと例の 3 行目が 2 1 ではなく 4 1 になり、空行も 1 0 と出ます。
filter((t) => t !== "") で取り除きます。
落とし穴その二:Java split("[ \t,]+") は末尾の空文字列だけを捨てて先頭のものは残すので、例の 3 行目で Long.parseLong("") が NumberFormatException で止まります。
落とし穴その三:C strtoll(tok, NULL, 0) のように基数に 0 を渡すと、0 で始まる数を 8 進数として読みます。
例の最後の行の 010 が 8 になり、08 は 0 で止まるので、2 18 が 2 8 になります。
基数には 10 を渡します。
Python int、JS・TS Number、Java Long.parseLong、C# long.Parse、C++ >>、Craykon 数に() は 007 と +5 を 10 進数の 7 と 5 として読みます。
落とし穴その四:コメントを切り捨てずに行全体から数を探すと(Python re.findall)、例の 1 行目の 7 と 2、3 行目の 99 まで数えます。
落とし穴その五:合計が約 21 億を超えます。
C・C++ は long long、Java・C# は long に足します。
Java で int sum に sum += Long.parseLong(t) と書くと、コンパイルエラーなしで int に切り詰められ、例の 4 番目の記録の合計 200000000000 が -1863462912 になります。
Python には大きさの制限がなく、JS・TS・Craykon の数は 2^53 まで正確なので、この問題で最大の全体の合計 1.5 × 10^15 もそのまま扱えます。
落とし穴その六:数のない行を読み飛ばさないと 0 0 が出力され、記録の数が増えます。
落とし穴その七:空行は入力の終わりではありません。
空行で止めると例の後ろの 2 つの記録を逃します。
入力の終わりは Java・C# の null、C fgets の NULL、C++ getline の偽、Craykon の なし で分かります。

Pythonmain.py · 16 行
import sys rows = count_all = total = 0out = []for line in sys.stdin:                 # 입력이 끝날 때까지 한 줄씩    data = line.split("#")[0]          # '#'부터는 주석    # 쉼표를 빈칸으로 바꾸면 split()이 빈칸·탭·쉼표 덩이를 한 번에 건너뛴다    nums = [int(t) for t in data.replace(",", " ").split()]    if not nums:                       # 수가 없는 줄은 기록이 아니다        continue    rows += 1    count_all += len(nums)    total += sum(nums)    out.append(f"{len(nums)} {sum(nums)}")out.append(f"{rows} {count_all} {total}")print("\n".join(out))
JavaScriptmain.js · 19 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");let rows = 0;let countAll = 0;let total = 0;const out = [];for (const line of lines) {  const data = line.split("#")[0]; // '#'부터는 주석  // 나눈 뒤 빈 조각 ""을 꼭 걸러 낸다. Number("")는 오류 없이 0이다  const nums = data.split(/[ \t,]+/).filter((t) => t !== "").map(Number);  if (nums.length === 0) continue; // 수가 없는 줄은 기록이 아니다  let sum = 0;  for (const x of nums) sum += x;  rows++;  countAll += nums.length;  total += sum;  out.push(`${nums.length} ${sum}`);}out.push(`${rows} ${countAll} ${total}`);console.log(out.join("\n"));
TypeScriptmain.ts · 20 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");let rows = 0;let countAll = 0;let total = 0;const out: string[] = [];for (const line of lines) {  const data: string = line.split("#")[0]; // '#'부터는 주석  // 나눈 뒤 빈 조각 ""을 꼭 걸러 낸다. Number("")는 오류 없이 0이다  const nums: number[] = data.split(/[ \t,]+/).filter((t) => t !== "").map(Number);  if (nums.length === 0) continue; // 수가 없는 줄은 기록이 아니다  let sum = 0;  for (const x of nums) sum += x;  rows++;  countAll += nums.length;  total += sum;  out.push(`${nums.length} ${sum}`);}out.push(`${rows} ${countAll} ${total}`);console.log(out.join("\n"));
JavaMain.java · 33 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int rows = 0;        int countAll = 0;        long total = 0;        StringBuilder out = new StringBuilder();        String line;        while ((line = in.readLine()) != null) { // 입력이 끝나면 null            int hash = line.indexOf('#');            String data = hash >= 0 ? line.substring(0, hash) : line; // '#'부터는 주석            // StringTokenizer는 구분 글자가 겹치거나 맨 앞에 있어도 빈 조각을 만들지 않는다            StringTokenizer st = new StringTokenizer(data, " \t,");            int count = 0;            long sum = 0;            while (st.hasMoreTokens()) {                sum += Long.parseLong(st.nextToken());                count++;            }            if (count == 0) continue; // 수가 없는 줄은 기록이 아니다            rows++;            countAll += count;            total += sum;            out.append(count).append(' ').append(sum).append('\n');        }        out.append(rows).append(' ').append(countAll).append(' ').append(total).append('\n');        System.out.print(out);    }}
C#Main.cs · 29 行
using System;using System.Text; class Program{    static void Main()    {        int rows = 0, countAll = 0;        long total = 0;        var sb = new StringBuilder();        char[] seps = { ' ', '\t', ',' };        string line;        while ((line = Console.ReadLine()) != null) // 입력이 끝나면 null        {            string data = line.Split('#')[0]; // '#'부터는 주석            // RemoveEmptyEntries가 없으면 구분 글자가 겹친 곳마다 빈 문자열이 생긴다            string[] parts = data.Split(seps, StringSplitOptions.RemoveEmptyEntries);            if (parts.Length == 0) continue; // 수가 없는 줄은 기록이 아니다            long sum = 0;            foreach (string t in parts) sum += long.Parse(t);            rows++;            countAll += parts.Length;            total += sum;            sb.Append(parts.Length).Append(' ').Append(sum).Append('\n');        }        sb.Append(rows).Append(' ').Append(countAll).Append(' ').Append(total).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 30 行
#include <algorithm>#include <iostream>#include <sstream>#include <string>using namespace std; int main() {    int rows = 0, countAll = 0;    long long total = 0;    string line;    // cin >> x 는 줄 바꿈도 건너뛰어서 줄의 끝을 알 수 없다. getline으로 한 줄씩 읽는다    while (getline(cin, line)) {        line = line.substr(0, line.find('#')); // '#'부터는 주석(없으면 npos라 줄 전체)        replace(line.begin(), line.end(), ',', ' ');        istringstream in(line);        int count = 0;        long long sum = 0, x;        while (in >> x) { // 빈칸·탭 덩이를 건너뛰며 수를 하나씩 읽는다            sum += x;            count++;        }        if (count == 0) continue; // 수가 없는 줄은 기록이 아니다        rows++;        countAll += count;        total += sum;        cout << count << ' ' << sum << '\n';    }    cout << rows << ' ' << countAll << ' ' << total << '\n';    return 0;}
Cmain.c · 27 行
#include <stdio.h>#include <stdlib.h>#include <string.h> int main(void) {    char line[256];    int rows = 0, count_all = 0;    long long total = 0;    /* scanf("%lld")는 줄 바꿈도 건너뛰어서 줄의 끝을 알 수 없다. 한 줄씩 읽어서 나눈다 */    while (fgets(line, sizeof line, stdin)) {        char *hash = strchr(line, '#'); /* '#'부터는 주석 */        if (hash) *hash = '\0';        int count = 0;        long long sum = 0;        for (char *tok = strtok(line, " \t,\r\n"); tok; tok = strtok(NULL, " \t,\r\n")) {            sum += strtoll(tok, NULL, 10); /* 꼭 10진법으로: 0을 주면 010을 8진수로 읽는다 */            count++;        }        if (count == 0) continue; /* 수가 없는 줄은 기록이 아니다 */        rows++;        count_all += count;        total += sum;        printf("%d %lld\n", count, sum);    }    printf("%d %d %lld\n", rows, count_all, total);    return 0;}
Craykonmain.cray · 28 行
rows = 0;countAll = 0;total = 0;間 (真){    line = コンソール.行読み();    もし (line == なし)    {        抜ける;    }    data = line.分ける("#")[0]; // '#'부터는 주석    // 쉼표를 빈칸으로 바꾸면 나누기()가 빈칸·탭·쉼표 덩이를 한 번에 건너뛴다    parts = data.置き換え(",", " ").分ける();    もし (parts.個数 == 0)    {        次へ;    }    sum = 0;    それぞれ (t の中 parts)    {        sum += t.数に();    }    rows += 1;    countAll += parts.個数;    total += sum;    コンソール.書く(parts.個数 + " " + sum);}コンソール.書く(rows + " " + countAll + " " + total);