pragmatic_tokenizer 3.0.6 → 3.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pragmatic_tokenizer/languages.rb +26 -26
- data/lib/pragmatic_tokenizer/languages/arabic.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/bulgarian.rb +3 -3
- data/lib/pragmatic_tokenizer/languages/common.rb +14 -24
- data/lib/pragmatic_tokenizer/languages/czech.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/danish.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/deutsch.rb +3 -93
- data/lib/pragmatic_tokenizer/languages/dutch.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/english.rb +11 -14
- data/lib/pragmatic_tokenizer/languages/finnish.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/french.rb +10 -9
- data/lib/pragmatic_tokenizer/languages/greek.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/indonesian.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/italian.rb +1 -1
- data/lib/pragmatic_tokenizer/languages/norwegian.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/persian.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/polish.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/portuguese.rb +1 -1
- data/lib/pragmatic_tokenizer/languages/romanian.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/russian.rb +3 -3
- data/lib/pragmatic_tokenizer/languages/slovak.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/swedish.rb +2 -2
- data/lib/pragmatic_tokenizer/languages/turkish.rb +2 -2
- data/lib/pragmatic_tokenizer/post_processor.rb +41 -93
- data/lib/pragmatic_tokenizer/pre_processor.rb +33 -142
- data/lib/pragmatic_tokenizer/regex.rb +149 -0
- data/lib/pragmatic_tokenizer/tokenizer.rb +76 -113
- data/lib/pragmatic_tokenizer/version.rb +1 -1
- data/pragmatic_tokenizer.gemspec +4 -5
- data/spec/performance_spec.rb +0 -1
- data/spec/spec_helper.rb +1 -1
- metadata +3 -3
- data/lib/pragmatic_tokenizer/full_stop_separator.rb +0 -58
@@ -3,7 +3,7 @@ module PragmaticTokenizer
|
|
3
3
|
module Portuguese
|
4
4
|
include Languages::Common
|
5
5
|
ABBREVIATIONS = Set.new([]).freeze
|
6
|
-
STOP_WORDS = Set.new(%w
|
6
|
+
STOP_WORDS = Set.new(%w[acerca agora algmas alguns ali ambos antes apontar aquela aquelas aquele aqueles aqui atrás bem bom cada caminho cima com como comprido conhecido corrente das debaixo dentro desde desligado deve devem deverá direita diz dizer dois dos e é ela ele eles em enquanto então está estado estão estar estará este estes esteve estive estivemos estiveram eu fará faz fazer fazia fez fim foi fora horas iniciar inicio ir irá ista iste isto ligado maioria maiorias mais mas mesmo meu muito muitos não nome nós nosso novo o onde os ou outro para parte pegar pelo pessoas pode poderá podia por porque povo promeiro qual qualquer quando quê quem quieto saber são sem ser seu somente tal também tem têm tempo tenho tentar tentaram tente tentei teu teve tipo tive todos trabalhar trabalho tu último um uma umas uns usa usar valor veja ver verdade verdadeiro você a à adeus aí ainda além algo algumas ano anos ao aos apenas apoio após aquilo área as às assim até através baixo bastante boa boas bons breve cá catorze cedo cento certamente certeza cinco coisa conselho contra custa da dá dão daquela daquelas daquele daqueles dar de demais depois dessa dessas desse desses desta destas deste destes dez dezanove dezasseis dezassete dezoito dia diante dizem do doze duas dúvida elas embora entre era és essa essas esse esses esta estas estás estava estiveste estivestes estou exemplo faço falta favor fazeis fazem fazemos fazes final fomos for foram forma foste fostes fui geral grande grandes grupo há hoje hora isso já lá lado local logo longe lugar maior mal máximo me meio menor menos mês meses meus mil minha minhas momento na nada naquela naquelas naquele naqueles nas nem nenhuma nessa nessas nesse nesses nesta nestas neste nestes nível no noite nos nossa nossas nossos nova novas nove novos num numa número nunca obra obrigada obrigado oitava oitavo oito ontem onze outra outras outros parece partir paucas pela pelas pelos perto pôde podem poder põe põem ponto pontos porquê posição possível possivelmente posso pouca pouco poucos primeira primeiras primeiro primeiros própria próprias próprio próprios próxima próximas próximo próximos puderam quáis quanto quarta quarto quatro que quer quereis querem queremas queres quero questão quinta quinto quinze relação sabe sabem se segunda segundo sei seis sempre seria sete sétima sétimo seus sexta sexto sim sistema sob sobre sois somos sou sua suas talvez tanta tantas tanto tão tarde te temos tendes tens ter terceira terceiro teus tivemos tiveram tiveste tivestes toda todas todo três treze tua tuas tudo vai vais vão vários vem vêm vens vez vezes viagem vindo vinte vocês vos vós vossa vossas vosso vossos zero and]).freeze
|
7
7
|
CONTRACTIONS = {}.freeze
|
8
8
|
end
|
9
9
|
end
|
@@ -3,8 +3,8 @@ module PragmaticTokenizer
|
|
3
3
|
module Romanian
|
4
4
|
include Languages::Common
|
5
5
|
ABBREVIATIONS = Set.new([]).freeze
|
6
|
-
STOP_WORDS
|
7
|
-
CONTRACTIONS
|
6
|
+
STOP_WORDS = Set.new(%w[acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ăla ale alea ălea altceva altcineva am ar are aş aşadar asemenea asta ăsta astăzi astea ăstea ăştia asupra aţi au avea avem aveţi azi bine bucur bună ca că căci când care cărei căror cărui cât câte câţi către câtva caut ce cel ceva chiar cinci cînd cine cineva cît cîte cîţi cîtva contra cu cum cumva curând curînd da dă dacă dar dată datorită dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după ea ei el ele eram este eşti eu face fără fata fi fie fiecare fii fim fiţi fiu frumos graţie halbă iar ieri îi îl îmi împotriva în înainte înaintea încât încît încotro între întrucât întrucît îţi la lângă le li lîngă lor lui mă mai mâine mea mei mele mereu meu mi mie mîine mine mult multă mulţi mulţumesc ne nevoie nicăieri nici nimeni nimeri nimic nişte noastră noastre noi noroc noştri nostru nouă nu opt ori oricând oricare oricât orice oricînd oricine oricît oricum oriunde până patra patru patrulea pe pentru peste pic pînă poate pot prea prima primul prin puţin puţina puţină rog sa să săi sale şapte şase sau său se şi sînt sîntem sînteţi spate spre ştiu sub sunt suntem sunteţi sută ta tăi tale tău te ţi ţie timp tine toată toate tot toţi totuşi trei treia treilea tu un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vă vi voastră voastre voi vor voştri vostru vouă vreme vreo vreun zece zero zi zice]).freeze
|
7
|
+
CONTRACTIONS = {}.freeze
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -2,9 +2,9 @@ module PragmaticTokenizer
|
|
2
2
|
module Languages
|
3
3
|
module Russian
|
4
4
|
include Languages::Common
|
5
|
-
ABBREVIATIONS = Set.new([
|
6
|
-
STOP_WORDS
|
7
|
-
CONTRACTIONS
|
5
|
+
ABBREVIATIONS = Set.new(%w[y y.e а авт адм.-терр акад в вв вкз вост.-европ г гг гос гр д деп дисс дол долл ежедн ж жен з зап зап.-европ заруб и ин иностр инст к канд кв кг куб л л.h л.н м мин моск муж н нед о п пгт пер пп пр просп проф р руб с сек см спб стр т тел тов тт тыс у у.е ул ф ч]).freeze
|
6
|
+
STOP_WORDS = Set.new(%w[а е и ж м о на не ни об но он мне мои мож она они оно мной много многочисленное многочисленная многочисленные многочисленный мною мой мог могут можно может можхо мор моя моё мочь над нее оба нам нем нами ними мимо немного одной одного менее однажды однако меня нему меньше ней наверху него ниже мало надо один одиннадцать одиннадцатый назад наиболее недавно миллионов недалеко между низко меля нельзя нибудь непрерывно наконец никогда никуда нас наш нет нею неё них мира наша наше наши ничего начала нередко несколько обычно опять около мы ну нх от отовсюду особенно нужно очень отсюда в во вон вниз внизу вокруг вот восемнадцать восемнадцатый восемь восьмой вверх вам вами важное важная важные важный вдали везде ведь вас ваш ваша ваше ваши впрочем весь вдруг вы все второй всем всеми времени время всему всего всегда всех всею всю вся всё всюду г год говорил говорит года году где да ее за из ли же им до по ими под иногда довольно именно долго позже более должно пожалуйста значит иметь больше пока ему имя пор пора потом потому после почему почти посреди ей два две двенадцать двенадцатый двадцать двадцатый двух его дел или без день занят занята занято заняты действительно давно девятнадцать девятнадцатый девять девятый даже алло жизнь далеко близко здесь дальше для лет зато даром первый перед затем зачем лишь десять десятый ею её их бы еще при был про процентов против просто бывает бывь если люди была были было будем будет будете будешь прекрасно буду будь будто будут ещё пятнадцать пятнадцатый друго другое другой другие другая других есть пять быть лучше пятый к ком конечно кому кого когда которой которого которая которые который которых кем каждое каждая каждые каждый кажется как какой какая кто кроме куда кругом с т у я та те уж со то том снова тому совсем того тогда тоже собой тобой собою тобою сначала только уметь тот тою хорошо хотеть хочешь хоть хотя свое свои твой своей своего своих свою твоя твоё раз уже сам там тем чем сама сами теми само рано самом самому самой самого семнадцать семнадцатый самим самими самих саму семь чему раньше сейчас чего сегодня себе тебе сеаой человек разве теперь себя тебя седьмой спасибо слишком так такое такой такие также такая сих тех чаще четвертый через часто шестой шестнадцать шестнадцатый шесть четыре четырнадцать четырнадцатый сколько сказал сказала сказать ту ты три эта эти что это чтоб этом этому этой этого чтобы этот стал туда этим этими рядом тринадцать тринадцатый этих третий тут эту суть чуть тысяч]).freeze
|
7
|
+
CONTRACTIONS = {}.freeze
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -3,8 +3,8 @@ module PragmaticTokenizer
|
|
3
3
|
module Slovak
|
4
4
|
include Languages::Common
|
5
5
|
ABBREVIATIONS = Set.new([]).freeze
|
6
|
-
STOP_WORDS
|
7
|
-
CONTRACTIONS
|
6
|
+
STOP_WORDS = Set.new(%w[a aby aj ak ako ale alebo and ani áno asi až bez bude budem budeš budeme budete budú by bol bola boli bolo byť cez čo či ďalší ďalšia ďalšie dnes do ho ešte for i ja je jeho jej ich iba iné iný som si sme sú k kam každý každá každé každí kde keď kto ktorá ktoré ktorou ktorý ktorí ku lebo len ma mať má máte medzi mi mna mne mnou musieť môcť môj môže my na nad nám náš naši nie nech než nič niektorý nové nový nová noví o od odo of on ona ono oni ony po pod podľa pokiaľ potom práve pre prečo preto pretože prvý prvá prvé prví pred predo pri pýta s sa so svoje svoj svojich svojím svojími ta tak takže táto teda te tě ten tento the tieto tým týmto tiež to toto toho tohoto tom tomto tomuto tu tú túto tvoj ty tvojími už v vám váš vaše vo viac však všetok vy z za zo že a buď ju menej moja moje späť ste tá tam]).freeze
|
7
|
+
CONTRACTIONS = {}.freeze
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -3,8 +3,8 @@ module PragmaticTokenizer
|
|
3
3
|
module Swedish
|
4
4
|
include Languages::Common
|
5
5
|
ABBREVIATIONS = Set.new([]).freeze
|
6
|
-
STOP_WORDS
|
7
|
-
CONTRACTIONS
|
6
|
+
STOP_WORDS = Set.new(%w[aderton adertonde adjö aldrig alla allas allt alltid alltså än andra andras annan annat ännu artonde artonn åtminstone att åtta åttio åttionde åttonde av även båda bådas bakom bara bäst bättre behöva behövas behövde behövt beslut beslutat beslutit bland blev bli blir blivit bort borta bra då dag dagar dagarna dagen där därför de del delen dem den deras dess det detta dig din dina dit ditt dock du efter eftersom elfte eller elva en enkel enkelt enkla enligt er era ert ett ettusen få fanns får fått fem femte femtio femtionde femton femtonde fick fin finnas finns fjärde fjorton fjortonde fler flera flesta följande för före förlåt förra första fram framför från fyra fyrtio fyrtionde gå gälla gäller gällt går gärna gått genast genom gick gjorde gjort god goda godare godast gör göra gott ha hade haft han hans har här heller hellre helst helt henne hennes hit hög höger högre högst hon honom hundra hundraen hundraett hur i ibland idag igår igen imorgon in inför inga ingen ingenting inget innan inne inom inte inuti ja jag jämfört kan kanske knappast kom komma kommer kommit kr kunde kunna kunnat kvar länge längre långsam långsammare långsammast långsamt längst långt lätt lättare lättast legat ligga ligger lika likställd likställda lilla lite liten litet man många måste med mellan men mer mera mest mig min mina mindre minst mitt mittemot möjlig möjligen möjligt möjligtvis mot mycket någon någonting något några när nästa ned nederst nedersta nedre nej ner ni nio nionde nittio nittionde nitton nittonde nödvändig nödvändiga nödvändigt nödvändigtvis nog noll nr nu nummer och också ofta oftast olika olikt om oss över övermorgon överst övre på rakt rätt redan så sade säga säger sagt samma sämre sämst sedan senare senast sent sex sextio sextionde sexton sextonde sig sin sina sist sista siste sitt sjätte sju sjunde sjuttio sjuttionde sjutton sjuttonde ska skall skulle slutligen små smått snart som stor stora större störst stort tack tidig tidigare tidigast tidigt till tills tillsammans tio tionde tjugo tjugoen tjugoett tjugonde tjugotre tjugotvå tjungo tolfte tolv tre tredje trettio trettionde tretton trettonde två tvåhundra under upp ur ursäkt ut utan utanför ute vad vänster vänstra var vår vara våra varför varifrån varit varken värre varsågod vart vårt vem vems verkligen vi vid vidare viktig viktigare viktigast viktigt vilka vilken vilket vill aderton]).freeze
|
7
|
+
CONTRACTIONS = {}.freeze
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -3,8 +3,8 @@ module PragmaticTokenizer
|
|
3
3
|
module Turkish
|
4
4
|
include Languages::Common
|
5
5
|
ABBREVIATIONS = Set.new([]).freeze
|
6
|
-
STOP_WORDS
|
7
|
-
CONTRACTIONS
|
6
|
+
STOP_WORDS = Set.new(%w[acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir birçok biri birkaç birkez birşey birşeyi biz bize bizden bizi bizim böyle böylece bu buna bunda bundan bunlar bunları bunların bunu bunun burada çok çünkü da daha dahi de defa değil diğer diye doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor eğer elli en etmesi etti ettiği ettiğini gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir için iki ile ilgili ise işte itibaren itibariyle kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduğu olduğunu olduklarını olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa öyle pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin şey şeyden şeyi şeyler şöyle şu şuna şunda şundan şunları şunu tarafından trilyon tüm üç üzere var vardı ve veya ya yani yapacak yapılan yapılması yapıyor yapmak yaptı yaptığı yaptığını yaptıkları yedi yerine yetmiş yine yirmi yoksa yüz zaten altmýþ altý bazý beþ birþey birþeyi INSERmi kýrk mý nasýl onlari onlarýn yetmiþ þey þeyden þeyi þeyler þu þuna þunda þundan þunu]).freeze
|
7
|
+
CONTRACTIONS = {}.freeze
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -1,109 +1,63 @@
|
|
1
1
|
module PragmaticTokenizer
|
2
2
|
class PostProcessor
|
3
3
|
|
4
|
-
DOT
|
5
|
-
RANGE_DINGBATS = '[\u2701-\u27BE]'.freeze # e.g. ✁✎✳❄➾
|
6
|
-
RANGE_VARIATION_SELECTORS = '[\uFE00-\uFE0F]'.freeze # alter the previous character
|
7
|
-
RANGE_FULLWIDTH = '[\uFF01-\ufF1F]'.freeze # e.g. !"#'?
|
8
|
-
|
9
|
-
REGEXP_COMMAS = /^([,‚])+/
|
10
|
-
REGEXP_SINGLE_QUOTES = /(.+)([’'‘`])$/
|
11
|
-
REGEXP_SLASH = /^(?!(https?:|www\.))(.*)\//
|
12
|
-
REGEXP_QUESTION_MARK = /^(?!(https?:|www\.))(.*)(\?)/
|
13
|
-
REGEXP_PLUS_SIGN = /(.+)\+(.+)/
|
14
|
-
REGEXP_COLON = /^(:)(\S{2,})/
|
15
|
-
REGEXP_DINGBATS = /(#{RANGE_DINGBATS}#{RANGE_VARIATION_SELECTORS}*)/
|
16
|
-
REGEXP_ENDING_PUNCT = /(?<=\S)([#{RANGE_FULLWIDTH}!?]+)$/
|
17
|
-
REGEXP_DOMAIN = /^((https?:\/\/|)?[a-z0-9]+([\-\.][a-z0-9]+)*\.[a-z]{2,6}(:[0-9]{1,5})?(\/.*)?)$/ix
|
18
|
-
REGEXP_EMAIL = /\S+[@@]\S+/
|
19
|
-
REGEXP_DOMAIN_START = /^(https?:|www\.|[[:alpha:]]\.)/
|
20
|
-
REGEXP_DOMAIN_END = /\.(com|net|org|edu|gov|mil|int|[[:alpha:]]{2})$/
|
21
|
-
REGEXP_DIGIT = /[[:digit:]]+/
|
22
|
-
REGEXP_PERIOD1 = /(.*\.)/
|
23
|
-
REGEXP_PERIOD2 = /(\.)/
|
24
|
-
|
25
|
-
REGEX_UNIFIED1 = Regexp.union(REGEXP_SLASH,
|
26
|
-
REGEXP_QUESTION_MARK,
|
27
|
-
REGEXP_PLUS_SIGN,
|
28
|
-
REGEXP_COLON,
|
29
|
-
REGEXP_DINGBATS,
|
30
|
-
PragmaticTokenizer::Languages::Common::PREFIX_EMOJI_REGEX,
|
31
|
-
PragmaticTokenizer::Languages::Common::POSTFIX_EMOJI_REGEX)
|
32
|
-
|
33
|
-
REGEX_UNIFIED2 = Regexp.union(REGEXP_SINGLE_QUOTES,
|
34
|
-
REGEXP_COMMAS)
|
35
|
-
|
36
|
-
REGEX_DOMAIN_EMAIL = Regexp.union(REGEXP_DOMAIN,
|
37
|
-
REGEXP_EMAIL)
|
38
|
-
|
39
|
-
REGEX_DOMAIN = Regexp.union(REGEXP_DOMAIN_START,
|
40
|
-
REGEXP_DOMAIN_END)
|
4
|
+
DOT = '.'.freeze
|
41
5
|
|
42
6
|
attr_reader :text, :abbreviations, :downcase
|
43
7
|
|
44
8
|
def initialize(text:, abbreviations:, downcase:)
|
45
|
-
@text
|
46
|
-
@abbreviations
|
47
|
-
@downcase
|
9
|
+
@text = text
|
10
|
+
@abbreviations = abbreviations
|
11
|
+
@downcase = downcase
|
48
12
|
end
|
49
13
|
|
50
|
-
|
51
|
-
|
14
|
+
# Every #flat_map will increase memory usage, we should try to merge whatever can be merged
|
15
|
+
# We need to run #split(Regex::ENDS_WITH_PUNCTUATION2) before AND after #split(Regex::VARIOUS), can this be fixed?
|
16
|
+
def call
|
17
|
+
text
|
18
|
+
.split
|
19
|
+
.map { |token| convert_sym_to_punct(token) }
|
20
|
+
.flat_map { |token| token.split(Regex::COMMAS_OR_PUNCTUATION) }
|
21
|
+
.flat_map { |token| token.split(Regex::VARIOUS) }
|
22
|
+
.flat_map { |token| token.split(Regex::ENDS_WITH_PUNCTUATION2) }
|
23
|
+
.flat_map { |token| split_dotted_email_or_digit(token) }
|
24
|
+
.flat_map { |token| split_abbreviations(token) }
|
25
|
+
.flat_map { |token| split_period_after_last_word(token) }
|
52
26
|
end
|
53
27
|
|
54
28
|
private
|
55
29
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
unified1,
|
61
|
-
split_unknown_period1,
|
62
|
-
split_unknown_period2,
|
63
|
-
separate_ending_punctuation
|
64
|
-
]
|
65
|
-
end
|
66
|
-
|
67
|
-
def separate_ending_punctuation
|
68
|
-
proc { |token| token.split(REGEXP_ENDING_PUNCT) }
|
69
|
-
end
|
70
|
-
|
71
|
-
def unified1
|
72
|
-
proc { |token| token.split(REGEX_UNIFIED1) }
|
73
|
-
end
|
74
|
-
|
75
|
-
def full_stop_separated_tokens
|
76
|
-
FullStopSeparator.new(tokens: split_convert_commas_quotes, abbreviations: abbreviations, downcase: downcase).separate
|
77
|
-
end
|
78
|
-
|
79
|
-
def split_convert_commas_quotes
|
80
|
-
text
|
81
|
-
.split
|
82
|
-
.flat_map { |token| token.split(REGEX_UNIFIED2) }
|
83
|
-
.flat_map { |token| convert_sym_to_punct(token) }
|
84
|
-
end
|
85
|
-
|
86
|
-
def split_unknown_period1
|
87
|
-
proc { |token| unknown_period1?(token) ? token.split(REGEXP_PERIOD1) : token }
|
30
|
+
def convert_sym_to_punct(token)
|
31
|
+
PragmaticTokenizer::Languages::Common::PUNCTUATION_MAP
|
32
|
+
.each { |pattern, replacement| break if token.sub!(replacement, pattern) }
|
33
|
+
token
|
88
34
|
end
|
89
35
|
|
90
|
-
|
91
|
-
|
36
|
+
# Per specs, "16.1. day one,17.2. day two" will result in ["16.1", ".",…]. Do we really want that?
|
37
|
+
def split_dotted_email_or_digit(token)
|
38
|
+
return token unless token.end_with?(DOT) && token.length > 1
|
39
|
+
shortened = token.chomp(DOT)
|
40
|
+
return [shortened, DOT] if shortened =~ Regex::DOMAIN_OR_EMAIL
|
41
|
+
return [shortened, DOT] if shortened =~ Regex::ENDS_WITH_DIGIT
|
42
|
+
token
|
92
43
|
end
|
93
44
|
|
94
|
-
def
|
95
|
-
token.include?(DOT) &&
|
96
|
-
|
97
|
-
|
98
|
-
|
45
|
+
def split_abbreviations(token)
|
46
|
+
return token unless token.include?(DOT) && token.length > 1
|
47
|
+
return token if token =~ Regex::DOMAIN_OR_EMAIL
|
48
|
+
abbreviation = extract_abbreviation(token)
|
49
|
+
return token.split(Regex::PERIOD_AND_PRIOR) if abbreviations.include?(abbreviation)
|
50
|
+
token
|
99
51
|
end
|
100
52
|
|
101
|
-
def
|
102
|
-
token.include?(DOT) &&
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
53
|
+
def split_period_after_last_word(token)
|
54
|
+
return token unless token.include?(DOT) && token.length > 1
|
55
|
+
return token if token.count(DOT) > 1
|
56
|
+
return token if token =~ Regex::ONLY_DOMAIN3
|
57
|
+
return token if token =~ Regex::DIGIT
|
58
|
+
abbreviation = extract_abbreviation(token)
|
59
|
+
return token.split(Regex::PERIOD_ONLY) unless abbreviations.include?(abbreviation)
|
60
|
+
token
|
107
61
|
end
|
108
62
|
|
109
63
|
def extract_abbreviation(token)
|
@@ -111,11 +65,5 @@ module PragmaticTokenizer
|
|
111
65
|
downcase ? before_first_dot : Unicode.downcase(before_first_dot)
|
112
66
|
end
|
113
67
|
|
114
|
-
def convert_sym_to_punct(token)
|
115
|
-
PragmaticTokenizer::Languages::Common::PUNCTUATION_MAP
|
116
|
-
.each { |pattern, replacement| break if token.sub!(replacement, pattern) }
|
117
|
-
token
|
118
|
-
end
|
119
|
-
|
120
68
|
end
|
121
69
|
end
|
@@ -3,176 +3,67 @@ module PragmaticTokenizer
|
|
3
3
|
|
4
4
|
def pre_process(language: Languages::Common)
|
5
5
|
remove_non_breaking_space!
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
shift_inverted_exclamation!
|
10
|
-
shift_exclamation!
|
11
|
-
shift_ellipse_three_dots!
|
12
|
-
shift_ellipse_two_dots!
|
13
|
-
shift_horizontal_ellipsis!
|
14
|
-
shift_no_space_mention!
|
15
|
-
shift_not_equals!
|
16
|
-
shift_special_quotes!
|
17
|
-
shift_colon!
|
18
|
-
shift_bracket!
|
19
|
-
shift_semicolon!
|
20
|
-
shift_percent!
|
21
|
-
shift_caret!
|
6
|
+
shift_various_characters!
|
7
|
+
replace_colon_in_url!
|
8
|
+
shift_remaining_colons!
|
22
9
|
shift_hashtag!
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
convert_apostrophe_s!
|
28
|
-
shift_beginning_hyphen!
|
29
|
-
shift_ending_hyphen!
|
10
|
+
convert_double_quotes!
|
11
|
+
convert_single_quotes!(language)
|
12
|
+
convert_acute_accent_s!
|
13
|
+
shift_hyphens!
|
30
14
|
squeeze(' '.freeze)
|
31
15
|
end
|
32
16
|
|
33
17
|
private
|
34
18
|
|
35
19
|
def remove_non_breaking_space!
|
36
|
-
|
20
|
+
gsub!(Regex::NO_BREAK_SPACE, ''.freeze)
|
37
21
|
end
|
38
22
|
|
39
|
-
|
40
|
-
|
41
|
-
gsub!(/,(?!\d)/o, ' , '.freeze)
|
42
|
-
gsub!(/(?<=\D),(?=\S+)/, ' , '.freeze)
|
23
|
+
def shift_various_characters!
|
24
|
+
gsub!(Regex::PRE_PROCESS, ' \1 \2 \3 \4 \5 \6 \7 \8 \9 ')
|
43
25
|
end
|
44
26
|
|
45
|
-
def
|
46
|
-
gsub!(
|
27
|
+
def replace_colon_in_url!
|
28
|
+
gsub!(Regex::COLON_IN_URL, replacement_for_key(':'.freeze))
|
47
29
|
end
|
48
30
|
|
49
|
-
def
|
50
|
-
gsub!(
|
51
|
-
end
|
52
|
-
|
53
|
-
def shift_inverted_exclamation!
|
54
|
-
gsub!(/¡/, ' ¡ '.freeze)
|
55
|
-
end
|
56
|
-
|
57
|
-
def shift_exclamation!
|
58
|
-
gsub!(/(?<=[a-zA-z])!(?=[a-zA-z])/, ' ! '.freeze)
|
59
|
-
end
|
60
|
-
|
61
|
-
def shift_horizontal_ellipsis!
|
62
|
-
gsub!(/(…+)/o, ' \1 ')
|
63
|
-
end
|
64
|
-
|
65
|
-
def shift_ellipse_two_dots!
|
66
|
-
gsub!(/(\.\.+)/o, ' \1 ')
|
67
|
-
end
|
68
|
-
|
69
|
-
def shift_ellipse_three_dots!
|
70
|
-
gsub!(/(\.\.\.+)/o, ' \1 ')
|
71
|
-
end
|
72
|
-
|
73
|
-
def shift_no_space_mention!
|
74
|
-
gsub!(/\.(?=(@|@)[^\.]+(\s|\z))/, '. '.freeze)
|
75
|
-
end
|
76
|
-
|
77
|
-
def shift_not_equals!
|
78
|
-
gsub!(/≠/, ' ≠ '.freeze)
|
79
|
-
end
|
80
|
-
|
81
|
-
def shift_special_quotes!
|
82
|
-
gsub!(/([«»„“])/, ' \1 ')
|
83
|
-
end
|
84
|
-
|
85
|
-
def shift_colon!
|
86
|
-
return unless may_shift_colon?
|
87
|
-
# Ignore web addresses
|
88
|
-
replacement = replacement_for_key(':'.freeze)
|
89
|
-
gsub!(%r{(?<=[(https?|ftp)]):(?=//)}, replacement)
|
90
|
-
gsub!(/:/o, ' :'.freeze)
|
91
|
-
gsub!(/(?<=\s):(?=\#)/, ': '.freeze)
|
92
|
-
end
|
93
|
-
|
94
|
-
def may_shift_colon?
|
95
|
-
return false unless include?(':'.freeze)
|
96
|
-
partitions = partition(':'.freeze)
|
97
|
-
partitions.last[0] !~ /\A\d+/ || partitions.first[-1] !~ /\A\d+/
|
98
|
-
end
|
99
|
-
|
100
|
-
def shift_bracket!
|
101
|
-
gsub!(/([\(\[\{\}\]\)])/o, ' \1 ')
|
102
|
-
end
|
103
|
-
|
104
|
-
def shift_semicolon!
|
105
|
-
gsub!(/([;])/o, ' \1 ')
|
106
|
-
end
|
107
|
-
|
108
|
-
def shift_percent!
|
109
|
-
gsub!(/(?<=\D)%(?=\d+)/, ' %'.freeze)
|
110
|
-
end
|
111
|
-
|
112
|
-
def shift_caret!
|
113
|
-
gsub!(/\^/, ' ^ '.freeze)
|
31
|
+
def shift_remaining_colons!
|
32
|
+
gsub!(':'.freeze, ' :'.freeze) if self !~ Regex::TIME_WITH_COLON
|
114
33
|
end
|
115
34
|
|
116
35
|
def shift_hashtag!
|
117
|
-
gsub!(
|
118
|
-
end
|
119
|
-
|
120
|
-
def shift_ampersand!
|
121
|
-
gsub!(/\&/, ' & '.freeze)
|
122
|
-
end
|
123
|
-
|
124
|
-
def shift_vertical_bar!
|
125
|
-
gsub!(/\|/, ' | '.freeze)
|
126
|
-
end
|
127
|
-
|
128
|
-
def convert_dbl_quotes!
|
129
|
-
replace_left_double_quotes!
|
130
|
-
replace_remaining_double_quotes!
|
131
|
-
end
|
132
|
-
|
133
|
-
def replace_left_double_quotes!
|
134
|
-
replace_left_quotes!("''", '"'.freeze)
|
135
|
-
replace_left_quotes!('"', '"'.freeze)
|
136
|
-
replace_left_quotes!('“', '“'.freeze)
|
137
|
-
end
|
138
|
-
|
139
|
-
def replace_left_quotes!(style, replacement_key)
|
140
|
-
replacement = replacement_for_key(replacement_key)
|
141
|
-
gsub!(/#{style}(?=.*\w)/o, ' ' << replacement << ' ')
|
36
|
+
gsub!('#'.freeze, ' #'.freeze)
|
142
37
|
end
|
143
38
|
|
144
|
-
def
|
145
|
-
|
146
|
-
replace_remaining_quotes!("''", '"'.freeze)
|
147
|
-
replace_remaining_quotes!('”', '”'.freeze)
|
39
|
+
def convert_double_quotes!
|
40
|
+
gsub!(Regex::QUOTE, replacements_for_quotes)
|
148
41
|
end
|
149
42
|
|
150
|
-
def
|
151
|
-
|
152
|
-
|
43
|
+
def replacements_for_quotes
|
44
|
+
@replacements_for_quotes ||= {
|
45
|
+
"''" => ' ' << replacement_for_key('"'.freeze) << ' ',
|
46
|
+
'"' => ' ' << replacement_for_key('"'.freeze) << ' ',
|
47
|
+
'“' => ' ' << replacement_for_key('“'.freeze) << ' '
|
48
|
+
}.freeze
|
153
49
|
end
|
154
50
|
|
155
|
-
def
|
156
|
-
replace(
|
157
|
-
language::SingleQuotes.new
|
158
|
-
.handle_single_quotes(self)
|
159
|
-
else
|
160
|
-
PragmaticTokenizer::Languages::Common::SingleQuotes.new
|
161
|
-
.handle_single_quotes(self)
|
162
|
-
end)
|
51
|
+
def convert_single_quotes!(language)
|
52
|
+
replace(class_for_single_quotes(language).new.handle_single_quotes(self))
|
163
53
|
end
|
164
54
|
|
165
|
-
def
|
166
|
-
|
167
|
-
gsub!(/\s\u{0301}(?=s(\s|\z))/, replacement)
|
55
|
+
def class_for_single_quotes(language)
|
56
|
+
defined?(language::SingleQuotes) ? language::SingleQuotes : PragmaticTokenizer::Languages::Common::SingleQuotes
|
168
57
|
end
|
169
58
|
|
170
|
-
def
|
171
|
-
gsub!(
|
59
|
+
def convert_acute_accent_s!
|
60
|
+
gsub!(Regex::ACUTE_ACCENT_S, replacement_for_key('`'.freeze))
|
172
61
|
end
|
173
62
|
|
174
|
-
|
175
|
-
|
63
|
+
# can these two regular expressions be merged somehow?
|
64
|
+
def shift_hyphens!
|
65
|
+
gsub!(Regex::HYPHEN_AFTER_NON_WORD, ' - '.freeze)
|
66
|
+
gsub!(Regex::HYPHEN_BEFORE_NON_WORD, ' - '.freeze)
|
176
67
|
end
|
177
68
|
|
178
69
|
def replacement_for_key(replacement_key)
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module PragmaticTokenizer
|
2
|
+
class Regex
|
3
|
+
|
4
|
+
# Things that can or should be done:
|
5
|
+
# - check where the use of unicode categories helps (\p{Abbreviation})
|
6
|
+
# - use URI.parse and other libraries instead of regexp to identify urls, domains, emails
|
7
|
+
# - check multiple domain regex, we have spec issues when using one or the other
|
8
|
+
# - check multiple punctuation regex
|
9
|
+
|
10
|
+
# Text that needs to be tokenized is initially split into chunks of this length:
|
11
|
+
CHUNK_LONG_INPUT_TEXT = /\S.{1,10000}(?!\S)/m
|
12
|
+
|
13
|
+
# Ranges
|
14
|
+
RANGE_DINGBATS = /[\u2701-\u27BE]/ # e.g. ✁✎✳❄➾
|
15
|
+
RANGE_VARIATION_SELECTORS = /[\uFE00-\uFE0F]/ # alter the previous character
|
16
|
+
RANGE_FULLWIDTH = /[\uFF01-\ufF1F]/ # e.g. !"#'?
|
17
|
+
RANGE_ALPHANUMERIC_SUPPLEMENT = /[\u{1F100}-\u{1F1FF}]/
|
18
|
+
RANGE_UNUSUAL_AND_EMOJI = /[\u203C-\u3299\u{1F000}-\u{1F644}]/
|
19
|
+
|
20
|
+
# Regular expressions which do not need to capture anything are enclosed in /(?: … )/ to enhance performance
|
21
|
+
COLON1 = /(?:(:)([[:print:]]{2,}))/ # two non-space after colon prevent matching emoticons
|
22
|
+
COLON2 = /(?::)/
|
23
|
+
COMMAS = /(?:([,‚])+)/
|
24
|
+
ENCLOSED_PLUS = /(?:([[:print:]]+)\+([[:print:]]+))/
|
25
|
+
EMAIL = /(?:[[:print:]]+[@@][[:print:]]+\.[[:print:]]+)/
|
26
|
+
DIGIT = /(?:[[:digit:]]+)/
|
27
|
+
ASTERISK = /(?:\*+)/
|
28
|
+
UNDERSCORE = /(?:_+)/
|
29
|
+
HYPHEN_OR_UNDERSCORE = /(?:[-_])/
|
30
|
+
PERIOD_AND_PRIOR = /(?:(.+\.))/
|
31
|
+
PERIOD_ONLY = /(?:(\.))/
|
32
|
+
CONTRACTIONS = /(?:[‘’‚‛‹›'´`])/
|
33
|
+
PUNCTUATION1 = /(?:([\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Ps}])+)/ # all punctuation categories except Pc (Connector) and Po (other)
|
34
|
+
PUNCTUATION2 = /(?:(?<=\S)([!?#{RANGE_FULLWIDTH.source}]+))/
|
35
|
+
PUNCTUATION3 = /(?:[!%\-–\u00AD]+)/
|
36
|
+
PUNCTUATION4 = /(?:[..。]+)/
|
37
|
+
DINGBATS = /(?:(#{RANGE_DINGBATS.source}#{RANGE_VARIATION_SELECTORS.source}*+))/
|
38
|
+
NO_BREAK_SPACE = /(?:\u00A0+)/
|
39
|
+
HTTP = /(?:https?:\/\/)/
|
40
|
+
TIME_WITH_COLON = /(?:\d:\d)/
|
41
|
+
DOMAIN_PREFIX = /(?:https?:\/\/|www\.|[[:alpha:]]\.)/
|
42
|
+
DOMAIN_SUFFIX = /(?:[[:alpha:]]\.(?:com|net|org|edu|gov|mil|int|[[:alpha:]]{2}))/
|
43
|
+
DOMAIN1 = /(?:((https?:\/\/|)[[:print:]]+\.[[:alpha:]]{2,6}(:[0-9]{1,5})?(\/[[:print:]]*+)?))/
|
44
|
+
DOMAIN2 = /(?:[[:alnum:]]{2,}([\-.][[:alnum:]]+)*\.[[:alpha:]]{2,6}(:[0-9]{1,5})?(\/[[:print:]]*+)?)/
|
45
|
+
NOT_URL = /(?:^(?!#{DOMAIN_PREFIX.source})([[:print:]]*))/
|
46
|
+
HASHTAG_OR_MENTION = /(?:[@#@#][[:print:]]+)/
|
47
|
+
HASHTAG = /(?:[##][[:print:]]+)/
|
48
|
+
MENTION = /(?:[@@][[:print:]]+)/
|
49
|
+
HASHTAG_WITH_HYPHEN = /(?:^([##][[:digit:]]+)-)/
|
50
|
+
ONE_AS_EXCLAMATION = /(?:\D1+)/
|
51
|
+
ONES_EXCLAMATIONS = /(?:!+(1*+!*+)*+)/
|
52
|
+
MANY_PERIODS = /(?:^\.{2,}$)/
|
53
|
+
COPYRIGHT_TRADEMARK = /(?:[®©™]+)/
|
54
|
+
CONTROL_CHARACTER = /(?:[[:cntrl:]]+)/ # matches any character with hexadecimal value 00 through 1F or 7F.
|
55
|
+
APOSTROPHE_AND_S = /(?:['’`́]s)/
|
56
|
+
ALSO_DECIMALS = /(?:[[:alpha:]]*+[[:digit:]]+)/
|
57
|
+
ACUTE_ACCENT_S = /(?:\s\u0301(?=s))/
|
58
|
+
|
59
|
+
# Regular expressions used to capture items
|
60
|
+
CAPTURE_UNUSUAL_AND_EMOJI = /(#{RANGE_UNUSUAL_AND_EMOJI.source})/
|
61
|
+
QUESTION_MARK_NOT_URL = /#{NOT_URL.source}(\?)/
|
62
|
+
# Should we change specs and also capture "/", just like we capture ":" and "?"
|
63
|
+
SLASH_NOT_URL = /#{NOT_URL.source}\//
|
64
|
+
SHIFT_BOUNDARY_CHARACTERS = /([;^&|…«»„“¿¡≠]+)/
|
65
|
+
MULTIPLE_DOTS = /(\.{2,})/ # we keep all dashes
|
66
|
+
MULTIPLE_DASHES = /(-){2,}/ # we only keep first dash
|
67
|
+
BRACKET = /([{}()\[\]])/
|
68
|
+
EXCLAMATION_BETWEEN_ALPHA = /(?<=[[:alpha:]])(!)(?=[[:alpha:]])/
|
69
|
+
PERCENT_BEFORE_DIGIT = /(%)\d+/
|
70
|
+
COMMA_BEFORE_NON_DIGIT = /(,)(?=\D)/
|
71
|
+
COMMA_AFTER_NON_DIGIT = /(?<=\D)(,)/
|
72
|
+
COLON_IN_URL = /(?<=[(https?|ftp)]):(?=\/\/)/
|
73
|
+
QUOTE_BEFORE_PRINT = /(('')|["“])(?=[[:print:]])/
|
74
|
+
QUOTE = /('')|["”]/
|
75
|
+
HYPHEN_AFTER_NON_WORD = /(?<=\W)(-)/
|
76
|
+
HYPHEN_BEFORE_NON_WORD = /(-)(?=\W)/
|
77
|
+
|
78
|
+
STARTS_WITH_COMMAS = /^#{COMMAS.source}/
|
79
|
+
STARTS_WITH_HTTP = /^#{HTTP.source}/
|
80
|
+
STARTS_WITH_DOMAIN = /^#{DOMAIN_PREFIX.source}/
|
81
|
+
STARTS_WITH_COLON1 = /^#{COLON1.source}/
|
82
|
+
STARTS_WITH_UNDERSCORE = /^#{UNDERSCORE.source}/
|
83
|
+
STARTS_WITH_PUNCTUATION3 = /^#{PUNCTUATION3.source}/
|
84
|
+
|
85
|
+
ENDS_WITH_DOMAIN = /#{DOMAIN_SUFFIX.source}$/
|
86
|
+
ENDS_WITH_PUNCTUATION1 = /#{PUNCTUATION1.source}$/
|
87
|
+
ENDS_WITH_PUNCTUATION2 = /#{PUNCTUATION2.source}$/
|
88
|
+
ENDS_WITH_COLON2 = /#{COLON2.source}$/
|
89
|
+
ENDS_WITH_UNDERSCORE = /#{UNDERSCORE.source}$/
|
90
|
+
ENDS_WITH_ONES_EXCLAMATIONS = /#{ONES_EXCLAMATIONS.source}$/
|
91
|
+
ENDS_WITH_EXCITED_ONE = /#{ONE_AS_EXCLAMATION.source}$/
|
92
|
+
ENDS_WITH_APOSTROPHE_AND_S = /#{APOSTROPHE_AND_S.source}$/
|
93
|
+
ENDS_WITH_ALPHA = /[[:alpha:]]$/
|
94
|
+
ENDS_WITH_DIGIT = /[[:digit:]]$/
|
95
|
+
|
96
|
+
ONLY_DECIMALS = /(?:^[[:digit:]]+$)/
|
97
|
+
NO_DECIMALS = /(?:^\D+$)/
|
98
|
+
ONLY_PUNCTUATION = /^[[[:punct:]]^|+]+$/
|
99
|
+
ONLY_ROMAN_NUMERALS = /^(?=[MDCLXVI])M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/i
|
100
|
+
ONLY_EMAIL = /^#{EMAIL}$/
|
101
|
+
ONLY_HASHTAG_MENTION = /^#{HASHTAG_OR_MENTION}$/
|
102
|
+
ONLY_HASHTAG = /^#{HASHTAG}$/
|
103
|
+
ONLY_MENTION = /^#{MENTION}$/
|
104
|
+
ONLY_DOMAIN1 = /^#{DOMAIN1}$/
|
105
|
+
ONLY_DOMAIN2 = /^#{DOMAIN2}$/
|
106
|
+
ONLY_DOMAIN3 = Regexp.union(STARTS_WITH_DOMAIN, ENDS_WITH_DOMAIN)
|
107
|
+
DOMAIN_OR_EMAIL = Regexp.union(ONLY_DOMAIN1, ONLY_EMAIL)
|
108
|
+
UNDERSCORES_ASTERISK = Regexp.union(STARTS_WITH_UNDERSCORE, ENDS_WITH_UNDERSCORE, ASTERISK)
|
109
|
+
NO_DECIMALS_NO_NUMERALS = Regexp.union(ALSO_DECIMALS, ONLY_ROMAN_NUMERALS)
|
110
|
+
|
111
|
+
COMMAS_OR_PUNCTUATION = Regexp.union(
|
112
|
+
STARTS_WITH_COMMAS,
|
113
|
+
ENDS_WITH_PUNCTUATION1,
|
114
|
+
ENDS_WITH_PUNCTUATION2
|
115
|
+
)
|
116
|
+
|
117
|
+
# Can this constant name be clarified?
|
118
|
+
VARIOUS = Regexp.union(
|
119
|
+
SLASH_NOT_URL,
|
120
|
+
QUESTION_MARK_NOT_URL,
|
121
|
+
ENCLOSED_PLUS,
|
122
|
+
STARTS_WITH_COLON1,
|
123
|
+
DINGBATS,
|
124
|
+
HASHTAG_WITH_HYPHEN,
|
125
|
+
CAPTURE_UNUSUAL_AND_EMOJI
|
126
|
+
)
|
127
|
+
|
128
|
+
IRRELEVANT_CHARACTERS = Regexp.union(
|
129
|
+
STARTS_WITH_PUNCTUATION3,
|
130
|
+
ENDS_WITH_COLON2,
|
131
|
+
ENDS_WITH_ONES_EXCLAMATIONS,
|
132
|
+
CONTROL_CHARACTER,
|
133
|
+
COPYRIGHT_TRADEMARK,
|
134
|
+
RANGE_ALPHANUMERIC_SUPPLEMENT
|
135
|
+
)
|
136
|
+
|
137
|
+
PRE_PROCESS = Regexp.union(
|
138
|
+
SHIFT_BOUNDARY_CHARACTERS,
|
139
|
+
MULTIPLE_DOTS,
|
140
|
+
BRACKET,
|
141
|
+
MULTIPLE_DASHES,
|
142
|
+
EXCLAMATION_BETWEEN_ALPHA,
|
143
|
+
PERCENT_BEFORE_DIGIT,
|
144
|
+
COMMA_BEFORE_NON_DIGIT,
|
145
|
+
COMMA_AFTER_NON_DIGIT
|
146
|
+
)
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|