Python-Skript Update
[wortliste.git] / skripte / python / trennstellenkategorisierung / abgleich_neueintraege.py
blob0fc7e9e5047b2ca82e43d600bc3d4933d6de25fe
1 #!/usr/bin/env python
2 # -*- coding: utf8 -*-
3 # :Copyright: © 2014 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 u"""Trenne neue Wörter durch Ableitung von Einträgen der Wortliste.
13 Eingabe: 1 ungetrenntes Wort oder Eintrag im Wortliste-Format pro Zeile.
15 Ausgabe: Wortliste-Einträge (Vorschläge), sortiert nach:
16 identisch (falls Eingabe bereits Wortliste-Eintrag ist und eindeutig ist),
17 eindeutig abgeleitet
18 eindeutig abgeleitet (andere Großschreibung),
19 mehrdeutig abgeleitet,
20 Rest.
22 Bsp: python abgleich_neueintraege.py < dict-fail.txt > neu.todo
24 (``neu.todo`` kann (nach Durchsicht!!) mit `prepare_patch.py neu`
25 in die Wortliste eingepflegt werden.)
26 """
28 # ::
30 import sys, os, codecs, optparse
31 from collections import defaultdict # Wörterbuch mit Default
32 from wortliste import WordFile, WordEntry, join_word, toggle_case, sortkey_duden
33 from expand_teilwoerter import expand_wordfile
35 # Funktionen
36 # -----------
38 # Übertrag von Praefixen auf Wörter ohne Präfix::
40 def praefixabgleich(key, praefix, grossklein=False):
42 if key.istitle():
43 praefix = praefix.title()
45 if not key.startswith(join_word(praefix)):
46 return ''
48 altkey = key[len(join_word(praefix)):]
50 if grossklein:
51 altkey = toggle_case(altkey)
53 try:
54 altentry = words[altkey]
55 except KeyError:
56 return ''
58 entry = WordEntry(key)
59 # print "fundum", key, unicode(entry)
60 for wort in altentry[1:]:
61 if not wort.startswith(u'-'):
62 wort = wort.lower()
63 wort = u'<'.join([praefix, wort])
64 entry.append(wort)
66 return entry
68 praefixe = [u'abo',
69 u'ab',
70 u'ab<zu',
71 u'auf<zu',
72 u'aus<zu',
73 u'ein<zu',
74 u'mit<zu',
75 u'um<zu',
76 u'un-ter<zu',
77 u'weg<zu',
78 u'aber',
79 u'ad',
80 u'aero',
81 u'af-ro',
82 u'ag-ro',
83 u'al-lergo',
84 u'al-lein',
85 u'all',
86 u'als',
87 u'am-bi',
88 u'ami-no',
89 u'an-dro',
90 u'an-gio',
91 u'an-thro-po',
92 u'an-ti',
93 u'ang-lo',
94 u'an',
95 u'apo',
96 u'ar-chaeo',
97 u'ar-che',
98 u'ar-chäo',
99 u'ar-terio',
100 u'ar-thro',
101 u'asyn',
102 u'at-mo',
103 u'au-ßer',
104 u'auf',
105 u'aus',
106 u'aus<zu',
107 u'aut',
108 u'ba-ro',
109 u'bak-te-rio',
110 u'be',
111 u'bei',
112 u'ben-zo',
113 u'bi-blio',
114 u'bio',
115 u'che-mo',
116 u'chi-ro',
117 u'chlo-ro',
118 u'cho-reo',
119 u'chro-mo',
120 u'chro-no',
121 u'cy-ano',
122 u'dar',
123 u'de-ka',
124 u'de-zi',
125 u'demo',
126 u'der-ma-to',
127 u'des',
128 u'di-cho',
129 u'di-no',
130 u'dia',
131 u'dis',
132 u'dis-ko',
133 u'down',
134 u'drein',
135 u'durch',
136 u'dys',
137 u'e-tho',
138 u'ego',
139 u'ein',
140 u'elek-tro',
141 u'em-por',
142 u'emp',
143 u'en-do',
144 u'en-te-ro',
145 u'ent',
146 u'epi',
147 u'er-go',
148 u'er',
149 u'es-chato',
150 u'eth-no',
151 u'ety-mo',
152 u'ext-ro',
153 u'ex',
154 u'fe-ro',
155 u'fem-to',
156 u'fer-ro',
157 u'fo-no',
158 u'fort',
159 u'fran-ko',
160 u'für',
161 u'ga-so',
162 u'ge-gen',
163 u'ge-no',
164 u'ge-ron-to',
165 u'geo',
166 u'ge',
167 u'gi-ga',
168 u'gi-gan-to',
169 u'go-no',
170 u'gra-fo',
171 u'gra-pho',
172 u'gy-nä-ko',
173 u'he-lio',
174 u'he-te-ro',
175 u'he-xa',
176 u'hek-to',
177 u'hekt',
178 u'hemi',
179 u'her',
180 u'hier',
181 u'hin',
182 u'hin-ter',
183 u'hint',
184 u'ho-lo',
185 u'ho-mo',
186 u'ho-möo',
187 u'hoch',
188 u'hy-dro',
189 u'hy-per',
190 u'hy-po',
191 u'hym-no',
192 u'hyp-no',
193 u'hä-ma-to',
194 u'hä-mo',
195 u'ideo',
196 u'idio',
197 u'iko-no',
198 u'il',
199 u'im',
200 u'im-mu-no',
201 u'in',
202 u'in-fra',
203 u'in-ter',
204 u'in-tra',
205 u'ins',
206 u'int-ro',
207 u'io-no',
208 u'kar-dio',
209 u'kar-to',
210 u'kata',
211 u'klep-to',
212 u'kli-no',
213 u'kon',
214 u'kon-tra',
215 u'kor-re',
216 u'kos-mo',
217 u'kri-mi-no',
218 u'kri-no',
219 u'kryp-to',
220 u'leu-ko',
221 u'leuk',
222 u'le-xi-ko',
223 u'li-tho',
224 u'lim-no',
225 u'lo-go',
226 u'los',
227 u'lym-pho',
228 u'ma-gne-to',
229 u'mak-ro',
230 u'mam-mo',
231 u'me-ga',
232 u'me-lo',
233 u'me-so',
234 u'me-ta',
235 u'me-teo-ro',
236 u'me-tho-do',
237 u'mik-ro',
238 u'mil-li',
239 u'miss',
240 u'mit',
241 u'mo-no',
242 u'mor-pho',
243 u'mu-si-ko',
244 u'mul-ti',
245 u'my-co',
246 u'my-tho',
247 u'na-no',
248 u'nach',
249 u'ne-ben',
250 u'neo',
251 u'neu-ro',
252 u'neur',
253 u'nie-der',
254 u'no-wo',
255 u'non',
256 u'nost',
257 u'ob',
258 u'oben',
259 u'ober',
260 u'off',
261 u'ohn',
262 u'oli-go',
263 u'olig',
264 u'om-ni',
265 u'on-ko',
266 u'on-to',
267 u'op-to',
268 u'or-tho',
269 u'oszil-lo',
270 u'out',
271 u'over',
272 u'oxy',
273 u'ozea-no',
274 u'pa-ra',
275 u'pa-tho',
276 u'pa-tri',
277 u'pan-to',
278 u'pe-re',
279 u'pen-ta',
280 u'pet-ro',
281 u'phar-ma',
282 u'phar-ma-ko',
283 u'phi-lo',
284 u'phil',
285 u'pho-no',
286 u'pho-to',
287 u'phra-seo',
288 u'phy-lo',
289 u'phy-sio',
290 u'phy-to',
291 u'phä-no',
292 u'pneu-mo',
293 u'po-eto',
294 u'po-li-to',
295 u'po-ly',
296 u'po-ten-tio',
297 u'pro-to',
298 u'prä',
299 u'pseud',
300 u'psy-cho',
301 u'py-ro',
302 u'pä-do',
303 u'päd',
304 u'raus',
305 u're',
306 u'rein',
307 u'ret-ro',
308 u'ri-bo',
309 u'rä-to',
310 u'rück',
311 u'sa-mo',
312 u'sak-ro',
313 u'se-mi',
314 u'seis-mo',
315 u'selb',
316 u'ser-bo',
317 u'si-no',
318 u'so',
319 u'so-zio',
320 u'sou',
321 u'spek-tro',
322 u'ste-no',
323 u'ste-reo',
324 u'ste-tho',
325 u'stra-to',
326 u'su-per',
327 u'sub',
328 u'sup-ra',
329 u'sus',
330 u'syn',
331 u'ta-xo',
332 u'tau-to',
333 u'te-leo',
334 u'te-ra',
335 u'tech-no',
336 u'tele',
337 u'telo',
338 u'ter-mi-no',
339 u'tet-ra',
340 u'ther-mo',
341 u'throm-bo',
342 u'to-mo',
343 u'to-po',
344 u'to-xi-ko',
345 u'tra-gi',
346 u'trans',
347 u'tro-po',
348 u'tur-bo',
349 u'ty-po',
350 u'ul-tra',
351 u'um',
352 u'un',
353 u'un-der',
354 u'un-ter',
355 u'uni',
356 u'ur',
357 u'uro',
358 u'ver',
359 u'vi-no',
360 u'vi-ro',
361 u'vib-ra',
362 u'voll',
363 u'von',
364 u'vor',
365 u'vorn',
366 u'vul-ka-no',
367 u'weg',
368 u'wi-der',
369 u'xe-no',
370 u'xy-lo',
371 u'zen-ti',
372 u'zen-tri',
373 u'zer',
374 u'zu',
375 u'zwie',
376 u'zy-klo',
377 u'zy-to',
378 u'ägyp-to',
379 u'öko',
380 u'über',
383 # Nach Länge sortieren, damit spezifischere zuerst Probiert werden:
384 praefixe.sort(key = len)
385 praefixe.reverse()
388 # Übertrag von Einträgen auf Wörter mit anderer Endung::
390 def endungsabgleich(key, alt, neu, grossklein=False):
392 if not key.endswith(join_word(neu)):
393 return None
394 OK = True
395 altkey = key[:-len(join_word(neu))] + join_word(alt)
396 if grossklein:
397 altkey = toggle_case(altkey)
399 try:
400 altentry = words[altkey]
401 except KeyError:
402 return None
404 entry = WordEntry(key)
405 # print "fundum", key, unicode(entry)
406 for wort in altentry[1:]:
407 if wort.startswith(u'-'):
408 continue
409 if not wort.endswith(neu):
410 continue
411 if alt:
412 wort = wort[:-len(alt)]
413 wort += neu
414 if grossklein:
415 wort = toggle_case(wort)
416 if join_word(wort) != key:
417 OK = False
418 entry.append(wort)
420 if OK is False:
421 print u"# Übertragungsproblem: %s -> %s (%s,%s) %s" % (
422 altkey, key, alt, neu, unicode(entry))
423 return None
424 if len(entry) == 1: # keine Übertragung möglich
425 return None
427 entry.regelaenderungen() # Sprachabgleich
428 return entry
431 # Endungen
432 # --------
433 # ``(<alt>, <neu>)`` Paare von Endungen::
435 endungen = [
436 (u'', u'-de'),
437 # (u'', u'-en'),
438 # (u'', u'-er'),
439 # (u'', u'-is-mus'),
440 # (u'', u'-ität'),
441 (u'', u'-lein'),
442 (u'', u'-ne'),
443 (u'', u'-nem'),
444 (u'', u'-nen'),
445 (u'', u'-ner'),
446 (u'', u'-sche'),
447 (u'', u'-tum'),
448 (u'', u'>ar-tig'),
449 (u'', u'>chen'),
450 (u'', u'>heit'),
451 (u'', u'>keit'),
452 (u'', u'>los'),
453 (u'', u'>schaft'),
454 (u'', u'>schaft'),
455 (u'', u'>wei-se'),
456 # (u'', u'd'),
457 # (u'', u'e'),
458 # (u'', u'e-rin'),
459 # (u'', u'er'),
460 # (u'', u'is-mus'),
461 # (u'', u'm'),
462 # (u'', u'n'),
463 # (u'', u'ner'),
464 # (u'', u'r'),
465 # (u'', u's'),
466 # (u'', u's-te'),
467 # (u'', u's-te'),
468 # (u'', u's>los'),
469 # (u'', u'st'),
470 # (u'', u't'),
471 # (u'', u't-te'),
472 (u'-al', u'a-le'),
473 (u'-an', u'a-ne'),
474 (u'-at', u'a-te'),
475 (u'-ben', u'b-ne'),
476 # (u'-che', u'ch'),
477 (u'-de', u'd'),
478 (u'-en', u'>bar>keit'),
479 # (u'-en', u'e'),
480 (u'-en', u'e-ne'),
481 (u'-er', u'e-rei'),
482 (u'-er', u'e-rin'),
483 (u'-ern', u'e-re'),
484 (u'-ge', u'g'),
485 (u'-gen', u'g'),
486 (u'-in', u'i-ne'),
487 (u'-on', u'o-nen'),
488 (u'-re', u'r'),
489 (u'-re', u'rt'),
490 (u'-ren', u'r-ne'),
491 (u'-ren', u'rt'),
492 (u'-sche', u'sch'),
493 (u'-sen', u's-ne'),
494 (u'-sten', u's-mus'),
495 (u'-te',u't'),
496 (u'-tern', u't-re'),
497 (u'-ös', u'ö-se'),
498 (u'a', u'ar'),
499 (u'a', u'as'),
500 (u'b', u'-be'),
501 (u'b', u'-ber'),
502 (u'bar', u't'),
503 (u'bt', u'b-te'),
504 (u'ce', u'-cen'),
505 (u'ch', u'-che'),
506 (u'ch', u'-cher'),
507 (u'ck', u'-cke'),
508 (u'ck', u'-cker'),
509 (u'd', u'-de'),
510 (u'd', u'-dem'),
511 (u'd', u'-den'),
512 (u'd', u'-der'),
513 (u'd', u'-des'),
514 (u'd', u'>heit'),
515 (u'e', u'en'),
516 (u'e-ren', u'-ti-on'),
517 (u'e-ren', u'sch'),
518 (u'el', u'le'),
519 # (u'en', u'e'),
520 (u'en', u'em'),
521 (u'en', u'en-de'),
522 (u'en', u'end'),
523 (u'en', u'er'),
524 (u'en', u'es'),
525 (u'en', u'est'),
526 (u'en', u't'),
527 (u'en', u'te'),
528 (u'en', u'us'),
529 (u'end',u'en' ),
530 # (u'er', u'e'),
531 (u'er', u'e-rei'),
532 (u'er', u'ens'),
533 (u'er', u'in'),
534 (u'er', u'ung'),
535 (u'es', u'est'),
536 (u'es', u's-te'),
537 (u'f', u'-fe'),
538 (u'f', u'-fer'),
539 (u'g', u'-ge'),
540 (u'g', u'-gen'),
541 (u'g', u'-ger'),
542 (u'g', u'-ger'),
543 (u'g', u'-ges'),
544 (u'g', u'-gung'),
545 (u'ie', u'e'),
546 (u'in', u'en'),
547 (u'isch', u'i-sche'),
548 (u'ck', u'-cke'),
549 (u'k', u'-ke'),
550 (u'k', u'-ken'),
551 (u'k', u'-ker'),
552 (u'l', u'-le'),
553 (u'l', u'-len'),
554 (u'l', u'-ler'),
555 (u'l', u'-lis-mus'),
556 (u'le', u'-ler'),
557 (u'li-che', u'tem'),
558 (u'li-che', u'ten'),
559 (u'ln', u'-le'),
560 (u'lt', u'-le'),
561 (u'm', u'-me'),
562 (u'm', u'-mer'),
563 (u'me', u'men'),
564 (u'mus', u'men'),
565 (u'mus', u'ten'),
566 (u'mus', u'tik'),
567 (u'n', u'-at'),
568 (u'n', u'-er'),
569 (u'n', u'-ne'),
570 (u'n', u'-nen'),
571 (u'on', u'o-nis-mus'),
572 (u'n', u'-nis-mus'),
573 (u'n', u'r'),
574 (u'n', u'st'),
575 (u'n', u't'),
576 (u'n',u'-ner'),
577 (u'nd',u'n'),
578 (u'ne',u'ner'),
579 # (u'ne',u'n'),
580 (u'o',u'-on'),
581 (u'o',u'-os'),
582 (u'o',u'en'),
583 (u'on',u'o-nen'),
584 (u'p', u'-pe'),
585 (u'p', u'-pen'),
586 (u'p', u'-per'),
587 (u'ph', u'-phen'),
588 (u'ph', u'-phis-mus'),
589 (u'r', u'-re'),
590 (u'r', u'-rei'),
591 (u'r', u'-ren'),
592 (u'r', u'-rin'),
593 (u'r', u'-ris-mus'),
594 (u'r', u'-rung'),
595 (u're', u'ste'),
596 (u'ren', u'r-te'),
597 (u'ren', u'rst'),
598 (u'ren', u'rt'),
599 (u'rn', u'-re'),
600 (u'rn', u'-rung'),
601 (u'rn', u'-rung'),
602 (u'rt', u'-re'),
603 (u'rt', u'r-te'),
604 (u's', u''),
605 (u's', u'-se'),
606 (u's', u'-se-re'),
607 (u's', u'-se-res'),
608 (u's', u'-ser'),
609 (u's', u's-se'),
610 (u's', u's-ses'),
611 (u'sch', u'-sche'),
612 (u'sch', u'-schen'),
613 (u'sch', u'-scher'),
614 (u'st', u'-ste'),
615 (u'st', u'-sten'),
616 (u'st', u'n'),
617 (u't', u'-ba-re'),
618 (u't', u'>bar'),
619 (u't', u'-te'),
620 (u't', u'-te'),
621 (u't', u'-ten'),
622 (u't', u'-ter'),
623 (u't', u'-tes'),
624 (u't', u'-tin'),
625 (u't', u'-tis-mus'),
626 # (u't', u'e'),
627 (u't', u'n'),
628 (u't', u'st'),
629 (u'te', u'le'),
630 # (u'te', u't'),
631 (u'ten', u'mus'),
632 (u'ten', u'ren'),
633 (u'ten', u'tung'),
634 (u'ter', u'te-ren'),
635 (u'ti-on', u'tor'),
636 (u'um', u'a'),
637 (u'us', u'en'),
638 (u'v', u'-ve'),
639 (u'v', u'-ver'),
640 (u'v', u'-vis-mus'),
641 (u'-ve', u'v'),
642 (u'z', u'-ten'),
643 (u'z', u'-ze'),
644 (u'z', u'-zen'),
645 (u'z', u'-zer'),
646 (u'ß', u'-ße'),
647 (u'ß', u's-se'),
648 (u'ös', u'ö-se'),
649 (u'=öl', u'=öle'),
652 # Zerlege einen String mit von vorn bis hinten wandernder Bruchstelle::
654 # >>> from abgleich_neueintraege import zerlege
655 # >>> list(zerlege(u'wolle'))
656 # [(u'w', u'olle'), (u'wo', u'lle'), (u'wol', u'le'), (u'woll', u'e')]
658 # ::
660 def zerlege(s):
661 for i in range(1, len(s)):
662 yield s[:i], s[i:]
664 # Zerlege Kompositum in gleichberechtigte Teile::
666 # >>> from abgleich_neueintraege import split_composits
667 # >>> from wortliste import WordEntry
668 # >>> split_composits(WordEntry(u'Blockheizkraftwerk;Block===heiz==kraft=werk'))
669 # [u'Block', u'heiz', u'kraft', u'werk']
671 # ::
673 def split_composits(entry):
674 return [w for w in entry[1].split(u'=') if w]
676 # Zerlege String, wenn die Teile in der Wortliste vorhanden sind, setze
677 # sie neu zusammen und übernimm die Trennmarkierer:
680 def trenne_key(key, grossklein = False):
681 entries = []
682 sep = u'='
683 for k1, k2 in zerlege(key):
684 if grossklein:
685 k1 = toggle_case(k1)
686 if k1.istitle():
687 k2 = k2.title()
688 e1 = words.get(k1)
689 e2 = words.get(k2)
690 if not e2:
691 e2 = words.get(toggle_case(k2))
692 if e1 and e2:
693 if len(e1) != len(e2):
694 if len(e1) == 2:
695 e1 = [e1[1]] * len(e2)
696 elif len(e2) == 2:
697 e2 = [e2[1]] * len(e1)
698 else:
699 continue
700 entry = WordEntry(key)
701 for w1, w2 in zip(e1,e2)[1:]:
702 if w1.startswith(u'-'): # empty column -2-, -3-, ...
703 wort = w1
704 elif w2.startswith(u'-'):
705 wort = w2
706 else:
707 if grossklein:
708 w1 = toggle_case(w1)
709 w2 = w2.lower()
710 level = 1
711 while (level*sep in w1) or (level*sep in w2):
712 level += 1
713 wort = (level*sep).join([w1, w2])
714 entry.append(wort)
715 entry.conflate_fields()
716 entries.append(entry)
717 # Teste auf 3-teilige Composita und entferne die Wichtung:
718 # ['Kau==zahn=weh', 'Kau=zahn=weh'] -> ['Kau=zahn=weh']
719 if len(entries) == 2:
720 teile = [split_composits(entry) for entry in entries]
721 if teile[0] == teile[1]:
722 level = 1
723 while level*sep in teile[0]:
724 level += 1
725 entries = [entries[0]]
726 entries[0][1] = entries[0][1].replace((level+1)*sep, level*sep)
727 return entries
729 def filter_neuliste(liste, words):
730 for line in liste:
731 line = line.decode('utf8').strip()
732 if line.startswith('#'):
733 yield line
734 continue
735 neukey = line.split(u';')[0]
736 if neukey in words:
737 # print 'vorhanden:', line
738 continue
739 if neukey.title() in words:
740 # print 'Vorhanden:', line
741 continue
742 if neukey.lower() in words:
743 # print 'vorhanden (kleingeschrieben):', line
744 continue
745 yield line
747 class SortableDict(dict):
748 """Dictionary with additional sorting methods
750 Tip: use key starting with with '_' for sorting before small letters
751 and with '~' for sorting after small letters.
753 def sortedkeys(self):
754 """Return sorted list of keys"""
755 keys = self.keys()
756 keys.sort()
757 return keys
759 def sortedvalues(self):
760 """Return list of values sorted by keys"""
761 return [self[key] for key in self.sortedkeys()]
763 def filter_ableitungen(liste):
764 words = SortableDict()
765 words['#'] = '# Ableitungen entfernt'
766 for line in liste:
767 line = line.decode('utf8').strip()
768 if line.startswith('#'):
769 words['#'] += '\n' + line
770 continue
771 key = line.split(u';')[0]
772 gibts_schon = False
773 for alt, neu in endungen:
774 altkey = key[:-len(join_word(neu))] + join_word(alt)
775 if altkey in words:
776 gibts_schon = True
777 break
778 if not gibts_schon:
779 words[key] = line
780 return words.sortedvalues()
782 def print_proposal(entry):
783 proposal = getattr(entry, "proposal", u'')
784 if proposal and len(proposal) > 1:
785 print u' ' + unicode(entry)
786 print u'#' + unicode(proposal)
787 else:
788 print unicode(entry)
790 if __name__ == '__main__':
792 # Pfad zu "../../../wortliste" unabhängig vom Arbeitsverzeichnis::
794 default_wortliste = os.path.relpath(os.path.join(
795 os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
796 os.path.abspath(__file__))))),
797 'wortliste'))
799 # os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
801 # Optionen::
803 usage = '%prog [Optionen]\n' + __doc__
805 parser = optparse.OptionParser(usage=usage)
806 parser.add_option('-i', '--file', dest='wortliste',
807 help='Vergleichsdatei, Vorgabe "%s"'%default_wortliste,
808 default=default_wortliste)
809 parser.add_option('-f', '--filter', action="store_true",
810 help=u'in WORTLISTE vorhandene Wörter aussortieren',
811 default=False)
812 parser.add_option('-a', '--filter-ableitungen', action="store_true",
813 help=u'Ableitungen von Wörtern der Eingabe aussortieren',
814 default=False)
815 (options, args) = parser.parse_args()
817 # sys.stdout mit UTF8 encoding.
818 sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)
820 wordfile = WordFile(options.wortliste)
822 # Filtern::
824 if options.filter:
825 words = wordfile.asdict()
826 for line in filter_neuliste(sys.stdin, words):
827 print line
828 sys.exit()
830 if options.filter_ableitungen:
831 for line in filter_ableitungen(sys.stdin):
832 print line
833 sys.exit()
835 # `Wortliste` einlesen
837 # Wörter, Teilwörter und Kombinationen (siehe expand_teilwoerter.py)
838 # entweder vom "cache", oder "live" expandiert::
840 cache = "wortliste-expandiert"
841 try:
842 cache_mtime = os.path.getmtime(cache)
843 except OSError:
844 cache_mtime = 0
846 if os.path.getmtime(options.wortliste) <= cache_mtime:
847 words = WordFile(cache).asdict()
848 else:
849 words = expand_wordfile(wordfile)
851 # Aussortieren von Wörtern, die zu "false positives" führen::
853 # Wörter, die oft als Endungen auftauchen:
854 for alt, neu in endungen:
855 words.pop(join_word(neu), None)
857 for unwort in [u'Em', u'Gen']:
858 words.pop(unwort, None)
860 # Erstellen der neuen Einträge::
862 neue = []
863 neue_grossklein = []
864 rest = []
866 proposals = [WordEntry(line.decode('utf8').strip())
867 for line in sys.stdin
868 if not line.startswith('#')]
870 for newentry in proposals:
871 OK = False
872 key = newentry[0]
874 # print key, unicode(newentry)
875 # continue
877 # Test auf vorhandene (Teil-) Wörter:
879 entry = words.get(key)
880 if entry:
881 neue.append(entry)
882 continue
883 # kleingeschrieben
884 entry = words.get(key.lower())
885 if entry:
886 neue_grossklein.append(entry)
887 continue
888 # Großgeschrieben
889 entry = words.get(key.title())
890 if entry:
891 neue_grossklein.append(entry)
892 continue
894 # Endungsabgleich::
896 for alt, neu in endungen:
897 entry = endungsabgleich(key, alt, neu, grossklein=False)
898 if entry:
899 entry.comment = newentry.comment
900 neue.append(entry)
901 OK = True
902 # break
903 if OK:
904 continue
906 for alt, neu in endungen:
907 entry = endungsabgleich(key, alt, neu, grossklein=True)
908 if entry:
909 entry.comment = newentry.comment
910 neue_grossklein.append(entry)
911 OK = True
912 # break
913 if OK:
914 continue
916 # Präfixabgleich::
918 for praefix in praefixe:
919 entry = praefixabgleich(key, praefix, grossklein=False)
920 if entry:
921 entry.comment = newentry.comment
922 neue.append(entry)
923 OK = True
924 break
925 entry = praefixabgleich(key, praefix, grossklein=True)
926 if entry:
927 entry.comment = newentry.comment
928 neue_grossklein.append(entry)
929 OK = True
930 break
931 if OK:
932 continue
934 # Zerlegen und test auf Fugen::
936 entries = trenne_key(key, grossklein=False)
937 if entries:
938 neue.extend(entries)
939 continue
940 entries = trenne_key(key, grossklein=True)
941 if entries:
942 neue_grossklein.extend(entries)
943 continue
945 # Nicht gefundene Wörter::
947 rest.append(newentry)
949 # Mehrdeutige aussortieren::
951 alle_neuen = {}
952 doppelkeys = set()
953 doppelkeys_gleich = defaultdict(int)
955 # doppelte keys finden:
956 for entry in neue + neue_grossklein:
957 key = entry[0].lower()
958 if key in alle_neuen:
959 if entry == alle_neuen[key]:
960 doppelkeys_gleich[key] += 1
961 else:
962 doppelkeys.add(key)
963 alle_neuen[key] = entry
965 # doppelte Einträge "verlegen":
966 eindeutige = []
967 eindeutige_grossklein = []
968 doppelte = []
970 for entry in neue:
971 key = entry[0].lower()
972 if key in doppelkeys:
973 doppelte.append(entry)
974 elif doppelkeys_gleich[key] > 0:
975 doppelkeys_gleich[key] -= 1
976 else:
977 eindeutige.append(entry)
979 for entry in neue_grossklein:
980 key = entry[0].lower()
981 if key in doppelkeys:
982 doppelte.append(entry)
983 elif doppelkeys_gleich[key] > 0:
984 doppelkeys_gleich[key] -= 1
985 else:
986 eindeutige_grossklein.append(entry)
989 # Vergleich mit Original::
991 identische = {}
992 for proposal in proposals:
993 key = proposal[0].lower()
994 newentry = alle_neuen.get(key)
995 if proposal == newentry:
996 identische[key] = proposal
997 else:
998 if newentry:
999 newentry.proposal = proposal
1001 # Ausgabe::
1003 print u'\n# identisch rekonstruiert:'
1004 for entry in sorted(identische.values(), key=sortkey_duden):
1005 print unicode(entry)
1007 print u'\n# eindeutig abgeleitet'
1008 for entry in eindeutige:
1009 if entry[0].lower() not in identische:
1010 print_proposal(entry)
1011 print u'\n# eindeutig abgeleitet (andere Großschreibung)'
1012 for entry in eindeutige_grossklein:
1013 if entry[0].lower() not in identische:
1014 print_proposal(entry)
1016 print u'\n# mehrdeutig abgeleitet'
1017 for entry in doppelte:
1018 print_proposal(entry)
1021 print u'\n# Rest'
1023 for entry in rest:
1024 print_proposal(entry)