{ }codelper
读取一行读到输入结束P0325

把用反斜杠续写的行展开

在配置文件里,把很长的内容分成多行书写时,会在行尾加一个反斜杠(\)表示下一行接着写。
逐行读入输入直到结束,把续写的多行展开成一行。
输入中的每一行称为“输入行”,从 1 开始编号。
如果输入行的最后一个字符是反斜杠,这一行就是“续行”。
对于续行,只去掉最后那一个反斜杠,并把紧接着的下一个输入行的内容原样接在后面。
如果下一个输入行也是续行,就重复同样的操作;接上一个不是续行的输入行之后,一条“展开行”就结束了。
是否为续行只看这一个输入行本身,而不是看拼接出来的字符串。
即使行尾有多个反斜杠,也只去掉最后一个;反斜杠后面有空格的行不是续行。
如果输入的最后一行是续行,只去掉那一个反斜杠,展开行就在那里结束。
空行也算一个输入行,行首和行尾的空格也是内容,不能删除。
对每条展开行输出一行:它开始的输入行编号、一个空格,以及用方括号 [ 和 ] 括起来的内容。
最后一行输出输入行数和展开行数,用一个空格隔开。

输入
输入有 1 到 1,000 行,行数不会另外给出。
每行有 0 到 100 个字符,且只包含 ASCII 码 32(空格)到 126 的字符。
包括最后一行在内,每一行都以换行符结束
输出
对每条展开行输出一行 起始编号 [内容],最后一行输出输入行数和展开行数,用一个空格隔开
样例 1 · stdin
total = price \
  + tax
path C:\temp\\

  note \ 
end\
stdout
1 [total = price   + tax]
3 [path C:\temp\]
5 [  note \ ]
6 [end]
6 4
main.py
输入(stdin)

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

$ 就绪

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

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

题目不给行数,所以“没有更多行了”这个信号在各语言中形式不同。
Python 的 for line in sys.stdin: 会自己结束(input() 在结尾抛出 EOFError),Java readLine() 和 C# Console.ReadLine() 返回 null,C fgets 返回 NULL,C++ std::getline 变为假,Craykon 控制台.读行() 返回 空。
JavaScript、TypeScript 把全部内容读进来再用 split("\n") 分开。
读到的行是否带着换行符也不一样:Python 的 line 和 C 的 fgets 会保留换行符,要自己去掉,其余的都已经替你去掉。
反斜杠字符在八种语言里都要像 "\\" 这样写两个才表示一个(C、C++ 的字符是 '\\')。
判断最后一个字符:Python line.endswith(…),JS、TS line.endsWith(…),Java endsWith,C# EndsWith,C++ line.back(),C line[len - 1],Craykon line.结尾是(…)。
八个陷阱:① 在 Python 中为了去掉换行符而用 line.strip(),会把首尾空格也删掉。
示例第 5 行的反斜杠后面有空格,所以不续行,但空格被删掉后就会和第 6 行接在一起。
用 rstrip("\n") 只去掉换行符。
② 用 line.rstrip(…) 去掉反斜杠,会把行尾连续的反斜杠全部删掉。
示例第 3 行必须留下一个反斜杠。
要像 line[:-1] 这样只去掉一个字符。
③ JS 的 split("\n") 会在最后一个换行符之后多产生一个空字符串。
直接计数就多出一个不存在的行。
用 pop() 丢掉它。
④ 但是对整个输入调用 trim(),又会把第一行前面的空格和最末尾真正的空行一起删掉。
⑤ 把空行当作输入结束。
在 Craykon 中空行是 "",输入结束是 空,两者不同。
在 line == "" 处停下,读取就会在示例第 4 行结束。
⑥ 根据拼接后的字符串的结尾来判断是否续行:示例第 3 行留下一个反斜杠,接上空行(第 4 行)之后,拼接结果仍以反斜杠结尾,看起来又是续行,于是连第 5 行也接上了。
要只看单个输入行来判断。
⑦ 在 Java 中读入续行的下一行后不检查 null 就调用 endsWith,对最后一行以反斜杠结尾的这个示例会因 NullPointerException 而停止。
⑧ C 的 fgets 缓冲区要放下 100 个字符、换行符和结束符,至少 102 字节。
用 char line[101] 时,100 个字符的行会把换行符挤到下一次读取,多读出一个空行。

Pythonmain.py · 29 行
import sys  def main():    out = []    physical = 0    start = 0    parts = []    open_line = False  # 앞줄이 역슬래시로 끝나 아직 이어지는 중인가    for line in sys.stdin:  # 입력이 끝나면 반복도 끝난다        line = line.rstrip("\n")  # 줄바꿈만 뗀다. strip()은 앞뒤 빈칸까지 지운다        physical += 1        if not open_line:            start = physical            parts = []        if line.endswith("\\"):            parts.append(line[:-1])  # 마지막 역슬래시 하나만 뗀다            open_line = True        else:            parts.append(line)            out.append(f"{start} [{''.join(parts)}]")            open_line = False    if open_line:  # 마지막 줄이 역슬래시로 끝났다        out.append(f"{start} [{''.join(parts)}]")    out.append(f"{physical} {len(out)}")    print("\n".join(out))  main()
JavaScriptmain.js · 25 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");lines.pop(); // 마지막 줄바꿈 뒤에 생기는 빈 문자열은 줄이 아니다 const out = [];let start = 0;let text = "";let open = false; // 앞줄이 역슬래시로 끝나 아직 이어지는 중인가for (let i = 0; i < lines.length; i++) {  const line = lines[i];  if (!open) {    start = i + 1;    text = "";  }  if (line.endsWith("\\")) {    text += line.slice(0, -1); // 마지막 역슬래시 하나만 뗀다    open = true;  } else {    text += line;    out.push(`${start} [${text}]`);    open = false;  }}if (open) out.push(`${start} [${text}]`); // 마지막 줄이 역슬래시로 끝났다out.push(`${lines.length} ${out.length}`);console.log(out.join("\n"));
TypeScriptmain.ts · 26 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");lines.pop(); // 마지막 줄바꿈 뒤에 생기는 빈 문자열은 줄이 아니다 const out: string[] = [];let start = 0;let text = "";let open = false; // 앞줄이 역슬래시로 끝나 아직 이어지는 중인가lines.forEach((line: string, i: number) => {  if (!open) {    start = i + 1;    text = "";  }  if (line.endsWith("\\")) {    text += line.slice(0, -1); // 마지막 역슬래시 하나만 뗀다    open = true;  } else {    text += line;    out.push(`${start} [${text}]`);    open = false;  }});if (open) out.push(`${start} [${text}]`); // 마지막 줄이 역슬래시로 끝났다out.push(`${lines.length} ${out.length}`);console.log(out.join("\n"));
JavaMain.java · 34 行
import java.io.*; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringBuilder out = new StringBuilder();        StringBuilder text = new StringBuilder();        int physical = 0, logical = 0, start = 0;        boolean open = false; // 앞줄이 역슬래시로 끝나 아직 이어지는 중인가        String line;        while ((line = br.readLine()) != null) { // 입력이 끝나면 null을 돌려준다            physical++;            if (!open) {                start = physical;                text.setLength(0);            }            if (line.endsWith("\\")) {                text.append(line, 0, line.length() - 1); // 마지막 역슬래시 하나만 뗀다                open = true;            } else {                text.append(line);                out.append(start).append(" [").append(text).append("]\n");                logical++;                open = false;            }        }        if (open) { // 마지막 줄이 역슬래시로 끝났다            out.append(start).append(" [").append(text).append("]\n");            logical++;        }        out.append(physical).append(' ').append(logical).append('\n');        System.out.print(out);    }}
C#Main.cs · 42 行
using System;using System.Text; class Program{    static void Main()    {        var output = new StringBuilder();        var text = new StringBuilder();        int physical = 0, logical = 0, start = 0;        bool open = false; // 앞줄이 역슬래시로 끝나 아직 이어지는 중인가        string? line;        while ((line = Console.ReadLine()) != null) // 입력이 끝나면 null을 돌려준다        {            physical++;            if (!open)            {                start = physical;                text.Clear();            }            if (line.EndsWith('\\'))            {                text.Append(line, 0, line.Length - 1); // 마지막 역슬래시 하나만 뗀다                open = true;            }            else            {                text.Append(line);                output.Append(start).Append(" [").Append(text).Append("]\n");                logical++;                open = false;            }        }        if (open) // 마지막 줄이 역슬래시로 끝났다        {            output.Append(start).Append(" [").Append(text).Append("]\n");            logical++;        }        output.Append(physical).Append(' ').Append(logical).Append('\n');        Console.Write(output);    }}
C++main.cpp · 31 行
#include <iostream>#include <string> int main() {    std::ios::sync_with_stdio(false);    std::string line, text;    int physical = 0, logical = 0, start = 0;    bool open = false; // 앞줄이 역슬래시로 끝나 아직 이어지는 중인가    while (std::getline(std::cin, line)) { // 입력이 끝나면 거짓이 된다. 줄바꿈은 떼어 준다        physical++;        if (!open) {            start = physical;            text.clear();        }        if (!line.empty() && line.back() == '\\') {            line.pop_back(); // 마지막 역슬래시 하나만 뗀다            text += line;            open = true;        } else {            text += line;            std::cout << start << " [" << text << "]\n";            logical++;            open = false;        }    }    if (open) { // 마지막 줄이 역슬래시로 끝났다        std::cout << start << " [" << text << "]\n";        logical++;    }    std::cout << physical << ' ' << logical << '\n';}
Cmain.c · 31 行
#include <stdbool.h>#include <stdio.h>#include <string.h> int main(void) {    char line[128]; // 100자 + 줄바꿈 + 끝 표시('\0')가 들어가야 한다    int physical = 0, logical = 0;    bool open = false; // 앞줄이 역슬래시로 끝나 아직 이어지는 중인가    while (fgets(line, sizeof line, stdin) != NULL) { // 입력이 끝나면 NULL을 돌려준다        size_t len = strlen(line);        if (len > 0 && line[len - 1] == '\n') line[--len] = '\0'; // fgets는 줄바꿈까지 담아 준다        physical++;        if (!open) {            // 이은 줄을 모아 두지 않고, 읽는 대로 이어서 출력한다            printf("%d [", physical);            logical++;        }        if (len > 0 && line[len - 1] == '\\') {            line[len - 1] = '\0'; // 마지막 역슬래시 하나만 뗀다            fputs(line, stdout);            open = true;        } else {            fputs(line, stdout);            puts("]");            open = false;        }    }    if (open) puts("]"); // 마지막 줄이 역슬래시로 끝났다    printf("%d %d\n", physical, logical);    return 0;}
Craykonmain.cray · 37 行
physical = 0;logical = 0;start = 0;text = "";open = 假; // 앞줄이 역슬래시로 끝나 아직 이어지는 중인가当 (真){    line = 控制台.读行();    如果 (line == 空)    {        跳出;    }    physical += 1;    如果 (!open)    {        start = physical;        text = "";    }    如果 (line.结尾是("\\"))    {        text += line.截取(0, line.长度 - 1); // 마지막 역슬래시 하나만 뗀다        open = 真;    }    否则    {        text += line;        控制台.输出(start + " [" + text + "]");        logical += 1;        open = 假;    }}如果 (open){    控制台.输出(start + " [" + text + "]");    logical += 1;}控制台.输出(physical + " " + logical);