ai_dataset/main_qa_data/normalizer.py

1367 lines
75 KiB
Python
Raw Permalink Normal View History

2024-09-17 16:57:26 +00:00
from re import sub
import copy
import os
from tokenizer import Tokenizer
from data_helper import DataHelper
import general_functions as gf
import traceback,sys
class Normalizer():
def __init__(self,
half_space_char='\u200c',
date_normalizing_needed=False,
pinglish_conversion_needed=False,
train_file_path="resource/tokenizer/Bijan_khan_chunk.txt",
token_merger_path="resource/tokenizer/TokenMerger.pckl"):
self.dir_path = os.path.dirname(os.path.realpath(__file__)) + "/"
self.dic1_path = self.dir_path + 'resource/normalizer/Dic1_new.txt'
self.dic2_path = self.dir_path + 'resource/normalizer/Dic2_new.txt'
self.dic3_path = self.dir_path + 'resource/normalizer/Dic3_new.txt'
self.dic1 = self.load_dictionary(self.dic1_path)
self.dic2 = self.load_dictionary(self.dic2_path)
self.dic3 = self.load_dictionary(self.dic3_path)
self.date_normalizing_needed = date_normalizing_needed
self.pinglish_conversion_needed = pinglish_conversion_needed
self.data_helper = DataHelper()
if self.date_normalizing_needed or self.pinglish_conversion_needed:
self.tokenizer = Tokenizer()
self.date_normalizer = DateNormalizer()
self.pinglish_conversion = PinglishNormalizer()
def load_dictionary(self, file_path):
dict = {}
with open(file_path, 'r', encoding='utf-8') as f:
g = f.readlines()
for Wrds in g:
wrd = Wrds.split(' ')
dict[wrd[0].strip()] = sub('\n', '', wrd[1].strip())
return dict
def sub_alphabets(self, doc_string):
# try:
# doc_string = doc_string.decode('utf-8')
# except UnicodeEncodeError:
# pass
a0 = "ء"
b0 = "ئ"
c0 = sub(a0, b0, doc_string)
a1 = r"ٲ|ٱ|إ|"
a11 = r"ﺁ|آ"
b1 = r"ا"
b11 = r"آ"
c11 = sub(a11, b11, c0)
c1 = sub(a1, b1, c11)
a2 = r"ﺐ|ﺏ|ﺑ"
b2 = r"ب"
c2 = sub(a2, b2, c1)
a3 = r"ﭖ|ﭗ|ﭙ|ﺒ|ﭘ"
b3 = r"پ"
c3 = sub(a3, b3, c2)
a4 = r"ﭡ|ٺ|ٹ|ﭞ|ٿ|ټ|ﺕ|ﺗ|ﺖ|ﺘ"
b4 = r"ت"
c4 = sub(a4, b4, c3)
a5 = r"ﺙ|ﺛ"
b5 = r"ث"
c5 = sub(a5, b5, c4)
a6 = r"ﺝ|ڃ|ﺠ|ﺟ"
b6 = r"ج"
c6 = sub(a6, b6, c5)
a7 = r"ڃ|ﭽ|ﭼ"
b7 = r"چ"
c7 = sub(a7, b7, c6)
a8 = r"ﺢ|ﺤ|څ|ځ|ﺣ"
b8 = r"ح"
c8 = sub(a8, b8, c7)
a9 = r"ﺥ|ﺦ|ﺨ|ﺧ"
b9 = r"خ"
c9 = sub(a9, b9, c8)
a10 = r"ڏ|ډ|ﺪ|ﺩ"
b10 = r"د"
c10 = sub(a10, b10, c9)
a11 = r"ﺫ|ﺬ|ﻧ"
b11 = r"ذ"
c11 = sub(a11, b11, c10)
a12 = r"ڙ|ڗ|ڒ|ڑ|ڕ|ﺭ|ﺮ"
b12 = r"ر"
c12 = sub(a12, b12, c11)
a13 = r"ﺰ|ﺯ"
b13 = r"ز"
c13 = sub(a13, b13, c12)
a14 = r""
b14 = r"ژ"
c14 = sub(a14, b14, c13)
a15 = r"ݭ|ݜ|ﺱ|ﺲ|ښ|ﺴ|ﺳ"
b15 = r"س"
c15 = sub(a15, b15, c14)
a16 = r"ﺵ|ﺶ|ﺸ|ﺷ"
b16 = r"ش"
c16 = sub(a16, b16, c15)
a17 = r"ﺺ|ﺼ|ﺻ"
b17 = r"ص"
c17 = sub(a17, b17, c16)
a18 = r"ﺽ|ﺾ|ﺿ|ﻀ"
b18 = r"ض"
c18 = sub(a18, b18, c17)
a19 = r"ﻁ|ﻂ|ﻃ|ﻄ"
b19 = r"ط"
c19 = sub(a19, b19, c18)
a20 = r"ﻆ|ﻇ|ﻈ"
b20 = r"ظ"
c20 = sub(a20, b20, c19)
a21 = r"ڠ|ﻉ|ﻊ|ﻋ"
b21 = r"ع"
c21 = sub(a21, b21, c20)
a22 = r"ﻎ|ۼ|ﻍ|ﻐ|ﻏ"
b22 = r"غ"
c22 = sub(a22, b22, c21)
a23 = r"ﻒ|ﻑ|ﻔ|ﻓ"
b23 = r"ف"
c23 = sub(a23, b23, c22)
a24 = r"ﻕ|ڤ|ﻖ|ﻗ"
b24 = r"ق"
c24 = sub(a24, b24, c23)
a25 = r"ڭ|ﻚ|ﮎ|ﻜ|ﮏ|ګ|ﻛ|ﮑ|ﮐ|ڪ|ك"
b25 = r"ک"
c25 = sub(a25, b25, c24)
a26 = r"ﮚ|ﮒ|ﮓ|ﮕ|ﮔ"
b26 = r"گ"
c26 = sub(a26, b26, c25)
a27 = r"ﻝ|ﻞ|ﻠ|ڵ"
b27 = r"ل"
c27 = sub(a27, b27, c26)
a28 = r"ﻡ|ﻤ|ﻢ|ﻣ"
b28 = r"م"
c28 = sub(a28, b28, c27)
a29 = r"ڼ|ﻦ|ﻥ|ﻨ"
b29 = r"ن"
c29 = sub(a29, b29, c28)
a30 = r"ވ|ﯙ|ۈ|ۋ|ﺆ|ۊ|ۇ|ۏ|ۅ|ۉ|ﻭ|ﻮ|ؤ"
b30 = r"و"
c30 = sub(a30, b30, c29)
a31 = r"ﺔ||ھ||||ۀ|ە|ة|ہ"
b31 = r"ه"
c31 = sub(a31, b31, c30)
a32 = r"ﭛ|ﻯ|ۍ|ﻰ|ﻱ|ﻲ|ں|ﻳ|ﻴ|ﯼ|ې|ﯽ|ﯾ|ﯿ|ێ|ے|ى|ي"
b32 = r"ی"
c32 = sub(a32, b32, c31)
a33 = r'¬'
b33 = r''
c33 = sub(a33, b33, c32)
pa0 = r'•|·|●|·|・|∙|。|ⴰ'
pb0 = r'.'
pc0 = sub(pa0, pb0, c33)
pa1 = r',|٬|٫||'
pb1 = r'،'
pc1 = sub(pa1, pb1, pc0)
pa2 = r'ʕ'
pb2 = r'؟'
pc2 = sub(pa2, pb2, pc1)
na0 = r'۰|٠'
nb0 = r'0'
nc0 = sub(na0, nb0, pc2)
na1 = r'۱|١'
nb1 = r'1'
nc1 = sub(na1, nb1, nc0)
na2 = r'۲|٢'
nb2 = r'2'
nc2 = sub(na2, nb2, nc1)
na3 = r'۳|٣'
nb3 = r'3'
nc3 = sub(na3, nb3, nc2)
na4 = r'۴|٤'
nb4 = r'4'
nc4 = sub(na4, nb4, nc3)
na5 = r'۵'
nb5 = r'5'
nc5 = sub(na5, nb5, nc4)
na6 = r'۶|٦'
nb6 = r'6'
nc6 = sub(na6, nb6, nc5)
na7 = r'۷|٧'
nb7 = r'7'
nc7 = sub(na7, nb7, nc6)
na8 = r'۸|٨'
nb8 = r'8'
nc8 = sub(na8, nb8, nc7)
na9 = r'۹|٩'
nb9 = r'9'
nc9 = sub(na9, nb9, nc8)
np2 = r'²'
nm2 = r'2'
ng2 = sub(np2, nm2, nc9)
ea1 = r'|ِ|ُ|َ|ٍ|ٌ|ً|'
eb1 = r''
ec1 = sub(ea1, eb1, ng2)
ea1 = r'ـ'
eb1 = r'_'
ec2 = sub(ea1, eb1, ec1)
Sa1 = r'( )+'
Sb1 = r' '
Sc1 = sub(Sa1, Sb1, ec2)
Sa2 = r'(\n)+'
Sb2 = r'\n'
Sc2 = sub(Sa2, Sb2, Sc1)
return Sc2
def space_correction(self, doc_string):
a00 = r'^(بی|می|نمی)( )'
b00 = r'\1'
c00 = sub(a00, b00, doc_string)
a0 = r'( )(می|نمی|بی)( )'
b0 = r'\1\2'
c0 = sub(a0, b0, c00)
a1 = r'( )(هایی|ها|های|ایی|هایم|هایت|هایش|هایمان|هایتان|هایشان|ات|ان|ین' \
r'|انی|بان|ام|ای|یم|ید|اید|اند|بودم|بودی|بود|بودیم|بودید|بودند|ست)( )'
b1 = r'\2\3'
c1 = sub(a1, b1, c0)
a2 = r'( )(شده|نشده)( )'
b2 = r'\2'
c2 = sub(a2, b2, c1)
a3 = r'( )(طلبان|طلب|گرایی|گرایان|شناس|شناسی|گذاری|گذار|گذاران|شناسان|گیری|پذیری|بندی|آوری|سازی|' \
r'بندی|کننده|کنندگان|گیری|پرداز|پردازی|پردازان|آمیز|سنجی|ریزی|داری|دهنده|آمیز|پذیری' \
r'|پذیر|پذیران|گر|ریز|ریزی|رسانی|یاب|یابی|گانه|گانه‌ای|انگاری|گا|بند|رسانی|دهندگان|دار)( )'
b3 = r'\2\3'
c3 = sub(a3, b3, c2)
return c3
def space_correction_plus1(self, doc_string):
out_sentences = ''
for wrd in doc_string.split(' '):
try:
out_sentences = out_sentences + ' ' + self.dic1[wrd]
except KeyError:
out_sentences = out_sentences + ' ' + wrd
return out_sentences
def space_correction_plus2(self, doc_string):
out_sentences = ''
wrds = doc_string.split(' ')
L = wrds.__len__()
if L < 2:
return doc_string
cnt = 1
for i in range(0, L - 1):
w = wrds[i] + wrds[i + 1]
try:
out_sentences = out_sentences + ' ' + self.dic2[w]
cnt = 0
except KeyError:
if cnt == 1:
out_sentences = out_sentences + ' ' + wrds[i]
cnt = 1
if cnt == 1:
out_sentences = out_sentences + ' ' + wrds[i + 1]
return out_sentences
def space_correction_plus3(self, doc_string):
# Dict = {'گفتوگو': 'گفت‌وگو'}
out_sentences = ''
wrds = doc_string.split(' ')
L = wrds.__len__()
if L < 3:
return doc_string
cnt = 1
cnt2 = 0
for i in range(0, L - 2):
w = wrds[i] + wrds[i + 1] + wrds[i + 2]
try:
out_sentences = out_sentences + ' ' + self.dic3[w]
cnt = 0
cnt2 = 2
except KeyError:
if cnt == 1 and cnt2 == 0:
out_sentences = out_sentences + ' ' + wrds[i]
else:
cnt2 -= 1
cnt = 1
if cnt == 1 and cnt2 == 0:
out_sentences = out_sentences + ' ' + wrds[i + 1] + ' ' + wrds[i + 2]
elif cnt == 1 and cnt2 == 1:
out_sentences = out_sentences + ' ' + wrds[i + 2]
return out_sentences
def normalize(self, doc_string, new_line_elimination=False, return_dates = False):
normalized_string = gf.normalYehKe(doc_string)
normalized_string = self.sub_alphabets(doc_string)
#normalized_string = self.data_helper.clean_text(normalized_string, new_line_elimination).strip()
#normalized_string = self.space_correction(self.space_correction_plus1(self.space_correction_plus2(self.space_correction_plus3(normalized_string)))).strip()
# !!!آئین نامه را به آئین‌نامه تبدیل می کند و دو توکن را به یکی تبدیل می کند
#normalized_string = (self.space_correction_plus1(self.space_correction_plus2(self.space_correction_plus3(normalized_string)))).strip()
#if self.pinglish_conversion_needed:
#normalized_string = self.pinglish_conversion.pingilish2persian(self.tokenizer.tokenize_words(normalized_string))
if self.date_normalizing_needed:
token_list = self.tokenizer.tokenize_words(normalized_string)
# نرمالایز کردن اعداد حروفی و ...
normalized_string, additional_token_index_array = self.date_normalizer.normalize_numbers2(token_list)
# نرمالایز و تشخیص تاریخ ها
normalized_string, dates, recognized_dates = self.date_normalizer.general_normalize_date(normalized_string, additional_token_index_array)
#normalized_string_list = normalized_string.strip().split()
#normalized_string, dates, recognized_dates = self.date_normalizer.normalize_dates(normalized_string_list, additional_token_index_array, token_list_len= len(normalized_string_list), previous_slice_index_array=[0,])
# کنترل اعدادی که پشت سر هم آمده اند و با واو از هم جدا شده اند
normalized_string, recognized_numbers = self.date_normalizer.handle_continuous_numbers(normalized_string)
# در مواردی مانند وسیصد که واو به عدد سیصد چسبیده، ابتدا این دو را از هم جدا می کنیم
# تا عدد اصلی را به دست بیاوریم. در این صورت یک توکن اضافه تولید می شود که با این متد، توکن های اضافی را حذف می کنیم
normalized_string =self.date_normalizer.remove_additional_tokens(normalized_string.split(), additional_token_index_array)
if return_dates:
# مرتب کردن آرایه تاریخ های پیدا شده بر اساس توکن شروع عبارت حاوی تاریخ
recognized_dates.sort(key=lambda x: int(x['start_date_token_index']), reverse=False)
return normalized_string, dates, recognized_dates, recognized_numbers
else:
return normalized_string
class DateNormalizer():
def __init__(self):
self.month_dict = { "فروردین": 1,"فروردینماه": 1,'فروردین\u200cماه':1,
"اردیبهشت": 2,"اردیبهشتماه": 2,'اردیبهشت\u200cماه':2,
"خرداد": 3,"خردادماه": 3,
"تیر": 4,"تیرماه": 4,
"مرداد": 5,"مردادماه": 5,
"شهریور": 6,"شهریورماه": 6,
"مهر": 7,"مهرماه": 7,
"آبان": 8,"آبانماه": 8,'آبان\u200cماه':8,
"آذر": 9,"آذرماه": 9,
"دی": 10,"دیماه": 10,'دی\u200cماه':10,
"بهمن": 11,"بهمنماه": 11,'بهمن\u200cماه':11,
"اسفند": 12,"اسفندماه": 12}
self.num_dict = {"صد": 100,"وصد": 100, "یکصد": 100, "ویکصد": 100, "هزار": 1000,"وهزار": 1000,
"یکهزار": 1000,"ویکهزار": 1000,
"میلیون": 1000000,"ملیون": 1000000, "ومیلیون": 1000000,"وملیون": 1000000,
"یکمیلیون": 1000000,"یکملیون": 1000000,
"ویکمیلیون": 1000000,"ویکملیون": 1000000, "دویست": 200,"ودویست": 200,
"ده": 10,"وده": 10, "نه": 9,"ونه": 9, "هشت": 8,"وهشت": 8, "هفت": 7,"وهفت": 7,
"شش": 6,"وشش": 6, "پنج": 5,"وپنج": 5,"چهار": 4,"وچهار": 4, "سه": 3,"وسه": 3,
"دو": 2,"ودو": 2, "یک": 1,"ویک": 1, "یازده": 11,"ویازده": 11, "سیزده": 13, "وسیزده": 13,
"چهارده": 14, "دوازده": 12, "پانزده": 15, "شانزده": 16, "هفده": 17,"هیفده": 17,
"وچهارده": 14, "ودوازده": 12, "وپانزده": 15, "وشانزده": 16, "وهفده": 17,"وهیفده": 17,
"هجده": 18,"هیجده": 18, "نوزده": 19, "بیست": 20, "سی": 30, "چهل": 40, "پنجاه": 50,
"وهجده": 18,"وهیجده": 18, "ونوزده": 19, "وبیست": 20, "وسی": 30, "وچهل": 40, "وپنجاه": 50,
"شصت": 60, "هفتاد": 70, "نود": 90, "سیصد": 300, "چهارصد": 400,
"وشصت": 60, "وهفتاد": 70, "ونود": 90, "وسیصد": 300, "وچهارصد": 400,
"پانصد": 500, "ششصد": 600, "هفتصد": 700, "هشتصد": 800, "نهصد": 900,
"وپانصد": 500, "وششصد": 600, "وهفتصد": 700, "وهشتصد": 800, "ونهصد": 900,
"هشتاد": 80, " ": 0, "میلیارد": 1000000000,"ملیارد": 1000000000,
"یکمیلیارد": 1000000000,"یکملیارد": 1000000000,
"وهشتاد": 80, " ": 0, "ومیلیارد": 1000000000,"وملیارد": 1000000000,
"ویکمیلیارد": 1000000000,"ویکملیارد": 1000000000,
"صدم": 100, "هزارم": 1000, "دویستم": 200,
"وصدم": 100, "وهزارم": 1000, "ودویستم": 200,
"دهم": 10, "نهم": 9, "هشتم": 8, "هفتم": 7, "ششم": 6, "پنجم": 5,
"ودهم": 10, "ونهم": 9, "وهشتم": 8, "وهفتم": 7, "وششم": 6, "وپنجم": 5,
"چهارم": 4, "سوم": 3, "دوم": 2, "یکم": 1, "اول": 1, "یازدهم": 11, "سیزدهم": 13,
"وچهارم": 4, "وسوم": 3, "ودوم": 2, "ویکم": 1, "واول": 1, "ویازدهم": 11, "وسیزدهم": 13,
"چهاردهم": 14, "دوازدهم": 12, "پانزدهم": 15, "شانزدهم": 16, "هفدهم": 17,"هیفدهم": 17,
"وچهاردهم": 14, "ودوازدهم": 12, "وپانزدهم": 15, "وشانزدهم": 16, "وهفدهم": 17,"وهیفدهم": 17,
"هجدهم": 18,"هیجدهم": 18, "نوزدهم": 19, "بیستم": 20, "چهلم": 40, "پنجاهم": 50,
"وهجدهم": 18,"وهیجدهم": 18, "ونوزدهم": 19, "وبیستم": 20, "وچهلم": 40, "وپنجاهم": 50,
"شصتم": 60, "هفتادم": 70, "نودم": 90, "سیصدم": 300, "چهارصدم": 400,
"وشصتم": 60, "وهفتادم": 70, "ونودم": 90, "وسیصدم": 300, "وچهارصدم": 400,
"پانصدم": 500, "ششصدم": 600, "هفتصدم": 700, "هشتصدم": 800, "نهصدم": 900,
"وپانصدم": 500, "وششصدم": 600, "وهفتصدم": 700, "وهشتصدم": 800, "ونهصدم": 900,
"هشتادم": 80,"وهشتادم": 80}
def find_date_part(self, token_list):
import re
for index, element in enumerate(token_list):
# بررسی الگوی تاریخ 1398/12/18
pattern_date = r'^(\d{4}\s*/\s*([1-9]|0[1-9]|1[0-2])\s*/\s*([1-9]|0[1-9]|[12][0-9]|3[01]))$'
date_pattern = re.compile(pattern_date)
match_date = date_pattern.match(element)
if match_date:
date_parts = match_date.string.split('/')
year = int(re.search(r'\d+', date_parts[0]).group())
month = int(re.search(r'\d+', date_parts[1]).group())
day = int(re.search(r'\d+', date_parts[2]).group())
# اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد
if day > 999:
day = int(re.search(r'\d+', date_parts[0]).group())
year = int(re.search(r'\d+', date_parts[2]).group())
formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day)
return formal_date, index, index, index
# 24 /12 /1401 بررسی الگوی تاریخ
pattern_date = r'\s*\d{2}\s*/\s*\d{2}\s*/\s*\d{4}\s*'
date_pattern = re.compile(pattern_date)
match_date = date_pattern.match(element)
if match_date:
date_parts = match_date.string.split('/')
year = int(re.search(r'\d+', date_parts[0]).group())
month = int(re.search(r'\d+', date_parts[1]).group())
day = int(re.search(r'\d+', date_parts[2]).group())
# اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد
if day > 999:
day = int(re.search(r'\d+', date_parts[0]).group())
year = int(re.search(r'\d+', date_parts[2]).group())
formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day)
return formal_date, index, index, index
# بررسی الگوی تاریخ 1402،10،06
patterndate2 = r'^(\d{4}\s*،\s*([1-9]|0[1-9]|1[0-2])\s*،\s*([1-9]|0[1-9]|[12][0-9]|3[01]))$'
date_pattern = re.compile(patterndate2)
match_date2 = date_pattern.match(element)
if match_date2:
date_parts = match_date2.string.split('،')
year = int(re.search(r'\d+', date_parts[0]).group())
month = int(re.search(r'\d+', date_parts[1]).group())
day = int(re.search(r'\d+', date_parts[2]).group())
# اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد
if day > 999:
day = int(re.search(r'\d+', date_parts[0]).group())
year = int(re.search(r'\d+', date_parts[2]).group())
formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day)
return formal_date, index, index, index
# بررسی الگوی تاریخ 13ر04ر1345
patterndate2 = r'^(\d{4}\s*ر\s*([1-9]|0[1-9]|1[0-2])\s*ر\s*([1-9]|0[1-9]|[12][0-9]|3[01]))$'
date_pattern = re.compile(patterndate2)
match_date2 = date_pattern.match(element)
if match_date2:
date_parts = match_date2.string.split('ر')
year = int(re.search(r'\d+', date_parts[0]).group())
month = int(re.search(r'\d+', date_parts[1]).group())
day = int(re.search(r'\d+', date_parts[2]).group())
# اگر مقداری که در روز است، چهار رقمی باشد باید مقدار روز و سال را با هم عوض کرد
if day > 999:
day = int(re.search(r'\d+', date_parts[0]).group())
year = int(re.search(r'\d+', date_parts[2]).group())
formal_date = "y" + str(year) + "m" + str(month) + "d" + str(day)
return formal_date, index, index, index
if element == "/":
if index-1 >= 0 and index+1 < len(token_list) \
and token_list[index -1].isdigit() and token_list[index+1].isdigit():
if index+3 < len(token_list) and token_list[index+2] == "/" \
and token_list[index + 3].isdigit():
if int(token_list[index-1]) < 1450 and int(token_list[index+1]) < 13 and \
int(token_list[index+3]) < 32:
formal_date = [int(token_list[index-1]), int(token_list[index+1]), int(token_list[index+3])]
formal_date = "y" + str(formal_date[0]) + "m" + str(formal_date[1]) + "d" + str(formal_date[2])
#return formal_date, index-1, index+3, index
return formal_date, index, index, index
elif int(token_list[index-1]) < 32 and int(token_list[index+1]) < 13 and \
int(token_list[index+3]) < 1450:
formal_date = [int(token_list[index-1]), int(token_list[index+1]), int(token_list[index+3])]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0])
#return formal_date, index-1, index+3, index
return formal_date, index, index, index
else:
formal_date = [int(token_list[index-1]), int(token_list[index+1]), int(token_list[index+3])]
formal_date = "num(" + str(formal_date[0]) + "/" + str(formal_date[1]) + "/" + str(formal_date[2]) + ")"
#return formal_date, index-1, index+3, index
return formal_date, index, index, index
elif (int(token_list[index-1]) < 1450 and int(token_list[index-1]) > 1250) and int(token_list[index+1]) < 13:
formal_date = [int(token_list[index-1]), int(token_list[index+ 1]), 0]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0])
#return formal_date, index-1 , index+1, index
return formal_date, index , index, index
else:
formal_date = [int(token_list[index-1]), int(token_list[index+ 1])]
formal_date = "num(" + str(formal_date[0]) + "/" + str(formal_date[1]) + ")"
#return formal_date, index-1 , index+1, index
return formal_date, index , index, index
if element in self.month_dict or element == "سال":
if index + 1 < len(token_list) and index - 1 > -2:
try:
start_date_index = end_date_index = 0
if(token_list[index + 1] == 'ماه'):
if(token_list[index + 2] == 'سال' and str(token_list[index + 3]).isdigit()):
if(int(token_list[index + 3])==1000):
# در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد
# به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم
start_date_index = index - 1
day = int(token_list[index - 1])
if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()):
day += int(token_list[index - 3])# رقم دهگان روز
start_date_index = index - 3
year = 1000
if(len(token_list)>= index + 5):
if(token_list[index + 4]=='و' and token_list[index + 5].isdigit()):
year += int(token_list[index + 5])# رقم صدگان سال
end_date_index = index + 5
if(len(token_list)>= index + 7):
if(token_list[index + 6]=='و' and token_list[index + 7].isdigit()):
year += int(token_list[index + 7])# رقم دهگان سال
end_date_index = index + 7
if(len(token_list)>= index + 9):
if(token_list[index + 8]=='و' and token_list[index + 9].isdigit()):
year += int(token_list[index + 9])# رقم یکان سال
end_date_index = index + 9
formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 8 تیر ماه سال 1000 و 300 و 20 و 5
else:
start_date_index = index - 1
end_date_index = index + 3
day = int(token_list[index - 1])
if(token_list[index - 2]=='و' and int(token_list[index - 1])<10 and int(token_list[index - 3])<31 and token_list[index - 3].isdigit()):
start_date_index = index - 3
day += int(token_list[index - 3])# رقم دهگان روز
formal_date = [day, int(self.month_dict[token_list[index]]), int(token_list[index + 3])]# مثلا 20 و 7 تیر ماه سال 1368
formal_date = [day, int(self.month_dict[token_list[index]]), int(token_list[index + 3])]# مثلا 27 تیر ماه سال 1368
elif(str(token_list[index + 2]).isdigit()):
if(int(token_list[index + 2])==1000):
if token_list[index-1].strip() == 'ام': # مثلا 30 ام تیر ماه 1000 و 300 و 80 و 2
start_date_index = index - 2
day = int(token_list[index - 2])
else:
# در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد
# به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم
start_date_index = index - 1
day = int(token_list[index - 1])
if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()):
day += int(token_list[index - 3])# رقم دهگان روز
start_date_index = index - 3
year = 1000
if(len(token_list)>= index + 4):
if(token_list[index + 3]=='و' and token_list[index + 4].isdigit()):
year += int(token_list[index + 4])# رقم صدگان سال
end_date_index = index + 4
if(len(token_list)>= index + 6):
if(token_list[index + 5]=='و' and token_list[index + 6].isdigit()):
year += int(token_list[index + 6])# رقم دهگان سال
end_date_index = index + 6
if(len(token_list)>= index + 8):
if(token_list[index + 7]=='و' and token_list[index + 8].isdigit()):
year += int(token_list[index + 8])# رقم یکان سال
end_date_index = index + 8
formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 7 تیر ماه 1000 و 300 و 20 و 5
else:
formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])]# مثلا 27 تیر ماه سال 1368
start_date_index = index - 1
end_date_index = index + 2
#formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])] # مثلا 27 تیر ماه 1368
elif(token_list[index + 1] == 'سال' and (not(token_list[index + 2]).isdigit())):
formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])] # مثلا 27 تیر سال 1368
start_date_index = index - 1
end_date_index = index + 2
else:
if(token_list[index + 1] == 'سال' and str(token_list[index + 2]).isdigit()):
if(int(token_list[index + 2])==1000):
# در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد
# به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم
start_date_index = index - 1
day = int(token_list[index - 1])
if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()):
day += int(token_list[index - 3])# رقم دهگان روز
start_date_index = index - 3
year = 1000
if(len(token_list)>= index + 4):
if(token_list[index + 3]=='و' and token_list[index + 4].isdigit()):
year += int(token_list[index + 4])# رقم صدگان سال
end_date_index = index + 4
if(len(token_list)>= index + 6):
if(token_list[index + 5]=='و' and token_list[index + 6].isdigit()):
year += int(token_list[index + 6])# # رقم دهگان سال !!! برای تاریخ بین 1399 تا 1410 هم همین روال درست بر می گرداند، هرچند منطق غلطی دارد چون مثلا سال 1402 رقم صدگان ندارد
end_date_index = index + 6
if(len(token_list)>= index + 8):
if((len(token_list)>= index + 7 and token_list[index + 7]=='و') and (len(token_list)>= index + 8 and token_list[index + 8].isdigit())):
year += int(token_list[index + 8])# رقم یکان سال
end_date_index = index + 8
formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 8 تیر ماه سال 1000 و 300 و 20 و 5
else:
formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 2])]# مثلا 27 تیر سال 1368
start_date_index = index - 1
end_date_index = index + 2
elif(str(token_list[index + 1]).isdigit()):
if(int(token_list[index + 1])==1000):
# در این حالت، امکان دارد روز مربوط به این تاریخ هم به صورت حروف در متن قانون نوشته شده باشد
# به همین دلیل ، توکن های قبل از ماه را نیز کنترل می کنیم
start_date_index = index - 1
day = int(token_list[index - 1])
if(token_list[index - 2]=='و' and token_list[index - 3].isdigit()):
day += int(token_list[index - 3])# رقم دهگان روز
start_date_index = index - 3
year = 1000
if(len(token_list)>= index + 3):
if(token_list[index + 2]=='و' and token_list[index + 3].isdigit()):
year += int(token_list[index + 3])# رقم صدگان سال
end_date_index = index + 3
if(len(token_list)>= index + 5):
if(token_list[index + 4]=='و' and token_list[index + 5].isdigit()):
year += int(token_list[index + 5])# رقم دهگان سال !!! برای تاریخ بین 1399 تا 1410 هم همین روال درست بر می گرداند، هرچند منطق غلطی دارد چون مثلا سال 1402 رقم صدگان ندارد
end_date_index = index + 5
if(len(token_list)>= index + 7):
if(token_list[index + 6]=='و' and token_list[index + 7].isdigit()):
year += int(token_list[index + 7])# رقم یکان سال
end_date_index = index + 7
formal_date = [day, int(self.month_dict[token_list[index]]), year]# مثلا 20 و 7 تیر ماه 1000 و 300 و 20 و 5
else:
formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 1])]# مثلا 27 تیر 1320
start_date_index = index - 1
end_date_index = index + 1
else:
formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), int(token_list[index + 1])]# مثلا 27 تیر 1365
start_date_index = index - 1
end_date_index = index + 1
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0])
if token_list[index - 1] and token_list[index + 1]:
return formal_date, start_date_index, end_date_index, start_date_index
except Exception as e:
error = e.args[0]
try:
formal_date = [int(token_list[index - 1]), int(self.month_dict[token_list[index]]), 0]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0])#مثلا y1358m12d0
return formal_date, index-1, index, index-1
except:
try:
# مثلا سال 1400
if token_list[index] == "سال":
formal_date = [int(token_list[index + 1]),0, 0]
formal_date = "y" + str(formal_date[0]) + "m" + str(formal_date[1]) + "d" + str(formal_date[2])# y1358m0d0
return formal_date, index, index+1, index
elif token_list[index+1] == "ماه" and index + 1 < len(token_list):
if index + 2 < len(token_list) and token_list[index + 2].isdigit() and \
int(token_list[index-2]) < 1450:
formal_date = [0,int(self.month_dict[token_list[index]]),int(token_list[index+2])]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) # y0m12d5
return formal_date, index, index+2, index
else:
formal_date = [0,int(self.month_dict[token_list[index]]),0]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) #y0m12d0
return formal_date, index, index+1, index
elif index + 1 < len(token_list) and token_list[index + 1].isdigit() and int(token_list[index-2]) < 1450:
formal_date = [0,int(self.month_dict[token_list[index]]),int(token_list[index+1])]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0]) # y0m12d5
return formal_date, index, index+1, index
elif index-2 >= 0 and index+2 < len(token_list) and \
token_list[index - 1] == "/" and token_list[index+1] == "/" and \
token_list[index - 2].isdigit() and int(token_list[index-2]) < 32 and \
token_list[index + 2].isdigit() and int(token_list[index+2]) < 1450:
formal_date = [int(token_list[index-2]),int(self.month_dict[token_list[index]]),int(token_list[index+2])]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0])
return formal_date, index-2, index+2, index-2
elif index + 2 < len(token_list) and token_list[index + 1] == 'سال' and \
token_list[index + 2].isdigit() and int(token_list[index-2]) < 1450 :
formal_date = [0,int(self.month_dict[token_list[index]]),int(token_list[index+2])]
formal_date = "y" + str(formal_date[2]) + "m" + str(formal_date[1]) + "d" + str(formal_date[0])
return formal_date, index, index+1, index
#else:
# print("Name : %s -> after1: %s -> after2: %s -> after3: %s" % (token_list[index], token_list[index+1], token_list[index+2], token_list[index+3]))
except:
pass
def general_normalize_date(self,normalized_string, additional_token_index_array):
# 24 /12 /1401 جهت بررسی الگوی تاریخ
normalized_string, recognized_dates = DateNormalizer.separated_date_format_finder(normalized_string)
normalized_string_list = normalized_string.strip().split()
# جهت یافتن فرمت های مختلف تاریخ غیر از فرمت بررسی شده در بالا
normalized_string, dates, recognized_dates_2 = \
self.normalize_dates(normalized_string_list, additional_token_index_array,
token_list_len = len(normalized_string_list), previous_slice_index_array=[0,])
# تجمیع همه تاریخ ها
for item in recognized_dates_2:
recognized_dates.append(item)
return normalized_string, dates, recognized_dates
# 24 /12 /1401 متد بررسی الگوی تاریخ
def separated_date_format_finder(normalized_string):
import re
date_pattern = r'\d{1,2} /\d{1,2} /\d{2,4}'
regex = re.compile(date_pattern)
match_dates = regex.finditer(normalized_string)
recognized_dates = []
for date_item in match_dates:
position = date_item.span()
founded_item = date_item.group()
start_index = date_item.start() + 1
end_index = date_item.end() - 1
current_date = founded_item.replace(' ', '')
date_parts = current_date.split('/')
formal_date = "y" + str(date_parts[2]) + "m" + str(date_parts[1]) + "d" + str(date_parts[0])
pattern_tokens_state = gf.token_state_finder(normalized_string, start_index, end_index)
recognized_dates.append(
{
"original_date" : founded_item,
"date" : formal_date,
"start_index" : start_index,
"end_index" : end_index,
"date_token_index" : pattern_tokens_state["start_token_state"],
"start_date_token_index": pattern_tokens_state["start_token_state"],
"end_date_token_index" : pattern_tokens_state["end_token_state"],
})
normalized_string_list = normalized_string.strip().split()
for date_item in recognized_dates:
normalized_string_list[int(date_item['date_token_index'])] = date_item['date']
normalized_string_list[int(date_item['start_date_token_index'])+1] = 'tttt'
normalized_string_list[int(date_item['end_date_token_index'])] = 'tttt'
normalized_string_temp = ''
for token in normalized_string_list:
normalized_string_temp = normalized_string_temp + ' ' + token
return normalized_string_temp.strip(), recognized_dates
def normalize_dates(self, token_list, additional_token_index_array, token_list_len, previous_slice_index_array= []):
try:
finded = self.find_date_part(token_list)
except Exception as e:
finded = None
filename = 'law_section_errors.txt'
exc_type, exc_value, exc_traceback = sys.exc_info()
frame = exc_traceback.tb_frame
function_array = traceback.extract_tb(exc_traceback)
current_function = function_array[len(function_array)-1]
error = f'''
filename : {current_function.filename}
function : {current_function.name}
err line no : {current_function.lineno}
err line : {current_function.line}
err message : {e}
'''
gf.save_error(error, filename)
recognized_dates = []
if finded != None:
date_part = finded[0]
start_date_token_index = finded[1]
end_date_token_index = finded[2]
date_token_index = finded[3]
befor_date_part = " ".join(x for x in token_list[:start_date_token_index])
after_date_part = [x for x in token_list[end_date_token_index + 1:]]
previous_slice_index = token_list_len - len(after_date_part)
previous_slice_index_array.append(previous_slice_index)
after_normalized, after_dates, recognized_dates = self.normalize_dates(after_date_part, additional_token_index_array, token_list_len, previous_slice_index_array)
if after_dates == '':
after_dates = []
if recognized_dates == '':
recognized_dates = []
after_dates.insert(0, date_part)
previous_slice_index = previous_slice_index_array.pop(len(previous_slice_index_array)-2)
recognized_dates.append(
{
"date" : date_part,
"date_token_index" : date_token_index + previous_slice_index,
"start_date_token_index": start_date_token_index + previous_slice_index,
"end_date_token_index" : end_date_token_index + previous_slice_index
})
i = 0
while((date_token_index - start_date_token_index) > i ):
befor_date_part = ''.join([befor_date_part," tttt"]) # به عنوان توکنی که از جنس تاریخ بوده t
i += 1
i = 0
while((end_date_token_index - date_token_index) > i ):
date_part = ''.join([date_part," tttt"]) # به عنوان توکنی که از جنس تاریخ بوده t
i += 1
return befor_date_part + " " + date_part + " " + after_normalized, after_dates, recognized_dates
else:
return " ".join(x for x in token_list), '',''
def list2num(self, numerical_section_list):
value = 1
l = len(numerical_section_list)>3
if l :
value = 0
for index, el in enumerate(numerical_section_list):
if self.is_number(el):
value = self.num_dict[el]
elif el == '000':
#value *= 1000
pass
elif l == True:
value += float(el)
else:
value *= float(el)
return value
def convert2num(self, numerical_section_list):
value = 0
tmp_section_list = []
for index, el in enumerate(numerical_section_list):
if self.is_number(el) or (el.replace('.', '', 1).isdigit()):
tmp_section_list.append(el)
elif el == "و":
value += self.list2num(tmp_section_list)
tmp_section_list[:] = []
if len(tmp_section_list) > 0:
value += self.list2num(tmp_section_list)
tmp_section_list[:] = []
try:
if (value-int(value) == 0):
return int(value)
else:
return value
except:
return 0
def is_number(self, word):
return word in self.num_dict
def find_number_location(self, token_list, addWithVaa = False):
start_index = 0
number_section =[]
for i , el in enumerate(token_list):
if self.is_number(el) or (el.replace('.', '', 1).isdigit()):
start_index = i
number_section.append(start_index)
break
i = start_index+1
while(i < len(token_list)):
if token_list[i] == "و" and (i+1)<len(token_list) and addWithVaa:
if self.is_number(token_list[i+1]) or (token_list[i+1].replace('.', '', 1).isdigit()):
number_section.append(i)
number_section.append(i+1)
i += 2
else:
break
elif self.is_number(token_list[i]) or (token_list[i].replace('.', '', 1).isdigit()):
number_section.append(i)
i += 1
elif token_list[i] == "/" and (i+1)<len(token_list) and token_list[i+1] == "000":
number_section.append(i)
number_section.append(i+1)
i += 2
else:
break
return number_section
def normalize_numbers(self, token_list, converted=""):
for i, el in enumerate(token_list):
if el.endswith("ین") and self.is_number(el[:-2]):
token_list[i] = el[:-2]
finded = self.find_number_location(token_list)
if len(finded) == 0:
rest_of_string = " ".join(t for t in token_list)
return converted + " " + rest_of_string
else:
numerical_subsection = [token_list[x] for x in finded]
numerical_subsection = self.convert2num(numerical_subsection)
converted = converted + " " + " ".join(x for x in token_list[:finded[0]]) + " " + str(numerical_subsection)
new_index = finded[-1] + 1
return self.normalize_numbers(token_list[new_index:], converted)
# این متد برای تبدیل اعدادی حروفی به معادل رقمی ایجاد شده است
def normalize_numbers2(self, token_list, converted=""):
additional_token_index_array = [] # آرایه ای از توکن هایی که اضافه بر اصل متن ممکن است در این متد اضافه شوند
for index, token in enumerate(token_list):
if token in self.num_dict:
if token.startswith('و'): # برای حالتی مانند وسیصد که بین واو و سیصد، فاصله ای وجود ندارد
temp_token = token[1:]
if temp_token in self.num_dict:
# در متن، واو را از عدد جدا می کنیم
# در این حالت یک توکن به متن اصلی اضافه می شود که باید کنترل شود
converted = converted + " " + "و" + " " + str(int(self.num_dict[token]))
additional_token_index_array.append(index)
else:
converted = converted + " " + str(int(self.num_dict[token]))
else:
converted = converted + " " + token
return converted, additional_token_index_array
# این متد برای ضرب اعداد متوالی با جدا کننده اسپیس ایجاد شده
def multiply_continuous_numbers(self, normalized_string):
token_list = normalized_string.strip().split()
converted_string = ''
for index,token in enumerate(token_list):
num_array = []
if token.isdigit():
#if (token_list[index+1]).isdigit():
# اگر عدد مورد نظر با فرمت بالا نبود، حالت اعداد بدون فاصله کنار هم که باید در هم ضرب شوند را بررسی می کنیم
num_array.clear()
num_array.append(int(token))
step = 1
deletting_indexes = []
num_array_sum = 1
while True:
if (token_list[index+step]).isdigit() :
num_array.append(int(token_list[index+step]))
deletting_indexes.append(index+step)
step += 1
continue
else:
break
for index in deletting_indexes:
# بازای توکن هایی که عدد هستند و به دلیل ضرب در هم شدن باید حذف شوند، رشته بی معنی زیر را جایگزین کن
token_list[index] = 'nnnn' # به معنای توکنی که از جنس عدد بوده number
for number in num_array: # ،عدادی که پشت سرهم بوده اند، بدون فاصله و در مراحل قبل پردازش نشده اند و در آرایه ذخیره شده را ضرب می کند و در یک عدد، یک کاسه می کند، مثلا 2 1000 1000000000 تومان را
num_array_sum *= number
converted_string = converted_string + " " + str(num_array_sum)
else:
converted_string = converted_string + " " + token
return converted_string
# این متد برای جمع اعداد متوالی با جدا کننده واو ایجاد شده
def sum_continuous_numbers(self, normalized_string):
token_list = normalized_string.strip().split()
converted_string = ''
operand = '+'
for index,token in enumerate(token_list):
num_array = []
if token.isdigit():
# این بخش، متن را بررسی می کند و اعداد دو یا سه رقمی و بیشتر که با واو از هم جدا شده را با هم جمع و یک کاسه می کند
if token_list[index+1]=='و': # 2 , 50 , 600 :مانند
if(int(token) % 10 != 0): # اگر عدد دو یا سه یا چهار یا ... رقمی باشد، رقم دهگاه، صدگان، هزارگان و ... باید باقیمانده شان بر 2 برابر با صفر باشد، اگر نبود، ادامه نده
converted_string = converted_string + " " + token
else:
num_array.append(int(token))
step = 1
deletting_indexes = []
num_array_sum = 0
while True:
if token_list[index+step] == 'و' :
# اگر یکی از شرط های زیر را داشت، اعدادی که با واو جدا شده اند، مربوط به تاریخ هستند و نباید جمع عددی انجام شود
if (token_list[index+step+1] == 'tttt' or token_list[index+step+1].startswith('y')):
deletting_indexes.clear()
num_array.clear()
num_array_sum = int(token)
break
elif token_list[index+step+1].isdigit() and token_list[index+step+2]=='nnnn' :
operand = '*'
num_array_sum = 1
num_array.append(int(token_list[index+step+1]))
# ذخیره کردن ایندکس هایی که جمع شده و باید به جای آن یک توکن خنثی بگذاریم
deletting_indexes.append(index+step)
deletting_indexes.append(index+step+1)
step += 3
continue
elif token_list[index+step+1].isdigit() :
num_array.append(int(token_list[index+step+1]))
# ذخیره کردن ایندکس هایی که جمع شده و باید به جای آن یک توکن خنثی بگذاریم
deletting_indexes.append(index+step)
deletting_indexes.append(index+step+1)
step += 2
continue
else:
break
for index in deletting_indexes:
# بازای توکن هایی که عدد یا واو هستند و باید حذف شوند، رشته بی معنی زیر را جایگزین کن
token_list[index] = 'nnnn' # به معنای توکنی که از جنس عدد بوده number
for number in num_array: # اعدادی که با واو پشت سرهم بوده اند و در آرایه ذخیره شده را جمع می کند و در یک عدد، یک کاسه می کند
if operand == '+':
num_array_sum += number
elif operand == '*':
num_array_sum *= number
converted_string = converted_string + " " + str(num_array_sum)
else:
converted_string = converted_string + " " + token
return converted_string
# این متد اعداد پشت سر هم که با اسپیس یا واو از هم جدا شده اند و باید تبدیل به یک عدد به صورت یک کاسه شوند را هندل می کند
def handle_continuous_numbers(self, normalized_string):
token_list = normalized_string.strip().split()
converted_string = ''
number_parts_array = []
start_token_index = 0
end_token_index = 0
recognized_numbers = []
flag = True
is_tabsare_numbers = False
is_mavad_numbers = False
is_madde_numbers = False
for token_index,token in enumerate(token_list):
if token.isdigit() :
# if token == '61' :
# print(len(token_list))
if token_index - 2 >= 0:
# تشخیص اینکه یک سلسله از اعداد که با واو از هم جدا شده اند تبصره های مربوط به یک ماده نباشند
is_tabsare_numbers = (token_list[token_index-2] + ' ' + token_list[token_index-1]=='تبصره های')
if token_index - 1 >= 0:
# تشخیص اینکه یک سلسله از اعداد که با واو از هم جدا شده اند مواد یک قانون نباشند
is_mavad_numbers = (token_list[token_index-1]=='مواد')
if token_index - 1 >= 0:
# تشخیص اینکه یک سلسله از اعداد که با واو از هم جدا شده اند ماده های یک قانون نباشند
is_madde_numbers = (token_list[token_index-1]=='ماده')
if is_tabsare_numbers or is_mavad_numbers or is_madde_numbers:
flag = False
start_token_index = end_token_index = token_index
number_parts_array.clear()
current_token_index = token_index
number_parts_array.append(token)
if current_token_index + 1 < len(token_list):
while ((flag) and (token_list[current_token_index+1] == 'و' or token_list[current_token_index+1].isdigit())):
number_parts_array.append(token_list[current_token_index+1])
current_token_index += 1
end_token_index = current_token_index
if not (current_token_index + 1 < len(token_list)):
break
final_number = number_completter(number_parts_array)
token_list[token_index] = str(final_number)
recognized_numbers.append({
'number_value' : final_number,
'number_token_list': number_parts_array,
'start_token_index': start_token_index,
'end_token_index' : end_token_index
})
# جایگذاری مقدار رشته بی ارزش به جای توکن های مربوط به عدد جاری
# به منظور اینکه متن اصلی از نظر تعداد توکن تغییر نکند
for i in range(token_index + 1 , len(number_parts_array) + token_index):
token_list[i] = 'nnnn'
converted_string = converted_string + ' ' + token_list[token_index]
if token_index + 1 < len(token_list):
if flag == False and (token_list[token_index+1]=='و'):
flag = False
else:
flag = True
else:
converted_string = converted_string + ' ' + token
return converted_string, recognized_numbers
def remove_additional_tokens(self,token_list,additional_token_index_array):
converted_string = ''
for index in additional_token_index_array:
del token_list[index]
for token in token_list:
converted_string = converted_string + ' ' + token
return converted_string
class PinglishNormalizer():
def __init__(self):
self.data_helper = DataHelper()
self.file_dir = os.path.dirname(os.path.realpath(__file__)) + "/"
self.en_dict_filename = self.file_dir + "resource/tokenizer/enDict"
self.en_dict = self.data_helper.load_var(self.en_dict_filename)
self.fa_dict_filename = self.file_dir + "resource/tokenizer/faDict"
self.fa_dict = self.data_helper.load_var(self.fa_dict_filename)
def pingilish2persian(self, pinglish_words_list):
for i, word in enumerate(pinglish_words_list):
if word in self.en_dict:
pinglish_words_list[i] = self.en_dict[word]#.decode("utf-8")
#inp = inp.replace(word, enDict[word], 1)
else:
ch = self.characterize(word)
pr = self.map_char(ch)
amir = self.make_word(pr)
for wd in amir:
am = self.escalation(wd)
asd = ''.join(am)
if asd in self.fa_dict:
pinglish_words_list[i] = asd#.decode("utf-8")
#inp = inp.replace(word, asd, 1)
inp = " ".join(x for x in pinglish_words_list)
return inp
def characterize(self, word):
list_of_char = []
i = 0
while i < len(word):
char = word[i]
sw_out = self.switcher(char)
if (sw_out == None):
esp_out = None
if(i < len(word) - 1):
esp_out = self.esp_check(word[i], word[i + 1])
if(esp_out == None):
list_of_char.append(word[i])
else:
list_of_char.append(esp_out)
i += 1
else:
list_of_char.append(sw_out)
i += 1
return list_of_char
def switcher(self, ch):
switcher = {
"c": None,
"k": None,
"z": None,
"s": None,
"g": None,
"a": None,
"u": None,
"e": None,
"o": None
}
return switcher.get(ch, ch)
def esp_check(self, char1, char2):
st = char1 + char2
if (st == "ch"):
return "ch"
elif (st == "kh"):
return "kh"
elif (st == "zh"):
return "zh"
elif (st == "sh"):
return "sh"
elif (st == "gh"):
return "gh"
elif (st == "aa"):
return "aa"
elif (st == "ee"):
return "ee"
elif (st == "oo"):
return "oo"
elif (st == "ou"):
return "ou"
else:
return None
def map_char(self, word):
listm = []
sw_out = self.map_switcher(word[0])
i = 0
if (sw_out == None):
listm.append(["ا"])
i += 1
if (word[0] == "oo"):
listm.append(["او"])
i += 1
while i < len(word):
listm.append(self.char_switcher(word[i]))
i += 1
if word[len(word) - 1] == "e":
listm.append(["ه"])
elif word[len(word) - 1] == "a":
listm.append(["ا"])
elif word[len(word) - 1] == "o":
listm.append(["و"])
elif word[len(word) - 1] == "u":
listm.append(["و"])
return listm
def map_switcher(self, ch):
switcher = {
"a": None,
"e": None,
"o": None,
"u": None,
"ee": None,
"ou": None
}
return switcher.get(ch, ch)
def make_word(self, chp):
word_list = [[]]
for char in chp:
word_list_temp = []
for tmp_word_list in word_list:
for chch in char:
tmp = copy.deepcopy(tmp_word_list)
tmp.append(chch)
word_list_temp.append(tmp)
word_list = word_list_temp
return word_list
def escalation(self, word):
tmp = []
i = 0
t = len(word)
while i < t - 1:
tmp.append(word[i])
if word[i] == word[i + 1]:
i += 1
i += 1
if i != t:
tmp.append(word[i])
return tmp
def char_switcher(self, ch):
switcher = {
'a': ["", "ا"],
'c': ["ث", "ص", "ص"],
'h': ["ه", "ح"],
'b': ["ب"],
'p': ["پ"],
't': ["ت", "ط"],
's': ["س", "ص", "ث"],
'j': ["ج"],
'ch': ["چ"],
'kh': ["خ"],
'q': ["ق", "غ"],
'd': ["د"],
'z': ["ز", "ذ", "ض", "ظ"],
'r': ["ر"],
'zh': ["ژ"],
'sh': ["ش"],
'gh': ["", "غ"],
'f': ["ف"],
'k': ["ک"],
'g': ["گ"],
'l': ["ل"],
'm': ["م"],
'n': ["ن"],
'v': ["و"],
'aa': ["ا"],
'ee': ["ی"],
'oo': ["و"],
'ou': ["و"],
'i': ["ی"],
'y': ["ی"],
' ': [""],
'w': ["و"],
'e': ["", "ه"],
'o': ["", "و"]
}
return switcher.get(ch, "")
# این روال جهت جمع و ضرب کردن بخش های مختلف از صدگان و هزارگان و بالاتر از یک عدد که با حروف در متن وجود دارد کار می کند
def number_completter2(number_parts_array):
zarb_value = 0
temp_number_array = []
sum_number_parts = 0
final_number = 0
for index,item in enumerate(number_parts_array):
if item.isdigit():
temp_number_array.append(int(item))
if index + 1 >= len(number_parts_array):
for item3 in temp_number_array:
final_number += int(item3)
return final_number
current_value = number_parts_array[index+1]
if (current_value.isdigit() and (int(current_value) == 1000 or int(current_value) == 1000000 or int(current_value) == 1000000000)):# or int(current_value) == 1000000
zarb_value = int(number_parts_array[index+1])
for num_item in temp_number_array:
sum_number_parts += num_item
final_number = sum_number_parts * zarb_value
temp_array2 = []
x = index + 2
while x < len(number_parts_array):
# for x in range(index+2,len(number_parts_array)):
temp_array2.append(number_parts_array[x])
if x + 1 < len(number_parts_array):
if number_parts_array[x+1].isdigit() and (int(number_parts_array[x+1]) == 1000 or int(number_parts_array[x+1]) == 1000000 or int(number_parts_array[x+1]) == 1000000000):
if int(number_parts_array[x+1]) > zarb_value: # 1000000>1000
zarb_value = int(number_parts_array[x+1])
# تابع بازگشتی برای محاسبه عدد نهایی
final_number += number_completter2(temp_array2)
final_number *= zarb_value
temp_array2.clear()
zarb_value = 0
x += 2 # به این دلیل که مقدار ضرب را در آرایه تمپ ذخیره نکند
else: # 1000!>1000000
zarb_value = int(number_parts_array[x+1])
temp_num = 0
if (zarb_value == 1000 or zarb_value == 1000000) and len(number_parts_array) > x + 1:
num_array = number_parts_array[x+2:len(number_parts_array)]
temp_num = number_completter(num_array)
if temp_num == None:
temp_num = 0
'''for i in range(x+2,len(number_parts_array)):
if(number_parts_array[i].isdigit()):
temp_num += int(number_parts_array[i])
i+=1'''
temp_num2 = 1
for num_item2 in temp_array2:
if(num_item2.isdigit()):
temp_num2 += int(num_item2)
temp_num2 *= zarb_value
final_number += temp_num + temp_num2
break
else:
x += 1 #
else: # اگر از رنج آرایه خارج شدیم
temp_num = 0
# مقادیر موجود در آرایه تمپ را جمع کن
for num_item3 in temp_array2:
if(num_item3.isdigit()):
temp_num += int(num_item3)
# حاصل جمع اعداد موجود در آرایه ذخیره را در عدد نهایی که قبلا داشته ایم ضرب کن و از حلقه خارج شو
final_number *= temp_num
#final_number += temp_num
break
return final_number
# این روال جهت جمع و ضرب کردن بخش های مختلف از صدگان و هزارگان و بالاتر از یک عدد که با حروف در متن وجود دارد کار می کند
def number_completter(number_parts_array):
zarb_value = 0
previous_zarb_value = 0
previous_number_parts_sum = 0
temp_number_array = []
number_parts_sum = 0
final_number = 0
current_number_part = 0
for index,item in enumerate(number_parts_array):
if item.isdigit():
if (not(int(item) == 1000 or int(item) == 1000000 or int(item) == 1000000000)):
temp_number_array.append(item)
continue
elif((int(item) == 1000 or int(item) == 1000000 or int(item) == 1000000000)):
zarb_value = int(item)
for num_item in temp_number_array:
number_parts_sum += int(num_item)
if number_parts_sum == 0 and previous_number_parts_sum == 0:
number_parts_sum = 1
temp_number_array.clear()
else:# for example 952
zarb_value = 1
for num_item in temp_number_array:
number_parts_sum += int(num_item)
current_number_part = number_parts_sum + previous_number_parts_sum
continue
if previous_zarb_value < zarb_value:# for example 1000 < 1000000000
current_number_part = previous_number_parts_sum + number_parts_sum
current_number_part = zarb_value * current_number_part
else:# previous_zarb_value > zarb_value
if number_parts_sum == 0:
number_parts_sum = 1
current_number_part = zarb_value * number_parts_sum
current_number_part += previous_number_parts_sum
previous_number_parts_sum = current_number_part
current_number_part = 0
previous_zarb_value = zarb_value
number_parts_sum = 0
if len(temp_number_array) != 0:
remained_parts_sum = 0
for num_item in temp_number_array:
remained_parts_sum += int(num_item)
final_number = previous_number_parts_sum + remained_parts_sum
return final_number
final_number = previous_number_parts_sum
return final_number