Python-Skript update:
[wortliste.git] / skripte / python / abgleich_neueintraege.py
blob7c2a00eb2e03577fb13996a22b787f745368da05
1 #!/usr/bin/env python
2 # -*- coding: utf8 -*-
3 # :Copyright: © 2011 Günter Milde.
4 # Released without warranty under the terms of the
5 # GNU General Public License (v. 2 or later)
6 # :Id: $Id: $
8 # Versuche Trennstellen neuer Wörter aus vorhandenen zu ermitteln
9 # ===============================================================
11 # Übertragen von kategorisierten Trennstellen vorhandener Wörter
12 # auf neu aufzunehmende, ungetrennte Wörter.
14 # Erwartet eine Datei mit 1 Wort/Zeile.
16 # Erstellt einen Patch mit den Wörtern, welche durch Abgleich mit der
17 # Datenbasis getrennt werden konnten.
18 # ::
20 import re, sys, codecs, copy, os
21 from werkzeug import WordFile, WordEntry, join_word, toggle_case
23 # Konfiguration
24 # -------------
26 # Sprachvarianten
27 # ~~~~~~~~~~~~~~~
28 # Sprach-Tag nach [BCP47]_::
30 # sprachvariante = 'de-1901' # "traditionell"
31 sprachvariante = 'de-1996' # Reformschreibung
32 # sprachvariante = 'de-1901-x-GROSS' # ohne ß (Schweiz oder GROSS)
33 # sprachvariante = 'de-1996-x-GROSS' # ohne ß (Schweiz oder GROSS)
34 # sprachvariante = 'de-CH-1901' # ohne ß (Schweiz) ("süssauer")
36 # Funktionen
37 # -----------
39 # Übertrag von Praefixen auf Wörter ohne Präfix::
41 def praefixabgleich(key, praefix, grossklein=False):
43 if key.istitle():
44 praefix = praefix.title()
46 if not key.startswith(join_word(praefix)):
47 return ''
49 altkey = key[len(join_word(praefix)):]
51 if grossklein:
52 altkey = toggle_case(altkey)
54 try:
55 altentry = words[altkey]
56 except KeyError:
57 return ''
59 entry = WordEntry(key)
60 # print "fundum", key, unicode(entry)
61 for wort in altentry[1:]:
62 if not wort.startswith(u'-'):
63 if grossklein:
64 wort = toggle_case(wort)
65 wort = u'<'.join([praefix, wort])
66 entry.append(wort)
68 return entry
70 praefixe = [u'abo',
71 u'ab',
72 u'ab<zu',
73 u'auf<zu',
74 u'aus<zu',
75 u'ein<zu',
76 u'mit<zu',
77 u'um<zu',
78 u'un-ter<zu',
79 u'weg<zu',
80 u'aber',
81 u'ad',
82 u'aero',
83 u'af-ro',
84 u'ag-ro',
85 u'al-lergo',
86 u'all',
87 u'als',
88 u'am-bi',
89 u'ami-no',
90 u'an',
91 u'an-dro',
92 u'an-gio',
93 u'an-thro-po',
94 u'an-ti',
95 u'ang-lo',
96 u'apo',
97 u'ar-chaeo',
98 u'ar-che',
99 u'ar-chäo',
100 u'ar-terio',
101 u'ar-thro',
102 u'asyn',
103 u'at-mo',
104 u'au-ßer',
105 u'auf',
106 u'aus',
107 u'aus<zu',
108 u'aut',
109 u'ba-ro',
110 u'bak-te-rio',
111 u'be',
112 u'bei',
113 u'ben-zo',
114 u'bi-blio',
115 u'bio',
116 u'che-mo',
117 u'chi-ro',
118 u'chlo-ro',
119 u'cho-reo',
120 u'chro-mo',
121 u'chro-no',
122 u'cy-ano',
123 u'dar',
124 u'de-ka',
125 u'de-zi',
126 u'demo',
127 u'der-ma-to',
128 u'des',
129 u'di-cho',
130 u'di-no',
131 u'dia',
132 u'dis',
133 u'dis-ko',
134 u'down',
135 u'drein',
136 u'durch',
137 u'dys',
138 u'e-tho',
139 u'ego',
140 u'ein',
141 u'elek-tro',
142 u'em-por',
143 u'emp',
144 u'en-do',
145 u'en-te-ro',
146 u'ent',
147 u'epi',
148 u'er',
149 u'er-go',
150 u'es-chato',
151 u'eth-no',
152 u'ety-mo',
153 u'ex',
154 u'ext-ro',
155 u'fe-ro',
156 u'fem-to',
157 u'fer-ro',
158 u'fo-no',
159 u'fort',
160 u'fran-ko',
161 u'für',
162 u'ga-so',
163 u'ge',
164 u'ge-gen',
165 u'ge-no',
166 u'ge-ron-to',
167 u'geo',
168 u'gi-ga',
169 u'gi-gan-to',
170 u'go-no',
171 u'gra-fo',
172 u'gra-pho',
173 u'gy-nä-ko',
174 u'he-lio',
175 u'he-te-ro',
176 u'he-xa',
177 u'hek-to',
178 u'hekt',
179 u'hemi',
180 u'her',
181 u'hier',
182 u'hin',
183 u'hin-ter',
184 u'hint',
185 u'ho-lo',
186 u'ho-mo',
187 u'ho-möo',
188 u'hoch',
189 u'hy-dro',
190 u'hy-per',
191 u'hy-po',
192 u'hym-no',
193 u'hyp-no',
194 u'hä-ma-to',
195 u'hä-mo',
196 u'ideo',
197 u'idio',
198 u'iko-no',
199 u'il',
200 u'im',
201 u'im-mu-no',
202 u'in',
203 u'in-fra',
204 u'in-ter',
205 u'in-tra',
206 u'ins',
207 u'int-ro',
208 u'io-no',
209 u'kar-dio',
210 u'kar-to',
211 u'kata',
212 u'klep-to',
213 u'kli-no',
214 u'kon',
215 u'kon-tra',
216 u'kor-re',
217 u'kos-mo',
218 u'kri-mi-no',
219 u'kri-no',
220 u'kryp-to',
221 u'leu-ko',
222 u'leuk',
223 u'le-xi-ko',
224 u'li-tho',
225 u'lim-no',
226 u'lo-go',
227 u'los',
228 u'lym-pho',
229 u'ma-gne-to',
230 u'mak-ro',
231 u'mam-mo',
232 u'me-ga',
233 u'me-lo',
234 u'me-so',
235 u'me-ta',
236 u'me-teo-ro',
237 u'me-tho-do',
238 u'mik-ro',
239 u'mil-li',
240 u'miss',
241 u'mit',
242 u'mo-no',
243 u'mor-pho',
244 u'mu-si-ko',
245 u'mul-ti',
246 u'my-co',
247 u'my-tho',
248 u'na-no',
249 u'nach',
250 u'ne-ben',
251 u'neo',
252 u'neu-ro',
253 u'neur',
254 u'nie-der',
255 u'no-wo',
256 u'non',
257 u'nost',
258 u'ob',
259 u'oben',
260 u'ober',
261 u'off',
262 u'ohn',
263 u'oli-go',
264 u'olig',
265 u'om-ni',
266 u'on-ko',
267 u'on-to',
268 u'op-to',
269 u'or-tho',
270 u'oszil-lo',
271 u'out',
272 u'over',
273 u'oxy',
274 u'ozea-no',
275 u'pa-ra',
276 u'pa-tho',
277 u'pa-tri',
278 u'pan-to',
279 u'pe-re',
280 u'pen-ta',
281 u'pet-ro',
282 u'phar-ma',
283 u'phar-ma-ko',
284 u'phi-lo',
285 u'phil',
286 u'pho-no',
287 u'pho-to',
288 u'phra-seo',
289 u'phy-lo',
290 u'phy-sio',
291 u'phy-to',
292 u'phä-no',
293 u'pneu-mo',
294 u'po-eto',
295 u'po-li-to',
296 u'po-ly',
297 u'po-ten-tio',
298 u'pro-to',
299 u'prä',
300 u'pseud',
301 u'psy-cho',
302 u'py-ro',
303 u'pä-do',
304 u'päd',
305 u'raus',
306 u're',
307 u'rein',
308 u'ret-ro',
309 u'ri-bo',
310 u'rä-to',
311 u'rück',
312 u'sa-mo',
313 u'sak-ro',
314 u'se-mi',
315 u'seis-mo',
316 u'selb',
317 u'ser-bo',
318 u'si-no',
319 u'so',
320 u'so-zio',
321 u'sou',
322 u'spek-tro',
323 u'ste-no',
324 u'ste-reo',
325 u'ste-tho',
326 u'stra-to',
327 u'su-per',
328 u'sub',
329 u'sup-ra',
330 u'sus',
331 u'syn',
332 u'ta-xo',
333 u'tau-to',
334 u'te-leo',
335 u'te-ra',
336 u'tech-no',
337 u'tele',
338 u'telo',
339 u'ter-mi-no',
340 u'tet-ra',
341 u'ther-mo',
342 u'throm-bo',
343 u'to-mo',
344 u'to-po',
345 u'to-xi-ko',
346 u'tra-gi',
347 u'trans',
348 u'tro-po',
349 u'tur-bo',
350 u'ty-po',
351 u'ul-tra',
352 u'um',
353 u'un',
354 u'un-der',
355 u'un-ter',
356 u'uni',
357 u'ur',
358 u'uro',
359 u'ver',
360 u'vi-no',
361 u'vi-ro',
362 u'vib-ra',
363 u'voll',
364 u'von',
365 u'vor',
366 u'vorn',
367 u'vul-ka-no',
368 u'weg',
369 u'wi-der',
370 u'xe-no',
371 u'xy-lo',
372 u'zen-ti',
373 u'zen-tri',
374 u'zer',
375 u'zu',
376 u'zwie',
377 u'zy-klo',
378 u'zy-to',
379 u'ägyp-to',
380 u'öko',
381 u'über',
384 # Nach Länge sortieren, damit spezifischere zuerst Probiert werden:
385 praefixe.sort(key = len)
386 praefixe.reverse()
389 # Übertrag von Einträgen auf Wörter mit anderer Endung::
391 def endungsabgleich(key, alt, neu, grossklein=False):
393 if not key.endswith(join_word(neu)):
394 return ''
395 OK = True
396 altkey = key[:-len(join_word(neu))] + join_word(alt)
397 if grossklein:
398 altkey = toggle_case(altkey)
400 try:
401 altentry = words[altkey]
402 except KeyError:
403 return ''
405 entry = WordEntry(key)
406 # print "fundum", key, unicode(entry)
407 for wort in altentry[1:]:
408 if not wort.startswith(u'-'):
409 if alt:
410 wort = wort[:-len(alt)]
411 wort += neu
412 if grossklein:
413 wort = toggle_case(wort)
414 if join_word(wort) != key:
415 OK = False
416 entry.append(wort)
417 if OK is False:
418 print u"# Übertragungsproblem: %s -> %s (%s,%s) %s" % (
419 altkey, key, alt, neu, unicode(entry))
420 return ''
422 entry.conflate_fields()
423 return entry
426 # Endungen
427 # --------
428 # ``(<alt>, <neu>)`` Paare von Endungen::
430 endungen = [
431 (u'', u'-de'),
432 # (u'', u'-en'),
433 # (u'', u'-er'),
434 # (u'', u'-is-mus'),
435 # (u'', u'-ität'),
436 (u'', u'-lein'),
437 (u'', u'-ne'),
438 (u'', u'-nem'),
439 (u'', u'-nen'),
440 (u'', u'-ner'),
441 (u'', u'-sche'),
442 (u'', u'-tum'),
443 (u'', u'>ar-tig'),
444 (u'', u'>chen'),
445 (u'', u'>heit'),
446 (u'', u'>keit'),
447 (u'', u'>schaft'),
448 (u'', u'>schaft'),
449 (u'', u'>wei-se'),
450 # (u'', u'd'),
451 # (u'', u'e'),
452 # (u'', u'e-rin'),
453 # (u'', u'er'),
454 # (u'', u'is-mus'),
455 # (u'', u'm'),
456 # (u'', u'n'),
457 # (u'', u'ner'),
458 # (u'', u'r'),
459 # (u'', u's'),
460 # (u'', u's-te'),
461 # (u'', u's-te'),
462 # (u'', u's>los'),
463 # (u'', u'st'),
464 # (u'', u't'),
465 # (u'', u't-te'),
466 (u'-al', u'a-le'),
467 (u'-an', u'a-ne'),
468 (u'-at', u'a-te'),
469 (u'-ben', u'b-ne'),
470 # (u'-che', u'ch'),
471 (u'-de', u'd'),
472 (u'-en', u'>bar>keit'),
473 # (u'-en', u'e'),
474 (u'-en', u'e-ne'),
475 (u'-er', u'e-rei'),
476 (u'-er', u'e-rin'),
477 (u'-ern', u'e-re'),
478 (u'-ge', u'g'),
479 (u'-gen', u'g'),
480 (u'-in', u'i-ne'),
481 (u'-on', u'o-nen'),
482 (u'-re', u'r'),
483 (u'-re', u'rt'),
484 (u'-ren', u'r-ne'),
485 (u'-ren', u'rt'),
486 (u'-sche', u'sch'),
487 (u'-sen', u's-ne'),
488 (u'-sten', u's-mus'),
489 (u'-te',u't'),
490 (u'-tern', u't-re'),
491 (u'-ös', u'ö-se'),
492 (u'a', u'-ar'),
493 (u'a', u'-as'),
494 (u'b', u'-be'),
495 (u'b', u'-ber'),
496 (u'bar', u't'),
497 (u'bt', u'b-te'),
498 (u'ce', u'-cen'),
499 (u'ch', u'-che'),
500 (u'ch', u'-cher'),
501 (u'ck', u'-cke'),
502 (u'ck', u'-cker'),
503 (u'd', u'-de'),
504 (u'd', u'-dem'),
505 (u'd', u'-den'),
506 (u'd', u'-der'),
507 (u'd', u'-des'),
508 (u'd', u'>heit'),
509 (u'e', u'-en'),
510 (u'e-ren', u'-ti-on'),
511 (u'e-ren', u'sch'),
512 (u'el', u'le'),
513 # (u'en', u'e'),
514 (u'en', u'em'),
515 (u'en', u'en-de'),
516 (u'en', u'end'),
517 (u'en', u'er'),
518 (u'en', u'es'),
519 (u'en', u'est'),
520 (u'en', u't'),
521 (u'en', u'te'),
522 (u'en', u'us'),
523 (u'end',u'en' ),
524 # (u'er', u'e'),
525 (u'er', u'e-rei'),
526 (u'er', u'ens'),
527 (u'er', u'in'),
528 (u'er', u'ung'),
529 (u'es', u'est'),
530 (u'es', u's-te'),
531 (u'f', u'-fe'),
532 (u'f', u'-fer'),
533 (u'g', u'-ge'),
534 (u'g', u'-gen'),
535 (u'g', u'-ger'),
536 (u'g', u'-ger'),
537 (u'g', u'-ges'),
538 (u'g', u'-gung'),
539 (u'ie', u'e'),
540 (u'in', u'en'),
541 (u'isch', u'i-sche'),
542 (u'k', u'-ke'),
543 (u'k', u'-ken'),
544 (u'k', u'-ker'),
545 (u'l', u'-le'),
546 (u'l', u'-len'),
547 (u'l', u'-ler'),
548 (u'l', u'-lis-mus'),
549 (u'le', u'-ler'),
550 (u'li-che', u'tem'),
551 (u'li-che', u'ten'),
552 (u'ln', u'-le'),
553 (u'lt', u'-le'),
554 (u'm', u'-me'),
555 (u'm', u'-mer'),
556 (u'me', u'-men'),
557 (u'mus', u'men'),
558 (u'mus', u'ten'),
559 (u'mus', u'tik'),
560 (u'n', u'-at'),
561 (u'n', u'-er'),
562 (u'n', u'-ne'),
563 (u'n', u'-nen'),
564 (u'n', u'-nis-mus'),
565 (u'n', u'r'),
566 (u'n', u'st'),
567 (u'n', u't'),
568 (u'n',u'-ner'),
569 (u'nd',u'n'),
570 (u'ne',u'ner'),
571 # (u'ne',u'n'),
572 (u'o',u'-on'),
573 (u'o',u'-os'),
574 (u'o',u'en'),
575 (u'on',u'o-nen'),
576 (u'p', u'-pe'),
577 (u'p', u'-pen'),
578 (u'p', u'-per'),
579 (u'ph', u'-phen'),
580 (u'ph', u'-phis-mus'),
581 (u'r', u'-re'),
582 (u'r', u'-rei'),
583 (u'r', u'-ren'),
584 (u'r', u'-rin'),
585 (u'r', u'-ris-mus'),
586 (u'r', u'-rung'),
587 (u're', u'ste'),
588 (u'ren', u'r-te'),
589 (u'ren', u'rst'),
590 (u'ren', u'rt'),
591 (u'rn', u'-re'),
592 (u'rn', u'-rung'),
593 (u'rn', u'-rung'),
594 (u'rt', u'-re'),
595 (u'rt', u'r-te'),
596 (u's', u''),
597 (u's', u'-se'),
598 (u's', u'-se-re'),
599 (u's', u'-se-res'),
600 (u's', u'-ser'),
601 (u's', u's-se'),
602 (u's', u's-ses'),
603 (u'sch', u'-sche'),
604 (u'sch', u'-schen'),
605 (u'sch', u'-scher'),
606 (u'st', u'-ste'),
607 (u'st', u'-sten'),
608 (u'st', u'n'),
609 (u't', u'-ba-re'),
610 (u't', u'>bar'),
611 (u't', u'-te'),
612 (u't', u'-te'),
613 (u't', u'-ten'),
614 (u't', u'-ter'),
615 (u't', u'-tes'),
616 (u't', u'-tin'),
617 (u't', u'-tis-mus'),
618 # (u't', u'e'),
619 (u't', u'n'),
620 (u't', u'st'),
621 (u'te', u'le'),
622 # (u'te', u't'),
623 (u'ten', u'mus'),
624 (u'ten', u'ren'),
625 (u'ten', u'tung'),
626 (u'ter', u'te-ren'),
627 (u'ti-on', u'tor'),
628 (u'um', u'a'),
629 (u'us', u'en'),
630 (u'v', u'-ve'),
631 (u'v', u'-ver'),
632 (u'v', u'-vis-mus'),
633 (u'-ve', u'v'),
634 (u'z', u'-ten'),
635 (u'z', u'-ze'),
636 (u'z', u'-zen'),
637 (u'z', u'-zer'),
638 (u'ß', u'-ße'),
639 (u'ß', u's-se'),
640 (u'ös', u'ö-se'),
644 # Zerlege einen String mit von vorn bis hinten wandernder Bruchstelle::
646 # >>> from abgleich_neueintraege import zerlege
647 # >>> list(zerlege(u'wolle'))
648 # [(u'w', u'olle'), (u'wo', u'lle'), (u'wol', u'le'), (u'woll', u'e')]
650 # ::
652 def zerlege(s):
653 for i in range(1, len(s)):
654 yield s[:i], s[i:]
657 # Zerlege String, wenn die Teile in der Wortliste vorhanden sind, setze
658 # sie neu zusammen und übernehme die Trennmarkierer:
661 def trenne_key(key, grossklein = False):
662 entries = []
663 for k1, k2 in zerlege(key):
664 if grossklein:
665 k1 = toggle_case(k1)
666 if k1.istitle():
667 k2 = k2.title()
668 e1 = words.get(k1)
669 e2 = words.get(k2)
670 if not e2:
671 e2 = words.get(toggle_case(k2))
672 if e1 and e2:
673 if len(e1) != len(e2):
674 if len(e1) == 2:
675 e1 = [e1[1]] * len(e2)
676 elif len(e2) == 2:
677 e2 = [e2[1]] * len(e1)
678 else:
679 continue
680 entry = WordEntry(key)
681 for w1, w2 in zip(e1,e2)[1:]:
682 if w1.startswith(u'-'):
683 wort = w1
684 elif w2.startswith(u'-'):
685 wort = w2
686 else:
687 if grossklein:
688 w1 = toggle_case(w1)
689 w2 = w2.lower()
690 if (u'==' in w1) or (u'==' in w2):
691 sep = u'==='
692 elif (u'=' in w1) or (u'=' in w2):
693 sep = u'=='
694 else:
695 sep = u'='
696 wort = sep.join([w1, w2])
697 entry.append(wort)
698 entries.append(entry)
699 return entries
702 if __name__ == '__main__':
704 # sys.stdout mit UTF8 encoding.
705 sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)
707 # `Wortliste` einlesen::
710 wordfile = WordFile('wortliste-expandiert') # + Teilwort-Entries
711 words = wordfile.asdict()
713 neuwortdatei = "zusatzwörter-de-1996-hunspell-compact"
714 neueintraege = []
715 neueintraege_grossklein = []
716 restwoerter = []
719 # Erstellen der neuen Einträge::
721 for line in open(neuwortdatei):
722 OK = False
723 key = line.decode('utf8').strip()
725 if len(key) <= 3:
726 continue
728 # Test auf vorhandene (Teil-) Wörter:
730 entry = words.get(key)
731 if entry:
732 neueintraege.append(entry)
733 continue
734 # kleingeschrieben
735 entry = words.get(key.lower())
736 if entry:
737 neueintraege_grossklein.append(entry)
738 continue
739 # Großgeschrieben
740 entry = words.get(key.title())
741 if entry:
742 neueintraege_grossklein.append(entry)
743 continue
745 # Endungsabgleich::
747 for alt, neu in endungen:
748 entry = endungsabgleich(key, alt, neu, grossklein=False)
749 if entry:
750 neueintraege.append(entry)
751 OK = True
752 # break
753 if OK:
754 continue
756 for alt, neu in endungen:
757 entry = endungsabgleich(key, alt, neu, grossklein=True)
758 if entry:
759 neueintraege_grossklein.append(entry)
760 OK = True
761 # break
762 if OK:
763 continue
765 # Präfixabgleich::
767 for praefix in praefixe:
768 entry = praefixabgleich(key, praefix, grossklein=False)
769 if entry:
770 neueintraege.append(entry)
771 OK = True
772 break
773 entry = praefixabgleich(key, praefix, grossklein=True)
774 if entry:
775 neueintraege_grossklein.append(entry)
776 OK = True
777 break
778 if OK:
779 continue
781 # Zerlegen und test auf Fugen::
783 entries = trenne_key(key, grossklein=False)
784 if entries:
785 neueintraege.extend(entries)
786 continue
787 entries = trenne_key(key, grossklein=True)
788 if entries:
789 neueintraege_grossklein.extend(entries)
790 continue
792 # Nicht gefundene Wörter::
794 restwoerter.append(key)
797 # Ausgabe::
799 print u'# als Teilwörter'
800 for entry in neueintraege:
801 print unicode(entry)
802 print
803 print u'# als Teilwörter (andere Großschreibung)'
804 for entry in neueintraege_grossklein:
805 print unicode(entry)
807 outfile = open(neuwortdatei+'-rest', 'w')
809 for wort in restwoerter:
810 outfile.write(wort.encode('utf8')+'\n')
811 outfile.close()