静かなる名辞

pythonとプログラミングのこと



【python】区切り文字を含めてsplitする

 
 正規表現によるsplitで区切り文字(あるいは区切り文字列)を含めたいときがある。デフォルトでは区切り文字は消える。

 たとえば、文を句点で分割する場合。

>>> import re
>>> string = "吾輩は猫である。名前はまだない。"
>>> re.split("。", string)
['吾輩は猫である', '名前はまだない', '']

 実は「区切り文字(列)をリストの要素にする」のか「区切り文字(列)を前の要素に(あるいは後の要素に)くっつける」のかという問題もあるので、一筋縄ではいかない。検索すると前の方ばっかり出てくるので注意。

区切り文字をリストの要素に入れる

 これは検索してたくさん出てくる方。正規表現のグループ化というものを使えば簡単にできる。

>>> re.split("(。)", string) # 半角丸括弧でくくる
['吾輩は猫である', '。', '名前はまだない', '。', '']

 今回の例では、それが欲しいんじゃないんだけどなぁ・・・と相成る。

# こうなってほしい(空文字列はご愛嬌)
['吾輩は猫である。', '名前はまだない。', '']

区切り文字を前の要素に付ける。

 できるだけ簡単なやり方を考える。

後処理で連結する

 真っ先に思いつくのはこの方法。forループで処理する。

>>> lst = []
>>> for elem in re.split("(。)", string):
...     if elem != "。":
...         lst.append(elem)
...     else:
...         lst[-1] = lst[-1] + elem
... 
>>> lst
['吾輩は猫である。', '名前はまだない。', '']

 書き方は他にも幾らでもある。この書き方だと、区切り文字(列)が連続したときは連続が途切れるまで連結される。

 ただ、これは遅いかもしれない。

正規表現で書く

 rubyでやってるページは見つけた。
文章を一文ごとに分割する正規表現 - Writing to be me 
 ここの通りにやろうと思ったら、できませんでした・・・。

 ruby正規表現のsplitとは仕様が違うのかなぁ。たぶん『長さゼロの文字列』へのマッチを何らかの方法で書けば同様にできると思われる。どうやるんだろうか、というかできるんだろうか。詳しい人に教えてほしい。

まとめ

 正規表現とかよくわからないけど、それに学習コスト投入するのは面倒くさいので騙し騙し適当に処理してプログラム書くことにします・・・。

追記

 findallすれば等価のことができた。

>>> re.findall(".*?。", s)
['吾輩は猫である。', '名前はまだない。']

 ただし厳密に言えばこれはsplitではない。なのでこうなる。

>>> s2 = "吾輩は猫である。名前はまだない。どこで生まれたかとんと見当がつかぬ"
>>> re.findall(".*?。", s2)
['吾輩は猫である。', '名前はまだない。']

 邪道だと思うが、

>>> s2 = "吾輩は猫である。名前はまだない。どこで生まれたかとんと見当がつかぬ"
>>> re.findall(".*?。|.*$", s2)
['吾輩は猫である。', '名前はまだない。', 'どこで生まれたかとんと見当がつかぬ', '']

 こんな方向性で工夫すればなんとかなる可能性はある。正規表現が得意な人なら上手く書く方法を思いつくんだろうなぁ。

追記2

 コメントでこういう場合の書き方を教えていただきました。

>>> s2 = "吾輩は猫である。名前はまだない。どこで生まれたかとんと見当がつかぬ"
>>> import re
>>> re.findall("[^。]+。?",s2)
['吾輩は猫である。', '名前はまだない。', 'どこで生まれたかとんと見当がつかぬ']

 やっぱりsplitとは違う気がするけど、実用的には良さそうです。