tokmor 1.2.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tokmor/__init__.py +77 -0
- tokmor/api.py +194 -0
- tokmor/assets.py +365 -0
- tokmor/base.py +238 -0
- tokmor/brahmic.py +516 -0
- tokmor/cjk.py +497 -0
- tokmor/domain/__init__.py +11 -0
- tokmor/domain/sentiment.py +198 -0
- tokmor/factory.py +394 -0
- tokmor/indic.py +289 -0
- tokmor/inventory.py +51 -0
- tokmor/legacy_api.py +143 -0
- tokmor/lemma_store.py +102 -0
- tokmor/lookup_keys.py +145 -0
- tokmor/models/domain/sentiment/en.json +54 -0
- tokmor/models/domain/sentiment/ko.json +52 -0
- tokmor/models/seg_lexicon/km_wordfreq.pkl +0 -0
- tokmor/models/seg_lexicon/km_wordlist.pkl +0 -0
- tokmor/models/seg_lexicon/lo_wordfreq.pkl +0 -0
- tokmor/models/seg_lexicon/lo_wordlist.pkl +0 -0
- tokmor/models/seg_lexicon/my_wordfreq.pkl +0 -0
- tokmor/models/seg_lexicon/my_wordlist.pkl +0 -0
- tokmor/models/seg_lexicon/th_wordfreq.pkl +0 -0
- tokmor/models/seg_lexicon/th_wordlist.pkl +0 -0
- tokmor/models/seg_lexicon/zh_extra_dict.json +35 -0
- tokmor/models/seg_lexicon/zh_wordfreq.pkl +0 -0
- tokmor/morphology/__init__.py +395 -0
- tokmor/morphology/advanced_base.py +472 -0
- tokmor/morphology/arabic_advanced.py +247 -0
- tokmor/morphology/chinese.py +736 -0
- tokmor/morphology/chinese_advanced.py +425 -0
- tokmor/morphology/english.py +315 -0
- tokmor/morphology/english_advanced.py +560 -0
- tokmor/morphology/french_advanced.py +237 -0
- tokmor/morphology/german_advanced.py +343 -0
- tokmor/morphology/hindi_advanced.py +258 -0
- tokmor/morphology/japanese.py +417 -0
- tokmor/morphology/japanese_advanced.py +589 -0
- tokmor/morphology/korean.py +534 -0
- tokmor/morphology/korean_advanced.py +603 -0
- tokmor/morphology/russian_advanced.py +217 -0
- tokmor/morphology/spanish_advanced.py +226 -0
- tokmor/morphology/templates/__init__.py +32 -0
- tokmor/morphology/templates/arabic_script_template.py +162 -0
- tokmor/morphology/templates/brahmic_template.py +181 -0
- tokmor/morphology/templates/cyrillic_template.py +168 -0
- tokmor/morphology/templates/latin_template.py +235 -0
- tokmor/morphology/templates/other_scripts_template.py +475 -0
- tokmor/morphology/thai_native.py +274 -0
- tokmor/morphology/tier2.py +477 -0
- tokmor/morphology/tier3.py +449 -0
- tokmor/morphology/tier4.py +410 -0
- tokmor/morphology/unified.py +855 -0
- tokmor/morphology/universal_fallback.py +398 -0
- tokmor/ner_prep.py +747 -0
- tokmor/offline.py +89 -0
- tokmor/preprocess.py +80 -0
- tokmor/resources.py +288 -0
- tokmor/routing.py +147 -0
- tokmor/rtl.py +309 -0
- tokmor/schema.py +17 -0
- tokmor/sns_tags.py +281 -0
- tokmor/space_based.py +272 -0
- tokmor/token_quality.py +1185 -0
- tokmor/unified_tokens.py +228 -0
- tokmor-1.2.9.dist-info/METADATA +103 -0
- tokmor-1.2.9.dist-info/RECORD +70 -0
- tokmor-1.2.9.dist-info/WHEEL +5 -0
- tokmor-1.2.9.dist-info/licenses/LICENSE +22 -0
- tokmor-1.2.9.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Thai Native Morphological Analyzer - 자체 구현
|
|
3
|
+
==============================================
|
|
4
|
+
|
|
5
|
+
외부 라이브러리 없이 순수 Python으로 구현한 태국어 분석기
|
|
6
|
+
|
|
7
|
+
특징:
|
|
8
|
+
- 최장일치 분词 (Maximum Matching)
|
|
9
|
+
- 기본 단어 사전
|
|
10
|
+
- 숫자/라틴 문자 처리
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from typing import List, Tuple
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Morpheme:
|
|
20
|
+
"""형태소"""
|
|
21
|
+
surface: str
|
|
22
|
+
lemma: str
|
|
23
|
+
pos: str
|
|
24
|
+
start: int
|
|
25
|
+
end: int
|
|
26
|
+
|
|
27
|
+
def __repr__(self):
|
|
28
|
+
return f"{self.surface}/{self.pos}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ThaiNativeAnalyzer:
|
|
32
|
+
"""
|
|
33
|
+
태국어 자체 형태소 분석기
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
analyzer = ThaiNativeAnalyzer()
|
|
37
|
+
result = analyzer.analyze("สวัสดีครับ")
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
self._build_dictionary()
|
|
42
|
+
|
|
43
|
+
def _build_dictionary(self):
|
|
44
|
+
"""사전 구축"""
|
|
45
|
+
|
|
46
|
+
# 고빈도 태국어 단어 사전 (확장)
|
|
47
|
+
self.dictionary = {
|
|
48
|
+
# 인사/기본
|
|
49
|
+
'สวัสดี': 'INTJ', 'ครับ': 'PART', 'ค่ะ': 'PART', 'ขอบคุณ': 'V',
|
|
50
|
+
'ใช่': 'V', 'ไม่': 'ADV', 'ไม่ใช่': 'V', 'มี': 'V', 'ไม่มี': 'V',
|
|
51
|
+
'หวัดดี': 'INTJ', 'บาย': 'INTJ', 'ลาก่อน': 'INTJ', 'โทษ': 'V',
|
|
52
|
+
'ขอโทษ': 'V', 'ยินดี': 'V', 'เชิญ': 'V',
|
|
53
|
+
|
|
54
|
+
# 대명사
|
|
55
|
+
'ผม': 'PRON', 'ฉัน': 'PRON', 'คุณ': 'PRON', 'เขา': 'PRON',
|
|
56
|
+
'เรา': 'PRON', 'พวกเขา': 'PRON', 'มัน': 'PRON', 'ตัวเอง': 'PRON',
|
|
57
|
+
'ใคร': 'PRON', 'อะไร': 'PRON', 'ที่ไหน': 'PRON', 'เมื่อไหร่': 'PRON',
|
|
58
|
+
'ดิฉัน': 'PRON', 'ข้าพเจ้า': 'PRON', 'ท่าน': 'PRON', 'เธอ': 'PRON',
|
|
59
|
+
'พวกเรา': 'PRON', 'ตัว': 'PRON', 'นี่': 'PRON', 'นั่น': 'PRON',
|
|
60
|
+
'โน่น': 'PRON', 'ไหน': 'PRON', 'อย่างไร': 'PRON', 'ทำไม': 'PRON',
|
|
61
|
+
|
|
62
|
+
# 동사 (확장)
|
|
63
|
+
'กิน': 'V', 'ดื่ม': 'V', 'ไป': 'V', 'มา': 'V', 'ทำ': 'V',
|
|
64
|
+
'พูด': 'V', 'เห็น': 'V', 'ฟัง': 'V', 'อ่าน': 'V', 'เขียน': 'V',
|
|
65
|
+
'รัก': 'V', 'ชอบ': 'V', 'เกลียด': 'V', 'ต้องการ': 'V', 'อยาก': 'V',
|
|
66
|
+
'รู้': 'V', 'คิด': 'V', 'เชื่อ': 'V', 'หวัง': 'V', 'กลัว': 'V',
|
|
67
|
+
'เป็น': 'V', 'อยู่': 'V', 'นอน': 'V', 'นั่ง': 'V', 'ยืน': 'V',
|
|
68
|
+
'วิ่ง': 'V', 'เดิน': 'V', 'ขับ': 'V', 'บิน': 'V', 'ว่ายน้ำ': 'V',
|
|
69
|
+
'เรียน': 'V', 'สอน': 'V', 'เล่น': 'V', 'ทำงาน': 'V', 'พักผ่อน': 'V',
|
|
70
|
+
'ซื้อ': 'V', 'ขาย': 'V', 'จ่าย': 'V', 'ให้': 'V', 'รับ': 'V',
|
|
71
|
+
'เปิด': 'V', 'ปิด': 'V', 'เริ่ม': 'V', 'หยุด': 'V', 'จบ': 'V',
|
|
72
|
+
'หา': 'V', 'พบ': 'V', 'เจอ': 'V', 'ค้นหา': 'V', 'ส่ง': 'V',
|
|
73
|
+
'ถาม': 'V', 'ตอบ': 'V', 'บอก': 'V', 'เรียก': 'V', 'ร้อง': 'V',
|
|
74
|
+
'ดู': 'V', 'มอง': 'V', 'จ้อง': 'V', 'สังเกต': 'V', 'ตรวจ': 'V',
|
|
75
|
+
'ช่วย': 'V', 'แก้': 'V', 'สร้าง': 'V', 'ทำลาย': 'V', 'ซ่อม': 'V',
|
|
76
|
+
'ใช้': 'V', 'เลือก': 'V', 'ตัดสินใจ': 'V', 'ลอง': 'V', 'พยายาม': 'V',
|
|
77
|
+
'จำ': 'V', 'ลืม': 'V', 'เข้าใจ': 'V', 'รู้สึก': 'V', 'หมายความ': 'V',
|
|
78
|
+
'เยือน': 'V', 'เดินทาง': 'V', 'ท่องเที่ยว': 'V', 'แวะ': 'V', 'เข้า': 'V',
|
|
79
|
+
'ออก': 'V', 'ขึ้น': 'V', 'ลง': 'V', 'ผ่าน': 'V', 'ข้าม': 'V',
|
|
80
|
+
|
|
81
|
+
# 명사 - 일반 (추가)
|
|
82
|
+
'ทาง': 'N', 'ที่': 'N', 'แห่ง': 'N', 'จุด': 'N', 'ด้าน': 'N',
|
|
83
|
+
|
|
84
|
+
# 형용사 (확장)
|
|
85
|
+
'ดี': 'ADJ', 'ไม่ดี': 'ADJ', 'สวย': 'ADJ', 'หล่อ': 'ADJ',
|
|
86
|
+
'ใหญ่': 'ADJ', 'เล็ก': 'ADJ', 'สูง': 'ADJ', 'ต่ำ': 'ADJ',
|
|
87
|
+
'ร้อน': 'ADJ', 'เย็น': 'ADJ', 'หนาว': 'ADJ', 'อุ่น': 'ADJ',
|
|
88
|
+
'เร็ว': 'ADJ', 'ช้า': 'ADJ', 'ใหม่': 'ADJ', 'เก่า': 'ADJ',
|
|
89
|
+
'แพง': 'ADJ', 'ถูก': 'ADJ', 'ยาก': 'ADJ', 'ง่าย': 'ADJ',
|
|
90
|
+
'สำคัญ': 'ADJ', 'น่าสนใจ': 'ADJ', 'สนุก': 'ADJ', 'เบื่อ': 'ADJ',
|
|
91
|
+
'ยาว': 'ADJ', 'สั้น': 'ADJ', 'กว้าง': 'ADJ', 'แคบ': 'ADJ',
|
|
92
|
+
'หนัก': 'ADJ', 'เบา': 'ADJ', 'แข็ง': 'ADJ', 'อ่อน': 'ADJ',
|
|
93
|
+
'สะอาด': 'ADJ', 'สกปรก': 'ADJ', 'สด': 'ADJ', 'เน่า': 'ADJ',
|
|
94
|
+
'ถูกต้อง': 'ADJ', 'ผิด': 'ADJ', 'จริง': 'ADJ', 'เท็จ': 'ADJ',
|
|
95
|
+
'พิเศษ': 'ADJ', 'ธรรมดา': 'ADJ', 'แปลก': 'ADJ', 'ปกติ': 'ADJ',
|
|
96
|
+
|
|
97
|
+
# 명사 - 장소 (확장)
|
|
98
|
+
'คน': 'N', 'บ้าน': 'N', 'รถ': 'N', 'น้ำ': 'N', 'อาหาร': 'N',
|
|
99
|
+
'เมือง': 'N', 'ประเทศ': 'N', 'โลก': 'N', 'ทะเล': 'N', 'ภูเขา': 'N',
|
|
100
|
+
'โรงเรียน': 'N', 'มหาวิทยาลัย': 'N', 'วิทยาลัย': 'N', 'ห้องเรียน': 'N',
|
|
101
|
+
'โรงพยาบาล': 'N', 'คลินิก': 'N', 'ร้านขายยา': 'N', 'ห้องน้ำ': 'N',
|
|
102
|
+
'โรงแรม': 'N', 'ร้านอาหาร': 'N', 'ร้านกาแฟ': 'N', 'ตลาด': 'N',
|
|
103
|
+
'ห้างสรรพสินค้า': 'N', 'ร้านค้า': 'N', 'ธนาคาร': 'N', 'ไปรษณีย์': 'N',
|
|
104
|
+
'สถานี': 'N', 'สนามบิน': 'N', 'ท่าเรือ': 'N', 'ถนน': 'N',
|
|
105
|
+
'สวนสาธารณะ': 'N', 'สวน': 'N', 'วัด': 'N', 'โบสถ์': 'N',
|
|
106
|
+
'สำนักงาน': 'N', 'โรงงาน': 'N', 'บริษัท': 'N', 'ห้อง': 'N',
|
|
107
|
+
|
|
108
|
+
# 명사 - 시간/기간
|
|
109
|
+
'วัน': 'N', 'เดือน': 'N', 'ปี': 'N', 'เวลา': 'N', 'ชั่วโมง': 'N',
|
|
110
|
+
'นาที': 'N', 'วินาที': 'N', 'สัปดาห์': 'N', 'ศตวรรษ': 'N',
|
|
111
|
+
'วันนี้': 'N', 'พรุ่งนี้': 'N', 'เมื่อวาน': 'N', 'ตอนนี้': 'N',
|
|
112
|
+
'เช้า': 'N', 'บ่าย': 'N', 'เย็น': 'N', 'กลางคืน': 'N',
|
|
113
|
+
'วันจันทร์': 'N', 'วันอังคาร': 'N', 'วันพุธ': 'N', 'วันพฤหัส': 'N',
|
|
114
|
+
'วันพฤหัสบดี': 'N', 'วันศุกร์': 'N', 'วันเสาร์': 'N', 'วันอาทิตย์': 'N',
|
|
115
|
+
|
|
116
|
+
# 명사 - 가족/사람
|
|
117
|
+
'พ่อ': 'N', 'แม่': 'N', 'ลูก': 'N', 'พี่': 'N', 'น้อง': 'N',
|
|
118
|
+
'เพื่อน': 'N', 'ครู': 'N', 'หมอ': 'N', 'ตำรวจ': 'N', 'ทหาร': 'N',
|
|
119
|
+
'ลูกชาย': 'N', 'ลูกสาว': 'N', 'พี่ชาย': 'N', 'พี่สาว': 'N',
|
|
120
|
+
'น้องชาย': 'N', 'น้องสาว': 'N', 'ปู่': 'N', 'ย่า': 'N',
|
|
121
|
+
'ตา': 'N', 'ยาย': 'N', 'ลุง': 'N', 'ป้า': 'N', 'อา': 'N',
|
|
122
|
+
'สามี': 'N', 'ภรรยา': 'N', 'แฟน': 'N', 'คู่รัก': 'N',
|
|
123
|
+
'นักเรียน': 'N', 'นักศึกษา': 'N', 'อาจารย์': 'N', 'ผู้อำนวยการ': 'N',
|
|
124
|
+
'พนักงาน': 'N', 'ลูกค้า': 'N', 'ผู้จัดการ': 'N', 'เจ้าของ': 'N',
|
|
125
|
+
|
|
126
|
+
# 명사 - 물건
|
|
127
|
+
'โทรศัพท์': 'N', 'คอมพิวเตอร์': 'N', 'โน๊ตบุ๊ค': 'N', 'แท็บเล็ต': 'N',
|
|
128
|
+
'หนังสือ': 'N', 'สมุด': 'N', 'ปากกา': 'N', 'ดินสอ': 'N',
|
|
129
|
+
'เสื้อ': 'N', 'กางเกง': 'N', 'รองเท้า': 'N', 'กระเป๋า': 'N',
|
|
130
|
+
'แว่นตา': 'N', 'นาฬิกา': 'N', 'กุญแจ': 'N', 'เงิน': 'N',
|
|
131
|
+
'อาหาร': 'N', 'ข้าว': 'N', 'ผัก': 'N', 'ผลไม้': 'N', 'เนื้อ': 'N',
|
|
132
|
+
'ไก่': 'N', 'หมู': 'N', 'ปลา': 'N', 'กุ้ง': 'N', 'ไข่': 'N',
|
|
133
|
+
|
|
134
|
+
# 명사 - 추상/자연
|
|
135
|
+
'ความรัก': 'N', 'ความสุข': 'N', 'ความเศร้า': 'N', 'ความโกรธ': 'N',
|
|
136
|
+
'ความหวัง': 'N', 'ความกลัว': 'N', 'ความฝัน': 'N', 'ความคิด': 'N',
|
|
137
|
+
'ปัญหา': 'N', 'คำตอบ': 'N', 'คำถาม': 'N', 'ความหมาย': 'N',
|
|
138
|
+
'เรื่อง': 'N', 'เหตุการณ์': 'N', 'สถานการณ์': 'N', 'โอกาส': 'N',
|
|
139
|
+
'อากาศ': 'N', 'ฝน': 'N', 'แดด': 'N', 'ลม': 'N', 'เมฆ': 'N',
|
|
140
|
+
'ฟ้า': 'N', 'ดิน': 'N', 'ไฟ': 'N', 'ต้นไม้': 'N', 'ดอกไม้': 'N',
|
|
141
|
+
'สัตว์': 'N', 'หมา': 'N', 'แมว': 'N', 'นก': 'N', 'ช้าง': 'N',
|
|
142
|
+
|
|
143
|
+
# 지명 (확장)
|
|
144
|
+
'กรุงเทพ': 'PROPN', 'กรุงเทพมหานคร': 'PROPN', 'เชียงใหม่': 'PROPN',
|
|
145
|
+
'ภูเก็ต': 'PROPN', 'พัทยา': 'PROPN', 'หัวหิน': 'PROPN',
|
|
146
|
+
'ไทย': 'PROPN', 'ประเทศไทย': 'PROPN', 'อเมริกา': 'PROPN',
|
|
147
|
+
'จีน': 'PROPN', 'ญี่ปุ่น': 'PROPN', 'เกาหลี': 'PROPN',
|
|
148
|
+
'อังกฤษ': 'PROPN', 'ฝรั่งเศส': 'PROPN', 'เยอรมัน': 'PROPN',
|
|
149
|
+
'รัสเซีย': 'PROPN', 'อินเดีย': 'PROPN', 'ออสเตรเลีย': 'PROPN',
|
|
150
|
+
'สิงคโปร์': 'PROPN', 'มาเลเซีย': 'PROPN', 'เวียดนาม': 'PROPN',
|
|
151
|
+
'ลาว': 'PROPN', 'กัมพูชา': 'PROPN', 'พม่า': 'PROPN',
|
|
152
|
+
|
|
153
|
+
# News/Wiki-high frequency (helps segmentation quality immediately)
|
|
154
|
+
'ประชากร': 'N',
|
|
155
|
+
'เมืองหลวง': 'N',
|
|
156
|
+
'ราชอาณาจักร': 'N',
|
|
157
|
+
'รัฐบาล': 'N',
|
|
158
|
+
|
|
159
|
+
# 조사/전치사/접속사
|
|
160
|
+
'ที่': 'PART', 'ของ': 'PART', 'ใน': 'ADP', 'บน': 'ADP',
|
|
161
|
+
'ใต้': 'ADP', 'หน้า': 'ADP', 'หลัง': 'ADP', 'ข้าง': 'ADP',
|
|
162
|
+
'และ': 'CCONJ', 'หรือ': 'CCONJ', 'แต่': 'CCONJ', 'เพราะ': 'SCONJ',
|
|
163
|
+
'ถ้า': 'SCONJ', 'เมื่อ': 'SCONJ', 'ตอน': 'SCONJ', 'ขณะ': 'SCONJ',
|
|
164
|
+
'กับ': 'ADP', 'จาก': 'ADP', 'ถึง': 'ADP', 'เพื่อ': 'ADP',
|
|
165
|
+
'โดย': 'ADP', 'ตาม': 'ADP', 'ระหว่าง': 'ADP', 'ต่อ': 'ADP',
|
|
166
|
+
'แม้': 'SCONJ', 'แม้ว่า': 'SCONJ', 'เพราะว่า': 'SCONJ', 'เนื่องจาก': 'SCONJ',
|
|
167
|
+
'ดังนั้น': 'ADV', 'ฉะนั้น': 'ADV', 'อย่างไรก็ตาม': 'ADV',
|
|
168
|
+
|
|
169
|
+
# 수사
|
|
170
|
+
'หนึ่ง': 'NUM', 'สอง': 'NUM', 'สาม': 'NUM', 'สี่': 'NUM', 'ห้า': 'NUM',
|
|
171
|
+
'หก': 'NUM', 'เจ็ด': 'NUM', 'แปด': 'NUM', 'เก้า': 'NUM', 'สิบ': 'NUM',
|
|
172
|
+
'ร้อย': 'NUM', 'พัน': 'NUM', 'หมื่น': 'NUM', 'แสน': 'NUM', 'ล้าน': 'NUM',
|
|
173
|
+
'ยี่สิบ': 'NUM', 'สามสิบ': 'NUM', 'สี่สิบ': 'NUM', 'ห้าสิบ': 'NUM',
|
|
174
|
+
'หกสิบ': 'NUM', 'เจ็ดสิบ': 'NUM', 'แปดสิบ': 'NUM', 'เก้าสิบ': 'NUM',
|
|
175
|
+
'ที่หนึ่ง': 'NUM', 'ที่สอง': 'NUM', 'ที่สาม': 'NUM', 'แรก': 'NUM',
|
|
176
|
+
|
|
177
|
+
# 부사 (확장)
|
|
178
|
+
'มาก': 'ADV', 'น้อย': 'ADV', 'เสมอ': 'ADV', 'บ่อย': 'ADV',
|
|
179
|
+
'แล้ว': 'ADV', 'ก่อน': 'ADV', 'หลัง': 'ADV', 'เดี๋ยว': 'ADV',
|
|
180
|
+
'ก็': 'ADV', 'จะ': 'AUX', 'ได้': 'AUX', 'อาจ': 'AUX',
|
|
181
|
+
'คง': 'AUX', 'ต้อง': 'AUX', 'ควร': 'AUX', 'น่า': 'AUX',
|
|
182
|
+
'เคย': 'AUX', 'กำลัง': 'AUX', 'ยัง': 'ADV', 'เพิ่ง': 'ADV',
|
|
183
|
+
'ทันที': 'ADV', 'ตลอด': 'ADV', 'บางที': 'ADV', 'อาจจะ': 'ADV',
|
|
184
|
+
'แน่นอน': 'ADV', 'แน่': 'ADV', 'จริงๆ': 'ADV', 'จังๆ': 'ADV',
|
|
185
|
+
'ค่อนข้าง': 'ADV', 'เกือบ': 'ADV', 'ประมาณ': 'ADV', 'ราว': 'ADV',
|
|
186
|
+
|
|
187
|
+
# 기타
|
|
188
|
+
'นาย': 'N', 'นาง': 'N', 'นางสาว': 'N',
|
|
189
|
+
'รัฐมนตรี': 'N', 'นายกรัฐมนตรี': 'N', 'ประธานาธิบดี': 'N',
|
|
190
|
+
'กล่าว': 'V', 'ปราศรัย': 'V', 'ประกาศ': 'V', 'ประชุม': 'V',
|
|
191
|
+
'โรง': 'N', 'เรียน': 'V', # Fallback components
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# 최대 단어 길이
|
|
195
|
+
self.max_word_len = max(len(w) for w in self.dictionary) if self.dictionary else 10
|
|
196
|
+
|
|
197
|
+
# 태국 문자 패턴
|
|
198
|
+
self.thai = re.compile(r'[\u0e00-\u0e7f]+')
|
|
199
|
+
|
|
200
|
+
def analyze(self, text: str) -> List[Morpheme]:
|
|
201
|
+
"""분석"""
|
|
202
|
+
if not text:
|
|
203
|
+
return []
|
|
204
|
+
|
|
205
|
+
result = []
|
|
206
|
+
pos = 0
|
|
207
|
+
|
|
208
|
+
while pos < len(text):
|
|
209
|
+
# 공백 스킵
|
|
210
|
+
if text[pos].isspace():
|
|
211
|
+
pos += 1
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
# 비태국 문자
|
|
215
|
+
if not self.thai.match(text[pos:pos+1]):
|
|
216
|
+
# 라틴/숫자
|
|
217
|
+
match = re.match(r'[a-zA-Z0-9]+', text[pos:])
|
|
218
|
+
if match:
|
|
219
|
+
word = match.group()
|
|
220
|
+
result.append(Morpheme(word, word, 'X', pos, pos + len(word)))
|
|
221
|
+
pos += len(word)
|
|
222
|
+
else:
|
|
223
|
+
pos += 1
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
# 최장일치 사전 검색
|
|
227
|
+
matched = False
|
|
228
|
+
for length in range(min(self.max_word_len, len(text) - pos), 0, -1):
|
|
229
|
+
word = text[pos:pos+length]
|
|
230
|
+
if word in self.dictionary:
|
|
231
|
+
result.append(Morpheme(
|
|
232
|
+
word, word, self.dictionary[word],
|
|
233
|
+
pos, pos + length
|
|
234
|
+
))
|
|
235
|
+
pos += length
|
|
236
|
+
matched = True
|
|
237
|
+
break
|
|
238
|
+
|
|
239
|
+
if not matched:
|
|
240
|
+
# 미등록어 그룹화:
|
|
241
|
+
# - 사전 단어의 시작점이 보이면 그 직전에서 끊는다 (boundary-safe)
|
|
242
|
+
# - 너무 길어지면 상한으로 끊는다
|
|
243
|
+
def _dict_starts_at(i: int) -> bool:
|
|
244
|
+
if i < 0 or i >= len(text):
|
|
245
|
+
return False
|
|
246
|
+
if not self.thai.match(text[i:i+1]):
|
|
247
|
+
return False
|
|
248
|
+
mx = min(self.max_word_len, len(text) - i)
|
|
249
|
+
for L in range(mx, 0, -1):
|
|
250
|
+
if text[i:i+L] in self.dictionary:
|
|
251
|
+
return True
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
max_unknown = 12
|
|
255
|
+
end_pos = min(len(text), pos + 1)
|
|
256
|
+
while end_pos < len(text) and self.thai.match(text[end_pos:end_pos+1]):
|
|
257
|
+
# If a known word starts here, stop before it.
|
|
258
|
+
if _dict_starts_at(end_pos):
|
|
259
|
+
break
|
|
260
|
+
end_pos += 1
|
|
261
|
+
if end_pos - pos >= max_unknown:
|
|
262
|
+
break
|
|
263
|
+
|
|
264
|
+
# If we still ended up right before a known word, good.
|
|
265
|
+
# If not, keep the span as-is.
|
|
266
|
+
word = text[pos:end_pos]
|
|
267
|
+
result.append(Morpheme(word, word, 'N', pos, end_pos))
|
|
268
|
+
pos = end_pos
|
|
269
|
+
|
|
270
|
+
return result
|
|
271
|
+
|
|
272
|
+
def segment(self, text: str) -> List[str]:
|
|
273
|
+
"""단어 분리"""
|
|
274
|
+
return [m.surface for m in self.analyze(text)]
|