{ }codelper
reading n linesreading a linesplitting stringsP0295

Summarising a bundle of letters

n letters are joined one after another in a single file. One letter is a header line, then k body lines, then a attachment lines, in this order. The header line has the form "name k a".
The name is the sender, k is the number of body lines and a is the number of attachment lines. A body line is one line exactly as it was written.
It consists only of English letters, digits, spaces and .
, !
?
and its length is from 0 to 80 characters. A body line may be an empty line with no characters, may start or end with spaces, may contain several spaces in a row, and may consist of spaces only. A body line is still body even if it looks like a header line or a number.
Where the body ends is decided by k alone. An attachment line has the form "filename size". In the body, a word is one maximal run of characters that are not spaces. For each letter find the following four values. First, the number of body lines that contain at least one word. Second, the number of words in the whole body. Third, the length of the longest body line.
The length counts every character of the line, including leading and trailing spaces.
It is 0 if there is no body. Fourth, the sum of the attachment sizes.
It is 0 if there are no attachments. In the order the letters are given, print one line per letter: the name and the four numbers above, separated by single spaces.

Input
The first line has n.
Then the n letters follow in order.
The name, k and a on a header line, and the filename and size on an attachment line, are separated by single spaces, with no spaces at the start or end of the line.
1 ≤ n ≤ 1,000, 0 ≤ k ≤ 100, 0 ≤ a ≤ 20, 1 ≤ size ≤ 1,000,000.
A name is 1 to 10 lowercase English letters, and a filename is 1 to 20 characters of lowercase English letters, digits and dots.
The whole input has at most 10,000 lines
Output
n lines, one per letter: the name, the number of body lines with a word, the number of words, the length of the longest body line, and the sum of the attachment sizes
Example 1 · stdin
3
kim 3 2
Hello there, team.

  see you at 9
plan.txt 1200
map.png 30500
lee 0 0
park 2 1
lee 0 0
7
notes.md 8
stdout
kim 2 7 18 31700
lee 0 0 0 0
park 2 4 7 8
Example 2 · stdin
2
choi 3 0
a  b   c

ok
yun 1 1
12
photo.png 1000000
stdout
choi 2 4 8 0
yun 1 1 2 1000000
main.py
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 8 languages

This input nests 'here is a count, now read that many lines' twice: loop over n letters and read k lines and a lines for each.
You cannot split the whole input on whitespace.
The body is needed line by line, and some body lines look like headers (lee 0 0 in the first example), so only counting as you read tells them apart.
To read one whole line use Python input(), Java BufferedReader.readLine(), C# Console.ReadLine(), C++ getline(cin, line), C fgets and Craykon Console.ReadLine().
JavaScript and TypeScript turn the whole input into an array of lines with split("\n") and advance an index.
Trap one: mixing number reading with line reading shifts the lines.
C scanf("%d"), C++ cin >> and Java Scanner.nextInt() leave the newline after the number, so the very next fgets, getline or nextLine() reads an empty line.
So lines holding numbers are also read as whole lines and then taken apart with sscanf, istringstream or split.
Trap two: an empty line is still one body line.
Filtering out empty lines shifts everything after it by one.
Trap three: in JavaScript, calling trim() on the whole input removes the last line when the last letter's body ends with an empty line.
Trap four: the line length includes leading and trailing spaces.
Measuring after Python strip(), JavaScript trim() or Craykon Trim() gives a shorter length ( see you at 9 in the first example has 14 characters).
Trap five: splitting on a single space to count words yields empty strings where spaces repeat.
JavaScript "a b".split(" ") has 3 pieces, and C# Split(' ') does the same, so pass StringSplitOptions.RemoveEmptyEntries.
Python split() and Craykon Split() with no separator treat a run of spaces as one and return only the words.
Java uses countTokens() of StringTokenizer, C++ counts how many times >> reads from an istringstream, and C counts the places where a non-space character follows a space.
Trap six: a line of spaces only is not an empty string, but it has no words.
Comparing with line != "" is wrong; check whether the word count is 0.
Trap seven: C fgets(line, 81, stdin) reads at most 80 characters.
For an 80-character line the newline is left over and the next fgets reads an empty line, so the array needs room for the newline and the terminating '\0' as well.

Pythonmain.py · 18 lines
n = int(input())out = []for _ in range(n):    # 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a    name, k, a = input().split()    filled = words = longest = 0    for _ in range(int(k)):        line = input()  # 줄 끝의 줄바꿈만 떼고 앞뒤 빈칸은 그대로 둔다        count = len(line.split())  # split()은 빈칸이 이어져도 낱말만 돌려준다        if count > 0:            filled += 1        words += count        longest = max(longest, len(line))    total = 0    for _ in range(int(a)):        total += int(input().split()[1])    out.append(f"{name} {filled} {words} {longest} {total}")print("\n".join(out))
JavaScriptmain.js · 24 lines
// 줄 단위로 읽어야 하니 빈칸으로 쪼개지 않는다. 통째로 trim()하지도 않는다: 끝의 빈 줄도 본문일 수 있다const lines = require("fs").readFileSync(0, "utf8").split("\n");let at = 0;const n = Number(lines[at++]);const out = [];for (let i = 0; i < n; i++) {  // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a  const [name, k, a] = lines[at++].split(" ");  let filled = 0;  let words = 0;  let longest = 0;  for (let j = 0; j < Number(k); j++) {    const line = lines[at++];    // split(" ")은 빈칸이 이어지면 빈 문자열을 돌려주니 걸러 낸다    const count = line.split(" ").filter((w) => w !== "").length;    if (count > 0) filled++;    words += count;    longest = Math.max(longest, line.length);  }  let total = 0;  for (let j = 0; j < Number(a); j++) total += Number(lines[at++].split(" ")[1]);  out.push(`${name} ${filled} ${words} ${longest} ${total}`);}console.log(out.join("\n"));
TypeScriptmain.ts · 26 lines
import { readFileSync } from "node:fs"; // 줄 단위로 읽어야 하니 빈칸으로 쪼개지 않는다. 통째로 trim()하지도 않는다: 끝의 빈 줄도 본문일 수 있다const lines: string[] = readFileSync(0, "utf8").split("\n");let at = 0;const n = Number(lines[at++]);const out: string[] = [];for (let i = 0; i < n; i++) {  // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a  const [name, k, a] = lines[at++].split(" ");  let filled = 0;  let words = 0;  let longest = 0;  for (let j = 0; j < Number(k); j++) {    const line: string = lines[at++];    // split(" ")은 빈칸이 이어지면 빈 문자열을 돌려주니 걸러 낸다    const count = line.split(" ").filter((w) => w !== "").length;    if (count > 0) filled++;    words += count;    longest = Math.max(longest, line.length);  }  let total = 0;  for (let j = 0; j < Number(a); j++) total += Number(lines[at++].split(" ")[1]);  out.push(`${name} ${filled} ${words} ${longest} ${total}`);}console.log(out.join("\n"));
JavaMain.java · 31 lines
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        // readLine은 줄바꿈만 떼고 한 줄을 통째로 돌려준다: 앞뒤 빈칸도 빈 줄도 그대로다        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine());        StringBuilder sb = new StringBuilder();        for (int i = 0; i < n; i++) {            // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a            String[] head = br.readLine().split(" ");            int k = Integer.parseInt(head[1]);            int a = Integer.parseInt(head[2]);            int filled = 0, words = 0, longest = 0;            for (int j = 0; j < k; j++) {                String line = br.readLine();                // StringTokenizer는 빈칸이 이어져도 낱말만 센다                int count = new StringTokenizer(line).countTokens();                if (count > 0) filled++;                words += count;                longest = Math.max(longest, line.length());            }            int total = 0;            for (int j = 0; j < a; j++) total += Integer.parseInt(br.readLine().split(" ")[1]);            sb.append(head[0]).append(' ').append(filled).append(' ').append(words).append(' ')              .append(longest).append(' ').append(total).append('\n');        }        System.out.print(sb);    }}
C#Main.cs · 32 lines
using System;using System.Text; class Program{    static void Main()    {        // ReadLine은 줄바꿈만 떼고 한 줄을 통째로 돌려준다: 앞뒤 빈칸도 빈 줄도 그대로다        int n = int.Parse(Console.ReadLine()!);        var sb = new StringBuilder();        for (int i = 0; i < n; i++)        {            // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a            string[] head = Console.ReadLine()!.Split(' ');            int k = int.Parse(head[1]), a = int.Parse(head[2]);            int filled = 0, words = 0, longest = 0;            for (int j = 0; j < k; j++)            {                string line = Console.ReadLine()!;                // RemoveEmptyEntries가 없으면 빈칸이 이어질 때 빈 문자열이 낱말로 세어진다                int count = line.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;                if (count > 0) filled++;                words += count;                longest = Math.Max(longest, line.Length);            }            int total = 0;            for (int j = 0; j < a; j++) total += int.Parse(Console.ReadLine()!.Split(' ')[1]);            sb.Append($"{head[0]} {filled} {words} {longest} {total}\n");        }        Console.Write(sb);    }}
C++main.cpp · 45 lines
#include <algorithm>#include <iostream>#include <sstream>#include <string>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    // 모든 줄을 getline으로 읽는다: cin >> 와 섞으면 >> 가 남긴 줄바꿈 때문에 getline이 빈 줄을 읽는다    string line;    getline(cin, line);    int n = stoi(line);    for (int i = 0; i < n; i++) {        // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a — 읽은 줄을 istringstream에 넣어 >> 로 꺼낸다        getline(cin, line);        istringstream head(line);        string name;        int k, a;        head >> name >> k >> a;        int filled = 0, words = 0, longest = 0;        for (int j = 0; j < k; j++) {            getline(cin, line);            // >> 는 빈칸이 이어져도 낱말만 하나씩 꺼낸다            istringstream body(line);            string word;            int count = 0;            while (body >> word) count++;            if (count > 0) filled++;            words += count;            longest = max(longest, (int)line.size());        }        int total = 0;        for (int j = 0; j < a; j++) {            getline(cin, line);            istringstream file(line);            string fileName;            int size;            file >> fileName >> size;            total += size;        }        cout << name << ' ' << filled << ' ' << words << ' ' << longest << ' ' << total << '\n';    }    return 0;}
Cmain.c · 39 lines
#include <stdio.h>#include <string.h> int main(void) {    // 한 줄은 80자까지지만 줄바꿈과 끝 표시('\0')까지 들어갈 자리가 있어야 한다    char line[128];    int n;    // 수도 fgets로 한 줄을 통째로 읽고 sscanf로 꺼낸다: scanf("%d")는 줄바꿈을 남겨 다음 fgets가 빈 줄을 읽는다    if (!fgets(line, sizeof line, stdin)) return 0;    sscanf(line, "%d", &n);    for (int i = 0; i < n; i++) {        // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a        char name[11];        int k = 0, a = 0;        fgets(line, sizeof line, stdin);        sscanf(line, "%10s %d %d", name, &k, &a);        int filled = 0, words = 0, longest = 0;        for (int j = 0; j < k; j++) {            fgets(line, sizeof line, stdin);            int len = (int)strcspn(line, "\n"); // 줄바꿈 앞까지의 길이: 앞뒤 빈칸은 그대로 센다            // 빈칸 다음에 빈칸이 아닌 글자가 나올 때마다 낱말 하나가 시작된다            int count = 0;            for (int c = 0; c < len; c++)                if (line[c] != ' ' && (c == 0 || line[c - 1] == ' ')) count++;            if (count > 0) filled++;            words += count;            if (len > longest) longest = len;        }        int total = 0;        for (int j = 0; j < a; j++) {            int size = 0;            fgets(line, sizeof line, stdin);            sscanf(line, "%*s %d", &size); // %*s: 파일 이름은 읽고 버린다            total += size;        }        printf("%s %d %d %d %d\n", name, filled, words, longest, total);    }    return 0;}
Craykonmain.cray · 29 lines
// 줄읽기는 줄바꿈만 떼고 한 줄을 통째로 돌려준다: 앞뒤 빈칸도 빈 줄도 그대로다n = Console.ReadLine().ToNumber();for (i = 0; i < n; i++){    // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a    head = Console.ReadLine().Split();    k = head[1].ToNumber();    a = head[2].ToNumber();    filled = 0;    words = 0;    longest = 0;    for (j = 0; j < k; j++)    {        line = Console.ReadLine();        count = line.Split().Count; // 기준 없이 나누면 빈칸이 이어져도 낱말만 돌려준다        if (count > 0)        {            filled += 1;        }        words += count;        longest = Mathf.Max(longest, line.Length);    }    total = 0;    for (j = 0; j < a; j++)    {        total += Console.ReadLine().Split()[1].ToNumber();    }    Console.WriteLine(head[0] + " " + filled + " " + words + " " + longest + " " + total);}