diff --git a/pysbd/abbreviation_replacer.py b/pysbd/abbreviation_replacer.py index b4f2118..b9cf9e1 100644 --- a/pysbd/abbreviation_replacer.py +++ b/pysbd/abbreviation_replacer.py @@ -35,6 +35,7 @@ def replace(self): abbr_handled_text = "" for line in self.text.splitlines(True): abbr_handled_text += self.search_for_abbreviations_in_string(line) + self.text = abbr_handled_text self.replace_multi_period_abbreviations() self.text = Text(self.text).apply(*self.lang.AmPmRules.All) diff --git a/pysbd/about.py b/pysbd/about.py index c8c9404..94de0ae 100644 --- a/pysbd/about.py +++ b/pysbd/about.py @@ -2,7 +2,7 @@ # https://python-packaging-user-guide.readthedocs.org/en/latest/single_source_version/ __title__ = "pysbd" -__version__ = "0.3.4" +__version__ = "0.3.5" __summary__ = "pysbd (Python Sentence Boundary Disambiguation) is a rule-based sentence boundary detection that works out-of-the-box across many languages." __uri__ = "http://nipunsadvilkar.github.io/" __author__ = "Nipun Sadvilkar" diff --git a/pysbd/lang/ukrainian.py b/pysbd/lang/ukrainian.py new file mode 100644 index 0000000..5dbff3d --- /dev/null +++ b/pysbd/lang/ukrainian.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +import re + +from pysbd.abbreviation_replacer import AbbreviationReplacer +from pysbd.lang.common import Common, Standard + +class Ukrainian(Common, Standard): + + iso_code = 'uk' + + # Handling Cyrillic characters in re module + # https://stackoverflow.com/a/10982308/5462100 + MULTI_PERIOD_ABBREVIATION_REGEX = r'\b[\u0400-\u0500]+(?:\.\s?[\u0400-\u0500])+[.∯]|b[a-z](?:\.[a-z])+[.∯]' + + class Abbreviation(Standard.Abbreviation): + ABBREVIATIONS = ["а. с", "авт. арк", "автореф", "агр", "аграр", "адапт", "адм", "адмін", "акад", "акомп", "алф", "альб", "альм", "амер", "аналіт", "англ", "анот", "антол", "арх", "археол", "архіт", "асоц", "атл", "б. в", "б. д", "б. м", "б. р", "б. т", "б. тит. арк", "б. ц", "безкошт", "безпл", "берез", "бібл", "біблійн", "бібліогр", "бібліографозн", "бібліол", "біобібліогр", "біогр", "болг", "брош", "буд", "будів", "бух", "бюдж", "бюл", "в т. ч", "введ", "ввод", "вдоскон", "верес", "вет", "вивч", "вид", "вид. від", "викон", "використ", "виникн", "вип", "вип. дан", "випр", "вироб", "вист", "вих. дан", "вищ", "від", "відб", "відеофоногр", "відкр", "відом", "відп", "відред", "відродж", "вінниц", "віршов", "вісн", "вітчизн", "вкл. арк", "включ", "внутр", "волин", "впровадж", + "вугіл", "вчен", "газ", "ген", "генет", "геодез", "геологорозв", "гір", "гірн", "гірничорят", "глобал", "голланд", "голов", "гологр", "госп", "господарч", "грав", "громад", "ГРУД", "гуманіт", "Д", "декор", "демокр", "депоз", "депон", "держ", "держадмін", "деф", "див", "дип", "дис", "дит", "діагр", "діапоз", "діяльн", "дніпропетр", "довід", "дод", "додрук", "док", "докум", "донец", "доопрац", "доп", "допов", "допом", "дор", "доруч", "дослід", "дослідж", "драм", "друк", "духовн", "екол", "екон", "експерим", "електрон", "енерг", "енцикл", "євр", "європ", "житомир", "жовт", "журн", "забезп", "заг", "загот", "закарпат", "закінч", "закл", "зам", "зап", "запоріз", "зареєстр", "засід", "засл", "засн", "застосув", "затв", "заув", "зах", "зб", "здоб", "земел", "землероб", + "зібр", "змін", "зобр", "зрошув", "зрошувал", "ілюстр", "ім", "ін", "інв", "інд", "індивід", "інж", "інозем", "інспек", "інстр", "інструкт", "інструм", "інтегр", "інтенс", "інтерн", "інфл", "інформ", "ісп", "іст", "історіогр", "італ", "К", "каб", "канд", "карпат", "карт", "картогр", "картограф", "кас", "кат", "кат", "каф", "квіт", "кер", "керівн", "кіберн", "кінематогр", "кіровогр", "кл", "класиф", "класич", "клін.-експерим", "клініч", "кн", "книгозн", "коеф", "кол. авт", "колоніал", "ком", "комент", "комерц", "коміс", "коміч", "комп'ют", "комп'ют. наб", "компенс", "композ", "конгр", "конф", "концеп", "кооп", "коп", "кор", "корпор", "косм", "кресл", "крим", "кримін", "кримськотатар", "критич", "ксерокоп", "культ", "Л", "лаб", "латин", "лекс", "лип", "листоп", + "лібр", "лікар", "лікув", "лінгв", "лірич", "літ", "літогр", "луган", "луц", "львів", "лют", "М", "магістрал", "макс", "мал", "мат", "матер", "машинобуд", "мед", "меліор", "мемор", "менедж", "металоріз", "метод", "мех", "механіз", "миколаїв", "мистец", "мистецтвознав", "міжвід", "міжнар", "мін", "мінер", "міс", "міськ", "міськдержадмін", "місяч", "мкоп", "мовознав", "мол", "моногр", "морал", "муз", "мфотокоп", "н.-д", "наб. діапоз", "навч", "надзаг", "надзв", "напр", "нар", "народж", "народознав", "наст", "наук", "нац", "нім", "нормат", "О", "обкл", "обл", "обл.-вид. арк", "обладн", "облдержадмін", "образотв", "оброб", "обслуг", "обчисл", "оголош", "одес", "окр", "окр. арк", "оповід", "опр", "опрац", "опрацюв", "оптич", "опубл", "опублік", "орг", "ориг", "орк", + "оркестр", "офіц", "оформ", "паг", "парлам", "партит", "пед", "пер", "перевид", "перегл", "передм", "перероб", "переробл", "персон", "персп", "письм", "півд", "півн", "підвищ", "підгот", "підготов", "підзаг", "підруч", "планув", "пластич", "плем", "повідомл", "поетич", "покажч", "показн", "полем", "полігр", "політол", "полтав", "пом", "попул", "портр", "посіб", "почат", "поясн", "пр", "правл", "правознав", "практ", "предм", "препр", "приватиз", "пригод", "прикл", "приклад", "прим", "природн", "присвяч", "пробл", "прогр", "програмув", "прод", "продовж", "проектув", "пром", "пропоз", "проф", "проф.-викл", "псевд", "публ", "публіц", "публіч", "радіоелектрон", "райдержадмін", "район", "рац", "раціон", "ред", "редкол", "рез", "рек", "рекл", "реком", "реліг", "репр", + "репрод", "реставр", "ретросп", "реф", "рец", "рис", "рівнен", "розв", "розд", "розд. паг", "розповсюдж", "розроб", "розробл", "розшир", "рос", "ротапр", "рубр", "рубрик", "рукоп", "рум", "с.-г", "С.Пб", "сx", "сан", "сатир", "св", "селян", "семінар", "сенсац", "сер", "серп", "сес", "симп", "симф", "сист", "Сімф", "січ", "сканд", "склад", "скор", "сл", "слов", "словац", "словен", "служб", "соц", "соціол", "спец", "специф", "співавт", "співроб", "спорудж", "ст", "старш", "стат", "стер", "стереофон", "студ", "студент", "сум", "суспіл", "сцен", "Т", "т. д", "т. ін", "табл", "татар", "творч", "тез", "телегр", "теорет", "терит", "терміч", "терноп", "техн", "технол", "тиж", "тижд", "тис", "тит. арк", "торг", "трав", "тракт", "транскр", "трансліт", "трансп", "турец", + "у т. ч", "увед", "увод", "угор", "удоскон", "уклад", "укр", "українознав", "ум. друк. арк", "унів", "упоряд", "упорядкув", "упр", "урив", "устатк", "учасн", "факс", "факульт", "фантаст", "фармац", "федер", "фест", "фіз", "філ", "філол", "філос", "фільмогр", "фін", "фкоп", "фоногр", "формув", "фотогр", "фотокоп", "фототип", "фр", "фрагм", "франків", "футл", "харк", "херсон", "хім", "хмельниц", "хор", "хорв", "хореогр", "християн", "церк", "цит", "черв", "черкас", "чернів", "черніг", "чес", "шк", "щодекад", "щоден", "щокв", "щомісяч", "щоріч", "щотиж", "ювіл", "юнац", "яз", "ян", "яп", "X", + "р", "акад", "арт", "асист", "асп", "в. о", "вид", "викл", "викон", "вул", "д. чл", "деп", "дир", "дириг", "доц", "зав", "заст", "іл", "інсп", "комп", "м", "наб", "оз", "п", "пер", "пл", "пров", "просп", "проф", "ред", "реж", "с", "секр", "ст", "уклад", "упоряд", "худож", "чл.-кор", + "№ пор", "р", "рр", "арк", "б", "в", "гл", "к", "к", "кв", "од", "пр", "с", "ст", "т", "ц", "ч", "часоп", "чис", "н.е"] + PREPOSITIVE_ABBREVIATIONS = [] + NUMBER_ABBREVIATIONS = [] + + class AbbreviationReplacer(AbbreviationReplacer): + + SENTENCE_STARTERS = [] + + def __init__(self, text, lang): + super().__init__(text, lang) + + def replace_period_of_abbr(self, txt, abbr): + txt = re.sub(r'(?<=\s{abbr})\.'.format(abbr=abbr.strip()), '∯', txt) + txt = re.sub(r'(?<=\A{abbr})\.'.format(abbr=abbr.strip()), '∯', txt) + txt = re.sub(r'(?<=^{abbr})\.'.format(abbr=abbr.strip()), '∯', txt) + return txt diff --git a/pysbd/languages.py b/pysbd/languages.py index a7d764c..60ccf2b 100644 --- a/pysbd/languages.py +++ b/pysbd/languages.py @@ -22,6 +22,7 @@ from pysbd.lang.deutsch import Deutsch from pysbd.lang.kazakh import Kazakh from pysbd.lang.slovak import Slovak +from pysbd.lang.ukrainian import Ukrainian LANGUAGE_CODES = { 'en': English, @@ -46,7 +47,8 @@ 'ja': Japanese, 'de': Deutsch, 'kk': Kazakh, - 'sk': Slovak + 'sk': Slovak, + 'uk': Ukrainian, } diff --git a/tests/conftest.py b/tests/conftest.py index 21298a4..5a95396 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -146,3 +146,8 @@ def kk_default_fixture(): def sk_default_fixture(): sk_segmenter = pysbd.Segmenter(language="sk", clean=False, char_span=False) return sk_segmenter + +@pytest.fixture() +def uk_default_fixture(): + uk_segmenter = pysbd.Segmenter(language="uk", clean=False, char_span=False) + return uk_segmenter diff --git a/tests/lang/test_ukrainian.py b/tests/lang/test_ukrainian.py new file mode 100644 index 0000000..e3709e3 --- /dev/null +++ b/tests/lang/test_ukrainian.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +import re +import pytest + +GOLDEN_UK_RULES_TEST_CASES = [ + ("Я народився у м. Харків. Нині я мешкаю на вул. Сводоби 12, кв. 0. ", + ["Я народився у м. Харків.", "Нині я мешкаю на вул. Сводоби 12, кв. 0."]), + + ('"Га?" — відповів хлопець, цілковито здивований. "Що сьогодні, мій веселий компаньйоне?" — гукнув Скрудж.', + ['"Га?" — відповів хлопець, цілковито здивований.', '"Що сьогодні, мій веселий компаньйоне?" — гукнув Скрудж.']), + + ("Попередньо забронювала велику альтанку, ту, що якраз під лісом на 20.09. Почнемо з'їжджатися десь на 11-ту.", + ["Попередньо забронювала велику альтанку, ту, що якраз під лісом на 20.09.", "Почнемо з'їжджатися десь на 11-ту."]), + + ("Станом на 2500 р. до н.е.", + ["Станом на 2500 р. до н.е."]), +] + + +@pytest.mark.parametrize('text,expected_sents', GOLDEN_UK_RULES_TEST_CASES) +def test_uk_sbd(uk_default_fixture, text, expected_sents): + """Ukrainian language SBD tests""" + segments = uk_default_fixture.segment(text) + segments = [s.strip() for s in segments] + assert segments == expected_sents + + +def test_uk_multiperiod_abbreviation_regex(uk_default_fixture): + text = "Станом на 2500 р. до н.е." + match = re.search(uk_default_fixture.language_module.MULTI_PERIOD_ABBREVIATION_REGEX, + text) + assert match.group() == "н.е."