File size: 2,129 Bytes
7d402a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/usr/bin/env python3
"""Lightweight tokenizer approximating UDPipe tokenization, for the webapp's
local (UDPipe-free) mode.

Rules were derived empirically from the training corpora's UD token
columns (results_cleaned_UD/words):

English (english-ewt):
- contractions split for straight and curly apostrophes
  (don't -> do + n't, we’re -> we + ’re, John's -> John + 's),
- hyphenated compounds kept whole (non-executive), en-dash ranges kept
  whole (6–24), ellipsis "..." kept as one token.

Czech (czech-pdtc):
- no word-internal splitting at all (abych, kdybys stay whole; Czech-PDT
  UD has no multiword tokens),
- punctuation runs split into single characters ("..." -> . + . + .,
  "?!" -> ? + !).

Known approximations: glued artifacts like "3.One" are split here but
kept whole by UDPipe; URLs/emails are not treated specially. Good enough
for the surface-only detector; for exact annotation use the UDPipe mode.
"""

import re

_TOKEN_EN = re.compile(
    r"\d+(?:[.,:–-]\d+)+"       # 3.14, 12:30, 6–24, 2020-21
    r"|\w+(?:['’–-]\w+)*"  # words incl. apostrophes and hyphens
    r"|\.{2,}|-{2,}|[!?]{2,}"        # ... -- ?!
    r"|\S", re.UNICODE)

_TOKEN_CS = re.compile(
    r"\d+(?:[.,:]\d+)+"              # 3,14, 12:30
    r"|\w+"                          # words, nothing glued
    r"|\S", re.UNICODE)              # punctuation char by char

_EN_CONTRACTION = re.compile(
    r"(?i)^(.+?)(n['’]t|['’](?:re|ll|ve|s|d|m))$")


def tokenize(text, lang):
    """Return a list of word tokens approximating UDPipe output."""
    if lang == "cs":
        return _TOKEN_CS.findall(text)
    out = []
    for tok in _TOKEN_EN.findall(text):
        if tok.lower() == "cannot":
            out += [tok[:3], tok[3:]]
            continue
        m = _EN_CONTRACTION.match(tok)
        if m and m.group(1):
            out += [m.group(1), m.group(2)]
        else:
            out.append(tok)
    return out


if __name__ == "__main__":
    print(tokenize("Don't worry — we’re fine (non-executive, 6–24)...", "en"))
    print(tokenize("Řekl, abychom šli domů. Kdybys věděl?! No...", "cs"))