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