{ }codelper
1行読み込みEOFまで読むP0325

バックスラッシュで続けた行を伸ばす

設定ファイルでは、長い内容を複数の行に分けて書くとき、行の終わりにバックスラッシュ(\)を付けて次の行に続ける。
入力を最後まで 1 行ずつ読み、続けて書かれた行を 1 行に伸ばせ。
入力の 1 行 1 行を「入力行」と呼び、1 番から番号を付ける。
入力行の最後の文字がバックスラッシュなら、その行は「続く行」である。
続く行では最後のバックスラッシュ 1 つだけを取り除き、すぐ次の入力行の内容をそのまま後ろにつなげる。
次の入力行も続く行なら同じことを繰り返し、続く行でない入力行をつなげたところで「伸ばした行」1 つが終わる。
続く行かどうかは、つなげてできた文字列ではなく、入力行 1 つだけを見て決める。
行の終わりにバックスラッシュがいくつあっても最後の 1 つだけを取り除き、バックスラッシュのあとに空白がある行は続く行ではない。
入力の最後の行が続く行なら、バックスラッシュ 1 つだけを取り除き、そこで伸ばした行を終える。
空行も入力行 1 つであり、行の前後の空白も内容なので消さない。
伸ばした行ごとに 1 行ずつ、その行が始まった入力行の番号、空白 1 つ、そして角かっこ [ と ] で囲んだ内容を出力する。
最後の行には入力行の数と伸ばした行の数を空白 1 つで区切って出力する。

入力
入力は 1 行以上 1,000 行以下で、行数は別には与えられない。
各行は 0 文字以上 100 文字以下で、ASCII コード 32(空白)から 126 までの文字だけからなる。
最後の行を含むすべての行は改行で終わる
出力
伸ばした行ごとに 開始番号 [内容] を 1 行ずつ出力し、最後の行に入力行の数と伸ばした行の数を空白 1 つで区切って出力する
例 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] のように 1 文字だけ取ります。
③ JS の split("\n") は最後の改行のあとに空文字列をもう一つ作ります。
そのまま数えると存在しない行が一つ増えます。
pop() で捨てます。
④ だからといって入力全体に trim() をかけると、最初の行の前の空白と、いちばん最後の本物の空行まで消えます。
⑤ 空行を入力の終わりとみなすこと。
Craykon では空行は ""、入力の終わりは なし で、別のものです。
line == "" で止めると例の 4 行目で読み込みが終わります。
⑥ つなげてできた文字列の終わりを見て続くかどうかを決めると、例の 3 行目で残したバックスラッシュのあとに空行(4 行目)がつながったとき、また続く行に見えて 5 行目までつながります。
入力行 1 つだけを見て決めます。
⑦ 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);