diff --git a/pythainlp/util/wordtonum.py b/pythainlp/util/wordtonum.py index 82bc00570..ea71b8161 100644 --- a/pythainlp/util/wordtonum.py +++ b/pythainlp/util/wordtonum.py @@ -57,7 +57,15 @@ def _tokenizer() -> Tokenizer: return Tokenizer(custom_dict=_valid_tokens) -def _check_is_thainum(word: str) -> tuple[bool, Optional[str]]: +def _check_is_thainum( + word: str, + next_word: str = "", + thainum: Optional[list[str]] = None, +) -> tuple[bool, Optional[str]]: + if word == "ศูนย์" and thainum is not None: + if "จุด" in thainum or next_word == "จุด": + return (True, "num") + return (False, None) for j in _digits: if j in word: return (True, "num") @@ -167,6 +175,16 @@ def words_to_num(words: list[str]) -> float: return num +def _flush(thainum: list[str], result: list[str]) -> None: + has_digit = any( + w == "ศูนย์" or _check_is_thainum(w)[1] == "num" for w in thainum + ) + if has_digit: + result.append(str(words_to_num(thainum))) + else: + result.extend(thainum) + + def text_to_num(text: str) -> list[str]: """Thai text to list of Thai words with floating point numbers @@ -188,25 +206,19 @@ def text_to_num(text: str) -> list[str]: last_index = -1 list_word_new = [] for i, word in enumerate(_temp): - if ( - _check_is_thainum(word)[0] - and last_index + 1 == i - and i + 1 == len(_temp) - ): + next_word = _temp[i + 1] if i + 1 < len(_temp) else "" + isthainum = _check_is_thainum(word, next_word, thainum)[0] + if isthainum and last_index + 1 == i and i + 1 == len(_temp): thainum.append(word) - list_word_new.append(str(words_to_num(thainum))) - elif _check_is_thainum(word)[0] and last_index + 1 == i: + _flush(thainum, list_word_new) + elif isthainum and last_index + 1 == i: thainum.append(word) last_index = i - elif _check_is_thainum(word)[0]: + elif isthainum: thainum.append(word) last_index = i - elif ( - not _check_is_thainum(word)[0] - and last_index + 1 == i - and last_index != -1 - ): - list_word_new.append(str(words_to_num(thainum))) + elif not isthainum and last_index + 1 == i and last_index != -1: + _flush(thainum, list_word_new) thainum = [] list_word_new.append(word) else: diff --git a/tests/core/test_util.py b/tests/core/test_util.py index 514eb6753..d09c48c62 100644 --- a/tests/core/test_util.py +++ b/tests/core/test_util.py @@ -287,6 +287,39 @@ def test_number(self): with self.assertRaises(TypeError): text_to_thai_digit(None) # type: ignore[arg-type] + def test_text_to_num_zero(self): + # "ศูนย์" (zero) is excluded from the digit table as a special + # case, which used to corrupt or crash on any floating-point + self.assertEqual( + text_to_num("หนึ่งร้อยยี่สิบสี่จุดศูนย์สี่"), ["124.04"] + ) + self.assertEqual( + text_to_num("หนึ่งร้อยยี่สิบเอ็ดจุดศูนย์สี่ห้า"), ["121.045"] + ) + self.assertEqual(text_to_num("ศูนย์จุดศูนย์เก้า"), ["0.09"]) + self.assertEqual(text_to_num("ห้าจุดศูนย์ศูนย์เก้า"), ["5.009"]) + self.assertEqual(text_to_num("สามจุดสี่ศูนย์เก้าศูนย์"), ["3.409"]) + + # "ศูนย์" as part of an ordinary word (e.g. "center") must stay + self.assertEqual( + text_to_num("ศูนย์ประชุมอยู่ที่กรุงเทพ"), + ["ศูนย์", "ประชุม", "อยู่", "ที่", "กรุงเทพ"], + ) + self.assertEqual( + text_to_num("ค่าเช่าศูนย์ประชุมคือหนึ่งร้อยบาท"), + ["ค่าเช่า", "ศูนย์", "ประชุม", "คือ", "100", "บาท"], + ) + + # "จุด" as an ordinary word (e.g. "point/spot") must not crash + self.assertEqual( + text_to_num("จุดศูนย์กลางของเมืองอยู่ที่นี่"), + ["จุด", "ศูนย์กลาง", "ของ", "เมือง", "อยู่", "ที่นี่"], + ) + self.assertEqual( + text_to_num("จุดศูนย์รวมของทุกคนคือที่นี่"), + ["จุด", "ศูนย์รวม", "ของ", "ทุกคน", "คือ", "ที่นี่"], + ) + # ### pythainlp.util.keyboard def test_keyboard(self):