Skip to content

Instantly share code, notes, and snippets.

@litagin02
Last active March 10, 2024 11:38
Show Gist options
  • Save litagin02/5eb8a6d02587bcb7b71518907b45fedb to your computer and use it in GitHub Desktop.
Save litagin02/5eb8a6d02587bcb7b71518907b45fedb to your computer and use it in GitHub Desktop.
笑い声・感動詞を文字列から識別するやつ
import re
import jaconv
# お好きな日本語文章正規化をimport, jaconv.normalizeとかでいいかも
from text import text_normalize
import regex
import pyopenjtalk
# 「ー」と「っ」を取り除いた文章に対するひらがなの笑い声の正規表現
warai_pattern = (
r"("
# 「は」行を最後に含む系統
+ r"あ+は+|い+ひ+|う+(は+|ひ+|ふ+|へ+|ほ+)|え+へ+|お+ほ+|"
+ r"かは+|きひ+|く(は+|ひ+|ふ+|へ+|ほ+)|けへ+|"
+ r"がは+|ぎひ+|ぐ(は+|ひ+|ふ+|へ+|ほ+)|げへ+|"
+ r"きゃは+|ぎゃは+|"
+ r"たは+|てへ+|"
+ r"なは+|に(は+|ひ+|ふ+|へ+|ほ+)|ぬ(は+|ひ+|ふ+|へ+|ほ+)|"
+ r"にゃは+|にゅふ+|にょほ+|"
+ r"ふ+(は+|ひ+|ふ+|へ+|ほ+)|"
+ r"ぶ+(は+|ひ+|ふ+|へ+|ほ+)|"
+ r"ぷ+(は+|ひ+|ふ+|へ+|ほ+)|"
+ r"(む|も)(は+|ひ+|ふ+|へ+|ほ+)|"
+ r"わ+は+|うぃひ+|うぇへ+|うぉほ+|"
+ r"ん(は+|ひ+|ふ+|へ+|ほ+)|"
# 「ひゃ」で終わる系統(叫び声もある)
+ r"あ(ひゃ)+|う(ひゃ)+|う(ひょ)+|"
# 「し」で終わる系統
+ r"(い|き|に)し+|"
# 「ん」で終わる系統(自慢げや感心の感嘆詞の可能性もある)
+ r"ふ{2,}ん|へ{2,}ん|ほ{2,}ん|"
# 2回以上の繰り返し
+ r"か{2,}|く{2,}|け{2,}|は{2,}|ひ{2,}|ふ{2,}|へ{2,}|ほ{2,}|ぷ{2,}|"
# 擬態語
+ r"くす|けら|げら|にこ|にや|にた|にか|にま|"
+ r"てへ+|"
+ r"にやり)+"
)
# 母音撥音促音等のパターン
basis = r"[あいうえおやゆよわんぁぃぅぇぉゃゅょゎーっ]"
# (母音 +)◯◯(+ 母音)で感嘆詞とみなせるパターン
single_nv_pattern = (
# フィラー
r"あ[ー]*|あの[うおー]*|え[ーっ]*と?|その[おー]*|ま[あー]*|う[ー]*ん?|"
# それ以外の単体の感嘆詞や口語表現
+ r"ちぇ|くそ|(やれ){2,}|すご|ちくしょ|やば|まじ(か|で)?|あれ+|でし|"
+ r"おっ?け|あっぱれ|おっ?す|うぃ?っ?す|しまった|よくも?|"
)
# ひらがな(「ー」「っ」含む)に対する非言語音声・感嘆詞の正規表現
nv_pattern = (
r"("
# 母音等の2回以上の繰り返しの場合(「うおわー」「いやぁ」「うん」等)
+ rf"{basis}{{2,}}|"
# 母音等以外が先頭・間に来る場合(「やったー」「げげ」「ぎょわあーーっ」「うみゃみゃーん」等)
+ rf"{basis}*("
+ r"き+|く+|が+|ぎ+|ぐ+|げ+|し+|そ+|た+|て+|と+|な+|に+|ぬ+|は+|ひ+|ふ+|へ+|ほ+|む+|"
+ r"ち?(ちゃ)+|ち?(ちゅ)+|ち?(ちょ)+|に?(にゃ)+|み?(みゃ)+|(ひゃ)+|(ひゅ)+|(ひょ)+|(しゃ)+|(しゅ)+|(しょ)+|"
+ rf"{single_nv_pattern}"
+ rf"){basis}*|"
# 「ら」「りゃ」が間に入る場合(「ありゃ」「おらー」「てりゃりゃー」等)
+ rf"[あいうおこてとんぁぃぅぇぉゃゅょゎーっ]+(ら|りゃ)+{basis}*"
+ r")+"
)
def is_laughing(text: str) -> bool:
text = text_normalize(text)
# 句読点やスペース、ハイフンを削除(「ー」「っ」は残す)
text = re.sub("[" + re.escape("。、!?,.'- ") + "]", "", text)
# wの繰り返しの場合はTrueを返す(書き起こし結果がたまに「www」となる)
if re.fullmatch(r"(w|W)+", text):
return True
# 漢字が含まれている場合はFalseを返す
if regex.search(r"\p{Script=Han}", text):
return False
# ひらがな、カタカナ以外があったらFalseを返す
if not re.fullmatch(r"[\u3040-\u309F\u30A0-\u30FF]+", text):
return False
# カタカナをひらがなに変換
text = jaconv.kata2hira(text)
# 「ー」と「っ」を取り除く
text = re.sub("[っー]", "", text)
# 全体がパターンにマッチするかどうかを判定
return bool(re.fullmatch(warai_pattern, text))
def is_kandoushi(text: str) -> bool:
result = pyopenjtalk.run_frontend(text)
for r in result:
if r["pos"] not in ["感動詞", "フィラー", "記号"]:
return False
return True
def is_nv(text: str) -> bool:
text = text_normalize(text)
if text == "":
return False
# 解析で感動詞、フィラー、記号のみからなればTrueを返す
if is_kandoushi(text):
return True
# 漢字が含まれている場合はFalseを返す
if regex.search(r"\p{Script=Han}", text):
return False
# 句読点やスペース、ハイフンを削除(「ー」「っ」は残す)
text = re.sub("[" + re.escape("。、!?,.'- ") + "]", "", text)
# ひらがな、カタカナ以外があったらFalseを返す
if not re.fullmatch(r"[\u3040-\u309F\u30A0-\u30FF]+", text):
return False
if text == "":
return False
# カタカナをひらがなに変換
text = jaconv.kata2hira(text)
# フィラーパターンにマッチするかどうかを判定
if bool(re.fullmatch(single_nv_pattern, text)):
return True
# nvパターンにマッチするかどうかを判定
if not bool(re.fullmatch(nv_pattern, text)):
return False
# 特定の単語が含まれている場合はFalseを返す
# 主に上のパターンでの1文字部分の繰り返しで意味のある単語ができてしまう場合に使用
exclude_words = [
"あなた",
"あんた",
"やがて",
"たとえ",
"とはいえ",
"よくない",
"なんとなく",
"そして",
"そうして",
"したい",
"いいよ",
] # 適宜追加
if any(word in text for word in exclude_words):
return False
return True
def is_kana_only(text: str) -> bool:
text = text_normalize(text)
# 削除したい文字を削除
text = re.sub("[" + re.escape("。、!?,.'- ") + "]", "", text)
# ひらがな、カタカナのみからなるかどうかを判定
return bool(re.fullmatch(r"[\u3040-\u309F\u30A0-\u30FF]+", text))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment