Last active
August 29, 2015 14:05
-
-
Save kyu999/5bb18b231494c3ded7cc to your computer and use it in GitHub Desktop.
改行ルールほぼ最終
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
separate編: | |
1. 助詞もしくは副詞の後は切る | |
2. 記号のあとは切る | |
3. 長いと切る | |
regulate編: | |
1. 左括弧の前で切れてたらくっつける | |
2. 記号の連続、助詞の連続の場合もくっつける | |
3. 右括弧以外の記号の前ではくっつける | |
-- coding:utf-8 --
import re
import unicodedata
import MeCab
import os
import title_cleaner
class SpeechTitleSplitter(object):
"""This class split a given title and produces split points for beatiful rendering of that by the simple way"""
def __init__(self):
self.hiragana_pattern = re.compile(u'[ぁ-ん]+')
self.kanji_pattern = re.compile(u'[一-龠]+')
self.katakana_pattern = re.compile(u'[ァ-ヴ]+')
self.alphanumeric_pattern = re.compile(u'[a-zA-Z0-9]+')
#開閉の区分けがある全角クォーテーションをleft_kakko, right_kakkoにいれているが、これはテキストエディタの自動変換によって生じたものなのでもし入り込まないと断定できるならこのパターン系には一切入れる必要がなくregulateの中で対処する。逆に必ず入り込むようなことがあるのならregulateの方を変更する。
self.kigou_pattern = re.compile(u'[!?:;/〜。、!\#$%&\*+,-./・:;<==>?]+')
self.left_kakko_pattern = re.compile(u'([\[\{{[「『((⦅〈《〔〘【〖<])')
self.right_kakko_pattern = re.compile(u'[}]」』))⦆〉》〕〙】〗>]+')
def is_kind_base(self, pattern, letters):
"""
文字種かどうかを調べるための関数のベース関数。判定は1文字でも含まれてたらTrue, else False
1文字ごとの判定にはこれを活用できる。
"""
if pattern.match(letters):
return True
else:
return False
def is_hiragana(self, letters):
return self.is_kind_base(self.hiragana_pattern, letters)
def is_kanji(self, letters):
return self.is_kind_base(self.kanji_pattern, letters)
def is_katakana(self, letters):
return self.is_kind_base(self.katakana_pattern, letters)
def is_alphanumeric(self, letters):
return self.is_kind_base(self.alphanumeric_pattern, letters)
def is_kigou(self, letters):
return self.is_kind_base(self.kigou_pattern, letters)
def is_left_kakko(self, letters):
return self.is_kind_base(self.left_kakko_pattern, letters)
def is_right_kakko(self, letters):
return self.is_kind_base(self.right_kakko_pattern, letters)
def is_quote(self, letter):
quotes = [u"\"",u"\'",u"”",u"’"]
if letter in quotes:
return True
else:
return False
def get_feature(self, node):
"""
品詞等の情報をnodeから取り出してunicode化する
"""
features = node.feature.split(",")
unicoded_features = []
for each_feature in features:
unicoded_features.append(unicode(each_feature, "utf-8"))
return unicoded_features
def separate(self, sentence):
"""
token単位でiterateして分割
"""
#MeCabは半角空白を認識しないので全角空白に置き換える
encoded_sentence = sentence.replace(u" ", u" ").encode("utf-8")
if len(encoded_sentence) <= 0:
return None
else:
tagger = MeCab.Tagger("-O wakati")
node = tagger.parseToNode(encoded_sentence)
break_points = []
current_position = 0
#現在つながっている長さ
chunk_len = 0
pre_node = node
current_node = node.next
while current_node:
if not node.surface:
node = node.next
continue
else:
breakable = False
unicoded_surface = unicode(current_node.surface, "utf-8")
current_position += len(unicoded_surface)
this_len = len(unicoded_surface)
chunk_len += this_len
pre_feature = self.get_feature(pre_node)
current_feature = self.get_feature(current_node)
#助詞もしくは副詞の後は切る
if current_feature[0] == u"助詞" or current_feature[0] == u"副詞":
breakable = True
#記号の後は切る
elif current_feature[0] == u"記号":
breakable = True
#長い名詞は切る。ただし数以外
elif chunk_len > 5 and pre_feature[0] == u"名詞" and current_feature[0] == u"名詞" and not current_feature[1] == u"数":
breakable = True
#whether break or not
if breakable:
break_points.append(current_position)
chunk_len = 0
#update cursor
pre_node = current_node
current_node = current_node.next
return break_points
def regulate(self, breaks, sentence):
"""
文字単位でiterateする。必要以上に区切られていたらその修正もする。
"""
pre = sentence[0]
now = ""
found_quotes = []
last_quote = None
for i, char in enumerate(sentence):
now = sentence[i]
pre_was_broken = not i == 0 and i in breaks
now_was_broken = i + 1 in breaks
#今までで最後に出現したクォーテーション取得
if found_quotes:
last_quote = found_quotes[-1]
#print(last_quote)
#print(found_quotes)
#前の文字後にて区切られていたら
if pre_was_broken:
#もし右括弧のまえでbreakしてたら取り除く
if self.is_right_kakko(char):
breaks.remove(i)
#記号の前で区切られてたら取り除く
elif self.is_kigou(char):
breaks.remove(i)
#空白は前につける
elif char == u" ":
breaks.remove(i)
#閉じクォーテーション前で区切られてたら
elif last_quote == char:
breaks.remove(i)
#前の文字列後が区切られてないけど追加したい場合
else:
#もし左括弧系の前が区切られたなかったら区切る
if self.is_left_kakko(char):
breaks.append(i)
#今の文字後にて区切られていたら
if now_was_broken:
#もし左括弧系の後にbreakしてたら取り除く
if self.is_left_kakko(char):
breaks.remove(i + 1)
#もし開きクォーテーションなら
elif not last_quote == char and self.is_quote(char):
breaks.remove(i + 1)
#今の文字後で区切られてないけど追加したい場合
else:
#もし右括弧系の後が区切られてなかったら区切る
if self.is_right_kakko(char):
breaks.append(i + 1)
#update
#閉じクォーテーションならそのセットを取り除く
if last_quote == char:
found_quotes.pop()
last_quote = None
elif self.is_quote(char):
found_quotes.append(char)
pre = now
return breaks
def get_break_points(self, sentence):
frequent_breaks = self.separate(sentence)
return self.regulate(frequent_breaks, sentence)
def visualize(self, break_points, sentence):
"""
タイトル分割の視覚化
"""
formatted_sentence = []
for i, char in enumerate(sentence):
formatted_sentence.append(char)
if i + 1 in break_points:
formatted_sentence.append("|")
return "".join(formatted_sentence)
def break_and_visualize(self, sentence):
"""
単体動作確認用
"""
breaks = self.get_break_points(sentence)
print(self.visualize(breaks, sentence))
def checking(self):
"""
複数動作確認用
"""
f = open("sample_title.txt", "r")
lines = f.readlines()
cleaner = title_cleaner.TitleCleaner()
for line in lines:
unicoded_line = cleaner.normalize(unicode(line, "utf-8"))
self.break_and_visualize(unicoded_line)
f.close()
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
追記:
1. クォーテーションは特殊な全角開閉区分けありの開バージョンさえ除外したらうまくうごく。title_cleanerのnormalizeを修正しよう。
2. クォーテーションや括弧の連続への対処がまだ。
3. これはどっちでもいいんだけど、空白の扱い。空白の前だけにするか後ろだけにするか両方で区切るのか。