{ }codelper
수 여러 개 읽기입력 끝까지 읽기큰 정수(64비트)P0392

쉼표와 주석이 섞인 측정 기록 읽기

실험실 장비가 측정값을 한 줄에 한 번씩 적어 둔 기록이 입력이 끝날 때까지 주어진다. 줄 수도, 한 줄에 든 수의 개수도 미리 알려 주지 않는다. 한 줄에서 '#'이 처음 나오는 곳부터 그 줄의 끝까지는 주석이고, 주석 속에 적힌 수는 세지 않는다. 주석을 뺀 나머지에는 정수가 0개 이상 있다. 정수와 정수 사이에는 빈칸, 탭, 쉼표가 하나 이상 섞여 있고, 줄의 맨 앞과 맨 뒤에도 이 글자들이 있을 수 있다. 쉼표가 겹쳐 있어도 그 사이에 값이 있다는 뜻은 아니다. 정수는 10진수이고, 맨 앞에 부호 '+'나 '-'가 붙을 수 있으며, 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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 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, 크레이콘 콘솔.줄읽기(), JS·TS는 한꺼번에 읽어 split("\n")), 그 줄을 조각으로 나눠요.
나누는 도구마다 빈 조각을 다루는 법이 달라요.
Python split()과 크레이콘 나누기()는 인자가 없으면 빈칸·탭 덩이를 한 번에 건너뛰므로 쉼표만 빈칸으로 바꿔 주면 돼요(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이에요.
거르지 않으면 예시 셋째 줄이 2 1이 아니라 4 1이 되고 빈 줄도 1 0으로 나와요.
filter((t) => t !== "")로 걸러요.
함정 둘: Java split("[ \t,]+")는 맨 뒤의 빈 문자열만 버리고 맨 앞의 것은 남겨서, 예시 셋째 줄에서 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++ >>, 크레이콘 수로변환()은 007과 +5를 10진수 7과 5로 읽어요.
함정 넷: 주석을 잘라 내지 않고 줄 전체에서 수를 찾으면(Python re.findall) 예시 첫 줄의 7과 2, 셋째 줄의 99까지 세요.
함정 다섯: 합이 21억을 넘어요.
C·C++ long long, Java·C# long으로 모아요.
Java에서 int sum에 sum += Long.parseLong(t)를 쓰면 컴파일 오류 없이 int로 잘려서 예시 넷째 기록의 합 200000000000이 -1863462912가 돼요.
Python은 크기 제한이 없고, JS·TS·크레이콘의 수는 2^53까지 정확해서 이 문제의 가장 큰 전체 합 1.5 × 10^15도 그대로 돼요.
함정 여섯: 수가 없는 줄을 건너뛰지 않으면 0 0이 출력되고 기록 수가 늘어요.
함정 일곱: 빈 줄은 입력의 끝이 아니에요.
빈 줄에서 멈추면 예시의 뒤쪽 두 기록을 놓쳐요.
입력의 끝은 Java·C# null, C fgets의 NULL, C++ getline의 거짓, 크레이콘 빈값으로 알아요.

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);