CodeIQ:英語の文章を一文ずつに分割してみよう

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「英語の文章を一文ずつに分割してみよう」(CodeIQ)を参照してください。

問題の概要

標準入力から1行で英文が与えられます。
次のルールに従って文章を分割し、分割した箇所(二番目以降の文の先端の直前)に改行コード(\n)を挿入した上で、1文ずつ標準出力に出力してください。(つまり分割後の行末にはスペースが残ります)

【ルール】
句点(.)、疑問符(?)、感嘆符(!)の直後にスペースが続く場合を、文の区切り対象とする。
スペースが続かない場合は、区切り対象としない。また、以下の例外においては、スペースが続いても区切り対象としない。
※直前に、Mr. Ms. Mrs. Mt. 4種の略称がある場合
※直前の文字列が、すべて数字で構成されている場合

私のプログラム

Pythonで解答しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/local/bin/python3
# -*- coding:utf-8 -*-
 
import fileinput
import re
import sys
 
def newliner(w):
    if w[len(w)-1] in ("!", "?"):
        return w + " \n"
    elif w[len(w)-1] == ".":
        if w in ("Mr.", "Ms.", "Mrs.", "Mt."):
            return w + " "
        elif re.match("^\d+\.$", w):
            return w + " "
        else:
            return w + " \n"
    else:
        if w[len(w)-1] == "\n":
            return w
        else:
            return w + " "
 
if __name__ == "__main__":
    for line in fileinput.input():
        if not line.strip():
            continue
 
        words = line.split(" ")
 
        str = ""
        for w in words:
            str += newliner(w)
 
        sys.stdout.write(str)

解説

私はこの問題を解く基本方針として2通りの方法を考えました。
1つはスペースで分割してから再構成する方法。
もう一つは先頭から1文字ずつ出力しながら改行の条件がヒットしたら改行を挿入する方法。
C言語でやるなら2つ目ですが、Pythonなので1つ目にしました。

main

標準入力をスペースで分割して1語ずつnewliner()に渡し、結果を再連結しています。newliner()では必要な場所に改行が入るので、再連結された文字列は改行されて表示されます。

newlinse()

まず、1段階目のif文で単語が「!か?」、「.」、それ以外、のどれで終わるかを判断しています。「!か?」の場合は改行を付けて戻すだけです。
「.」の場合、「Mr.」、「Ms.」、「Mrs.」、「Mt.」か「数字.」ならスペースを付加して返します。それ以外は文末なので改行を付加して返します。
上記以外の場合、基本的にスペースを付加して返せば良いのですが、入力パターンに意地悪があって「!、?、.」の後以外に改行があった場合を考慮しています。

雑感

多分、模範解答は1文字ずつ出力しながら改行条件が見つかったら改行を挿入するという方法だと思います。スペースで分割すると分割した時点でなくなっているスペースを改行の前にわざわざ挿入しなければなりませんから。