{ }codelper
读取多个数读到输入结束64 位整数P0392

读取混有逗号和注释的测量记录

实验室的仪器每测量一次就写下一行,这些记录一直给到输入结束。
行数和每行有几个数都不会事先给出。
在一行中,从第一个 '#' 开始到行尾是注释,写在注释里的数不计入。
去掉注释后剩下的部分有 0 个或更多个整数。
整数与整数之间混有一个或多个空格、制表符和逗号,行的最前面和最后面也可能有这些字符。
逗号连在一起并不表示它们之间有值。
整数是十进制数,最前面可以带符号 '+' 或 '-',也可以以 0 开头。
所以 007 是 7,-012 是 −12,+5 是 5。
只有至少含一个整数的行才是记录。
空行、只有空格·制表符·逗号的行、只有注释的行都不是记录,要跳过,它们后面还可能有记录。
对每条记录,按输入中出现的顺序在一行输出该行整数的个数和它们的和,用一个空格隔开。
最后一行输出记录数、所有记录的整数个数之和、所有整数的和,用一个空格隔开。
一条记录都没有时,只输出一行 "0 0 0"。

输入
输入有 1~1,000 行,每行不算换行符不超过 200 个字符。
整数去掉符号后有 1~12 个数字,值在 −100,000,000,000 到 100,000,000,000 之间。
一个整数最多有一个符号,符号只出现在该整数的最前面,符号和数字之间没有任何字符。
包括最后一行在内,每一行都以换行符结束。
注释以外的地方只出现数字、'+'、'-'、空格、制表符和逗号,注释中出现可打印的 ASCII 字符
输出
每条记录一行“个数 和”,最后一行“记录数 总个数 总和”。
数只写数字,不加逗号
样例 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 种语言题解

因为不知道一行有几个数,所以必须知道每一行在哪里结束。
C scanf("%lld")、C++ std::cin >> x、Java Scanner.nextLong() 把空格和换行一样跳过,无法知道行在哪里结束(遇到逗号和注释也会卡住)。
所以先读一行(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 行会得到 4 1 而不是 2 1,空行也会输出 1 0。
用 filter((t) => t !== "") 过滤。
陷阱二:Java split("[ \t,]+") 只丢掉末尾的空字符串,开头的会留下,所以在示例第 3 行 Long.parseLong("") 会因 NumberFormatException 而停止。
陷阱三:像 C strtoll(tok, NULL, 0) 这样把进制写成 0,以 0 开头的数会被当作八进制读取。
示例最后一行的 010 变成 8,08 读到 0 就停下,2 18 变成 2 8。
进制要写 10。
Python int、JS/TS Number、Java Long.parseLong、C# long.Parse、C++ >>、Craykon 转数字() 都把 007 和 +5 读成十进制的 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,记录数也会变多。
陷阱七:空行不是输入的结束。
在空行处停下会漏掉示例后面的两条记录。
输入结束的标志是 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);