{ }codelper
读取 n 行读取一行分割字符串P0295

整理一叠信件

n 封信一封接一封地连在同一个文件里。
一封信依次由 1 行信头、k 行正文、a 行附件组成。
信头行的格式是 "名字 k a"。
名字是寄信人,k 是正文行数,a 是附件行数。
正文行就是原样的一行。
它只由英文字母、数字、空格和 .
, !
?
组成,长度为 0 到 80 个字符。
正文行可能是没有任何字符的空行,可能以空格开头或结尾,可能有多个连续的空格,也可能只有空格。
正文行即使看起来像信头行或数字,也仍然是正文。
正文到哪里结束只由 k 决定。
附件行的格式是 "文件名 大小"。
正文中的单词是指一段连续的非空格字符。
对每封信求出以下四项。
第一,至少含有一个单词的正文行数。
第二,整个正文的单词数。
第三,最长的正文行的长度。
长度包括行首行尾的空格在内,计入该行的所有字符。
没有正文时为 0。
第四,附件大小之和。
没有附件时为 0。
按信件给出的顺序每封输出一行:名字和上面四个数,用一个空格隔开。

输入
第一行是 n。
接下来依次给出 n 封信。
信头行的名字、k、a 以及附件行的文件名、大小用一个空格隔开,行首行尾没有空格。
1 ≤ n ≤ 1,000,0 ≤ k ≤ 100,0 ≤ a ≤ 20,1 ≤ 大小 ≤ 1,000,000。
名字是 1 到 10 个小写英文字母,文件名是由小写英文字母、数字和句点组成的 1 到 20 个字符。
整个输入不超过 10,000 行
输出
每封信一行,共 n 行。
名字、有单词的正文行数、单词数、最长正文行的长度、附件大小之和
样例 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
样例 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
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

这个输入把“先告诉行数、再读这么多行”套了两层:循环 n 封信,每封再读 k 行和 a 行。
不能把整个输入按空白一次切开。
正文需要原样的行,而且有的正文长得和信头一样(第一个例子的 lee 0 0),只有边数边读才能区分。
读取一整行的方法是 Python 的 input()、Java 的 BufferedReader.readLine()、C# 的 Console.ReadLine()、C++ 的 getline(cin, line)、C 的 fgets、Craykon 的 控制台.读行()。
JavaScript 和 TypeScript 用 split("\n") 把整个输入变成行数组,再让下标逐个前进。
陷阱一:把读数的方法和读行的方法混用,行会错位。
C 的 scanf("%d")、C++ 的 cin >>、Java Scanner 的 nextInt() 会把数后面的换行留下,紧接着的 fgets、getline、nextLine() 就读到一个空行。
所以含数字的行也按整行读入,再用 sscanf、istringstream、split 取出。
陷阱二:空行也是一行正文。
把空行过滤掉再读,后面就会每次错一行。
陷阱三:在 JavaScript 中对整个输入做 trim(),当最后一封信的正文以空行结尾时,那一行就没了。
陷阱四:行的长度包括首尾的空格。
先做 Python 的 strip()、JavaScript 的 trim() 或 Craykon 的 去空白() 再量长度就会变短(第一个例子的 see you at 9 是 14 个字符)。
陷阱五:数单词时按一个空格切分,连续空格处会出现空字符串。
JavaScript 的 "a b".split(" ") 是 3 段,C# 的 Split(' ') 也一样,所以要传 StringSplitOptions.RemoveEmptyEntries。
Python 的 split() 和 Craykon 的 分割() 不给分隔符时把连续空格当作一个,只返回单词。
Java 用 StringTokenizer 的 countTokens(),C++ 数从 istringstream 用 >> 取出的次数,C 数空格后面出现字符的位置。
陷阱六:只有空格的行不是空字符串,但它没有单词。
用 line != "" 比较是错的,要看单词数是否为 0。
陷阱七:C 的 fgets(line, 81, stdin) 最多只读 80 个字符。
遇到 80 个字符的行,换行会留下来,下一次 fgets 读到空行,所以数组还要留出换行和结束符 '\0' 的位置。

Pythonmain.py · 18 行
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 行
// 줄 단위로 읽어야 하니 빈칸으로 쪼개지 않는다. 통째로 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 行
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 行
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 行
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 行
#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 行
#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 行
// 줄읽기는 줄바꿈만 떼고 한 줄을 통째로 돌려준다: 앞뒤 빈칸도 빈 줄도 그대로다n = 控制台.读行().转数字();循环 (i = 0; i < n; i++){    // 머리 줄: 이름, 본문 줄 수 k, 첨부 줄 수 a    head = 控制台.读行().分割();    k = head[1].转数字();    a = head[2].转数字();    filled = 0;    words = 0;    longest = 0;    循环 (j = 0; j < k; j++)    {        line = 控制台.读行();        count = line.分割().数量; // 기준 없이 나누면 빈칸이 이어져도 낱말만 돌려준다        如果 (count > 0)        {            filled += 1;        }        words += count;        longest = 数学.较大值(longest, line.长度);    }    total = 0;    循环 (j = 0; j < a; j++)    {        total += 控制台.读行().分割()[1].转数字();    }    控制台.输出(head[0] + " " + filled + " " + words + " " + longest + " " + total);}