3 # :Copyright: © 2012 Günter Milde.
4 # :Licence: This work may be distributed and/or modified under
5 # the conditions of the `LaTeX Project Public License`,
6 # either version 1.3 of this license or (at your option)
8 # :Version: 0.1 (2012-02-07)
14 """Hilfsmittel für die Arbeit mit der `Wortliste`"""
18 # Die hier versammelten Funktionen und Klassen dienen der Arbeit an und
19 # mit der freien `Wortliste der deutschsprachigen Trennmustermannschaft`_
25 # Python 2.7 mit den Standardbibliotheken::
34 # append path to the "edit_tools" package
35 sys
.path
.append(os
.path
.dirname(os
.path
.dirname(__file__
)))
36 # Filter für Wahltrennungen (Re<s-pekt -> Re<spekt, …)::
37 from edit_tools
.stilfilter
import morphemisch
38 from edit_tools
import stilfilter
44 # Klasse zum Lesen und Schreiben der `Wortliste`::
51 # Das Argument ``format`` erwartet einen der Strings
53 # :f8: das originale, maximal 8-spaltige Wortlisten-Format (Langform_),
54 # :f5: das neue, maximal 5-spaltige Wortlisten-Format (Kurzform_), oder
55 # :auto: bestimme das Format automatisch.
59 def __init__(self
, name
, mode
='r', encoding
='utf8', format
='f8'):
63 self
.encoding
= encoding
64 self
.wordfile
= open(name
, mode
=mode
, encoding
=encoding
)
65 self
.seek
= self
.wordfile
.seek
66 # Dateiformat bestimmen und die Eintrags-Klasse setzen:
67 self
.set_entry_class(format
)
69 # Klasse zum Verarbeiten der Zeilen setzen.
70 # Mit self.format == "auto", werden die ersten Zeilen der Datei untersucht
71 # und der Pointer zurückgesetzt.
75 def set_entry_class(self
, format
, search_limit
=1000):
77 assert(format
in ('f8', 'f5', 'auto'))
78 self
.format
= format
# default
80 self
.entry_class
= WordEntry
83 # Format anhand des Dateiinhalts bestimmen:
85 # print("auto-determining word file format")
87 # nur ein Feld oder erstes Feld leer oder mit Trennzeichen:
88 if len(e
) == 1 or e
and re
.search('[-=<>.·]', e
[0]):
91 # mehr als 5 Felder: 8-Felder-Format (Langform)
98 if self
.format
== 'f5':
99 self
.entry_class
= ShortEntry
101 self
.wordfile
.seek(0) # Dateizeiger zurücksetzen
107 # Die spezielle Funktion `__iter__` wird aufgerufen wenn über eine
108 # Klasseninstanz iteriert wird.
109 # Sie liefert einen Iterator über die "geparsten" Zeilen (Datenfelder)
113 entry_class
= self
.entry_class
114 for line
in self
.wordfile
:
115 yield entry_class(line
.rstrip())
121 # Gib ein `dictionary` (assoziatives Array) mit ungetrenntem Wort als
122 # Schlüssel (`key`) und den Datenfeldern als `value` zurück::
124 def asdict(self
, lowkey
=False):
128 if key
== '#': # reiner Kommentar
130 words
['#'].append(entry
.comment
)
132 words
['#'] = entry
.comment
134 words
[key
.lower()] = entry
142 # Schreibe eine Liste von `unicode` Strings (Zeilen ohne Zeilenendezeichen)
143 # in die Datei `destination`::
145 def writelines(self
, lines
, destination
, encoding
=None):
146 outfile
= open(destination
, 'w',
147 encoding
=(encoding
or self
.encoding
))
148 outfile
.writelines(lines
)
153 # Schreibe eine Liste von Einträgen (WordEntry_ oder ShortEntry_ Objekten) in
154 # die Datei `destination`::
156 def write_entries(self
, wortliste
, destination
, encoding
=None):
157 lines
= (str(entry
) for entry
in wortliste
)
158 self
.writelines(lines
, destination
, encoding
)
164 # Klasse für Einträge (Zeilen) der Wortliste_ in Langform_.
165 # Jede Zeile enthält einen Eintrag mit durch Semikolon „;“
166 # getrennten Feldern.
170 # >>> from wortliste import WordEntry
172 # >>> aalbestand = WordEntry('Aalbestand;Aal=be<stand # Test')
173 # >>> print(aalbestand)
174 # Aalbestand;Aal=be<stand # Test
177 # .. _8-Spalten-Format:
179 # Bedeutung der Felder im 8-Spalten-Format (Langform):
182 # 2. Wort mit Trennungen, falls für alle Varianten identisch,
184 # 3. falls Feld 2 leer, Trennung nach traditioneller Rechtschreibung
185 # 4. falls Feld 2 leer, Trennung nach reformierter Rechtschreibung (2006)
186 # 5. falls Feld 2 leer, Trennung für Wortform, die entweder in
187 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird
188 # und für traditionelle und reformierte Rechtschreibung identisch ist
189 # 6. falls Feld 5 leer, Trennung für Wortform, die entweder in
190 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
191 # traditionelle Rechtschreibung
192 # 7. falls Feld 5 leer, Trennung für Wortform, die entweder in
193 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
194 # reformierte Rechtschreibung (2006)
195 # 8. Falls Feld 5 leer und nicht identisch mit Feld 6, Trennung
196 # nach (deutsch)schweizerischer, traditioneller Rechtschreibung
197 # mit Trennung von „ss“, auch wenn es für „ß“ steht.
201 class WordEntry(list):
206 # feldnamen, feldindizes
207 # ~~~~~~~~~~~~~~~~~~~~~~
209 # Benennung der Felder mit Sprachkürzeln (Tags nach [BCP47]_).
210 # (Die Zählung der Indizes beginnt in Python bei 0.)::
212 feldnamen
= ('key', 'de', 'de-1901', 'de-1996', 'de-x-versal',
213 'de-1901-x-versal', 'de-1996-x-versal', 'de-CH-1901')
216 'key': 0, # Schlüssel
217 'de': 1, # Deutsch, allgemeingültig
218 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
219 'de-1996': 3, # reformierte Reformschreibung (1996)
220 'de-x-versal': 4, # ohne ß (Schweiz oder versal) allgemein
222 'de-1901-x-versal': 5, # ohne ß (Schweiz oder versal) "traditionell"
223 'de-1996-x-versal': 6, # ohne ß (Schweiz oder versal) "reformiert"
224 'de-CH-1996': 6, # Alias
225 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
232 # Bei der Auswahl eines Wortes in einer Rechtschreibvariante werden
233 # "generische" Felder gewählt, falls das "Originalfeld" fehlt. Die folgenden
234 # Tupel listen die Ersatzfelder für das ensprechende Originalfeld.
236 # Bsp: Ersatz-Felder für 'de-CH-1901':
238 # >>> for i in WordEntry.ersatzfelder[WordEntry.feldindizes['de-CH-1901']]:
239 # ... print(WordEntry.feldnamen[i])
247 # Ersatzindizes Index Nr Name
248 ersatzfelder
= (tuple(), # 0 1 key
250 (1,), # 2 3 'de-1901'
251 (1,), # 3 4 'de-1996'
252 (1,), # 4 5 'de-x-versal'
253 (4, 2, 1), # 5 6 'de-1901-x-versal'
254 (4, 3, 1), # 6 7 'de-1996-x-versal'
255 (5, 4, 2, 1), # 7 8 'de-CH-1901'
262 # Kommentar, Vorgabe ist ein leerer String::
272 def __init__(self
, line
, delimiter
=';'):
274 self
.delimiter
= delimiter
276 # eventuell vorhandenen Kommentar abtrennen und speichern::
279 line
, comment
= line
.split('#', 1)
280 self
.comment
= comment
.strip()
282 # print(line, self.comment)
284 if not line
: # kein Inhalt
287 # Zerlegen in Datenfelder, in Liste eintragen::
289 for field
in line
.split(delimiter
):
290 if field
.startswith('-'):
296 # Rückverwandlung in String
297 # -------------------------
299 # Erzeugen eines Eintrag-Strings (ohne Zeilenendezeichen) aus der Liste der
300 # Datenfelder und dem Kommentar
305 # >>> str(aalbestand)
306 # 'Aalbestand;Aal=be<stand # Test'
308 # >>> grse = WordEntry('Grüße;Grü-ße')
312 # >>> leerkommentar = WordEntry('# Testkommentar ')
313 # >>> leerkommentar, str(leerkommentar)
314 # (WordEntry('# Testkommentar'), '# Testkommentar')
319 # Nummerieren leerer Felder:
320 fields
= [field
or '-%d-' % (i
+1)
321 for (i
, field
) in enumerate(self
)]
322 line
= ';'.join(fields
)
324 line
+= ' # ' + self
.comment
331 # >>> print(type(repr(grse)), repr(grse))
332 # <class 'str'> WordEntry('Grüße;Grü-ße')
337 s
= '%s(\'%s\')' % (self
.__class
__.__name
__, self
)
343 # Bestimme den Index des zur Sprachvariante gehörenden Datenfeldes unter
344 # Verwendung der Ersatzfelder:
346 # >>> aalbestand.lang_index('de')
348 # >>> aalbestand.lang_index('de-1901')
350 # >>> aalbestand.lang_index('de-1996')
352 # >>> aalbestand.lang_index('de-x-versal')
354 # >>> aalbestand.lang_index('de-1901-x-versal')
356 # >>> aalbestand.lang_index('de-1996-x-versal')
358 # >>> abbeissen = WordEntry(
359 # ... 'abbeissen;-2-;-3-;-4-;-5-;ab<bei-ssen;ab<beis-sen;ab<beis-sen')
360 # >>> print(abbeissen.lang_index('de'))
362 # >>> print(abbeissen.lang_index('de-x-versal'))
364 # >>> abbeissen.lang_index('de-1901-x-versal')
366 # >>> abbeissen.lang_index('de-1996-x-versal')
368 # >>> abbeissen.lang_index('de-CH-1901')
370 # >>> urlaubstipp = WordEntry('Urlaubstipp;-2-;-3-;Ur<laubs=tipp')
371 # >>> print(urlaubstipp.lang_index('de'))
373 # >>> print(urlaubstipp.lang_index('de-1901'))
375 # >>> print(urlaubstipp.lang_index('de-1996'))
377 # >>> print(urlaubstipp.lang_index('de-x-versal'))
379 # >>> print(urlaubstipp.lang_index('de-1901-x-versal'))
384 def lang_index(self
, lang
):
386 if lang
not in self
.feldindizes
:
387 raise ValueError('Sprachvariante "%s" nicht in %s'
388 % (lang
, self
.feldindizes
.keys()))
390 # Einfacher Fall: eine allgemeine Schreibweise::
393 if 'ß' in self
[0] and ('CH' in lang
or 'versal' in lang
):
398 # Spezielle Schreibung::
401 i
= self
.feldindizes
[lang
]
404 if lang
== 'de-CH-1901':
405 return self
.lang_index('de-1901-x-versal')
406 # Allgemeine Schweiz/versal Schreibung:
407 if i
> 4 and len(self
) == 5:
409 # versal == normal (kein sz):
410 if len(self
) == 4 and 'ß' not in self
[0]:
411 if lang
in ('de-CH-1901', 'de-1901-x-versal'):
412 return self
.lang_index('de-1901')
413 elif lang
in ('de-CH-1996', 'de-1996-x-versal'):
414 return self
.lang_index('de-1996')
415 return None # Feld nicht vorhanden
417 if not feld
: # leeres Feld
425 # Gib einen Schlüssel (ungetrenntes Wort) zurück.
427 # Bei Einträgen im Langform_ ist der Schlüssel gleich dem Inhalt des ersten
428 # Feldes. Die Funktion dient zur Kompatibilität mit ShortEntry_.
430 # >>> print(WordEntry('Fluss;Quatsch').key())
432 # >>> print(WordEntry('Dreß;-1-;Dreß;-3-').key())
435 # Das `lang` Argument wird ignoriert, da der Schlüssel (im Gegensatz zur
436 # Kurzform_) stets eindeutig ist:
438 # >>> print(WordEntry('Dreß;-1-;Dreß;-3-').key('de-1996'))
441 # Der Schlüssel eines leeren Eintrags ist ein leerer String, der eines leeren
442 # Kommentars das Kommentarzeichen:
444 # >>> WordEntry('').key(), WordEntry('# toller Kommentar').key()
449 def key(self
, lang
=None):
450 """Gib den Schlüssel (ungetrenntes Wort) zurück."""
453 except IndexError: # reiner Kommentar oder leerer Eintrag
462 # Gib Feld ``i`` oder Ersatzfeld zurück.
465 def getitem(self
, i
):
466 """Return item ``i`` or a subsititute"""
469 except IndexError: # Feld i nicht vorhanden
471 # Ersatzregeln anwenden
472 for _i
in self
.ersatzfelder
[i
]:
475 except IndexError: # Feld i nicht vorhanden
476 continue # nächsten `tag` versuchen
477 # Spezialfall: in Versalschreibung ungültige Ersatz-Wörter
478 if i
>= 4 and _i
<4 and 'ß' in word
:
483 # .. _WordEntry.get():
488 # Trennmuster für eine Sprachvariante ausgeben:
490 # >>> aalbestand.get('de')
492 # >>> aalbestand.get('de-1901')
494 # >>> aalbestand.get('de-1996')
496 # >>> aalbestand.get('de-x-versal')
498 # >>> aalbestand.get('de-1901-x-versal')
500 # >>> aalbestand.get('de-1996-x-versal')
502 # >>> aalbestand.get('de-CH-1901')
505 # >>> weste = WordEntry('Weste;-2-;We-ste;Wes-te')
506 # >>> weste.get('de')
508 # >>> weste.get('de-1901')
510 # >>> weste.get('de-1996')
513 # >>> abbeissen.get('de')
515 # >>> abbeissen.get('de-x-versal')
517 # >>> abbeissen.get('de,de-x-versal')
519 # >>> abbeissen.get('de-1901-x-versal')
521 # >>> abbeissen.get('de,de-1901,de-1901-x-versal')
523 # >>> abbeissen.get('de-CH-1901')
529 for lang
in tags
.split(','):
530 word
= self
.getitem(self
.feldindizes
[lang
])
541 # Trennmuster für Sprachvariante setzen:
543 # >>> abbeissen.set('test', 'de-1901-x-versal')
544 # >>> print(abbeissen)
545 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
547 # >>> abbeissen.set('test', 'de-1901')
548 # Traceback (most recent call last):
550 # IndexError: kann kein leeres Feld setzen
552 # >>> abbeissen.set('test', 'de-1901,de-1901-x-versal')
553 # >>> print(abbeissen)
554 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
558 def set(self
, word
, tags
):
559 for lang
in tags
.split(','):
560 i
= self
.lang_index(lang
)
567 raise IndexError("kann kein leeres Feld setzen")
574 # >>> entry = WordEntry('test;test')
575 # >>> entry.setitem(4, 's-x')
577 # test;test;-3-;-4-;s-x
578 # >>> entry.setitem(3, 'sz')
580 # test;test;-3-;sz;s-x
584 def setitem(self
, i
, word
):
585 while len(self
) < i
+1:
593 # Alle Felder setzen:
595 # >>> print(WordEntry('Ruhe;Ru-he').completed())
596 # Ruhe;Ru-he;Ru-he;Ru-he;Ru-he;Ru-he;Ru-he;Ru-he
598 # >>> print(aalbestand, len(aalbestand))
599 # Aalbestand;Aal=be<stand # Test 2
600 # >>> print(aalbestand.completed())
601 # Aalbestand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand # Test
603 # >>> auffrass = WordEntry('auffrass;-2-;-3-;-4-;auf-frass')
604 # >>> print(auffrass.completed())
605 # auffrass;-2-;-3-;-4-;auf-frass;auf-frass;auf-frass;auf-frass
607 # >>> fresssack= WordEntry('Fresssack;-2-;-3-;Fress=sack;Fress=sack')
608 # >>> print(fresssack.completed())
609 # Fresssack;-2-;-3-;Fress=sack;Fress=sack;Fress=sack;Fress=sack;Fress=sack
611 # >>> line = 'Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-'
612 # >>> print(WordEntry(line).completed())
613 # Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-;Fluss=schi{ff/ff=f}ahrt
615 # >>> line = 'Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße'
616 # >>> ackerstrasse = WordEntry(line).completed()
617 # >>> print(ackerstrasse)
618 # Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße;-5-;-6-;-7-;-8-
623 for i
in range(len(self
),8):
624 self
.append(self
.getitem(i
))
626 # .. _WordEntry.completed():
631 # Gib eine vervollständigte Kopie des Eintrags zurück.
636 """Return expanded copy of self."""
637 neu
= copy
.copy(self
)
641 # .. _ShortEntry.prune():
648 # Felder für Sprachvarianten zusammenfassen, wenn gleich:
650 # >>> aalbestand.prune()
651 # >>> print(aalbestand)
652 # Aalbestand;Aal=be<stand # Test
653 # >>> auffrass.prune()
654 # >>> print(auffrass)
655 # auffrass;-2-;-3-;-4-;auf-frass
656 # >>> entry = WordEntry('distanziert;-2-;di-stan-ziert;di-stan-ziert')
659 # distanziert;di-stan-ziert
660 # >>> entry = WordEntry('Gauss;-2-;Gauss;Gauss;Gauss')
664 # >>> fresssack.prune()
665 # >>> print(fresssack)
666 # Fresssack;-2-;-3-;Fress=sack;Fress=sack
668 # >>> masse = WordEntry('Masse;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se')
673 # Aber nicht, wenn die Trennstellen sich unterscheiden:
675 # >>> abenddienste = WordEntry(
676 # ... 'Abenddienste;-2-;Abend=dien-ste;Abend=diens-te')
677 # >>> abenddienste.prune()
678 # >>> print(abenddienste)
679 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
680 # >>> ackerstrasse.prune()
681 # >>> print(ackerstrasse)
682 # Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße
683 # >>> entry = WordEntry(
684 # ... 'Schlammmasse;-2-;-3-;Schlamm=mas-se;-5-;-6-;Schlamm=mas-se;-8-')
687 # Schlammmasse;-2-;-3-;Schlamm=mas-se
688 # >>> entry = WordEntry(
689 # ... 'Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-')
692 # Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-
698 if self
[7] == self
[5]: # de-CH-1901 gleich versal-1901
701 if self
[6] == self
[5]:
702 self
[4] = self
[5] # umschreiben auf versal-allgemein
705 elif (not(self
[4] or self
[5] or self
[6])
706 or self
[5] == self
[2] and self
[6] == self
[3]):
707 # alle leer oder ohne ß-Ersetzung
714 elif self
[1] == self
[4]: # de-x-versal == de
718 elif self
[2] == self
[3] == self
[4]:
719 self
[1] = self
[2] # Umschreiben auf de (allgemein)
724 if self
[3] == self
[2]: # de-1996 == de-1901
725 self
[1] = self
[2] # Umschreiben auf de (allgemein)
738 # Gib eine gekürzte Kopie des Eintrags zurück.
742 """Return pruned copy of self."""
743 neu
= copy
.copy(self
)
747 # .. _WordEntry.merge():
752 # Einträge zusammenfassen:
754 # >>> entry = WordEntry('Dienste;-2-;Dien-ste')
755 # >>> entry.merge(WordEntry('Dienste;-2-;-3-;diens-te'))
757 # Dienste;-2-;Dien-ste;Diens-te
758 # >>> entry = WordEntry('Abenddress;Abend=dress')
759 # >>> entry.merge(WordEntry('Abenddress;-2-;-3-;-4-;Abend=dress'))
761 # Abenddress;Abend=dress
762 # >>> entry = WordEntry('Gauss;Gauss')
763 # >>> entry.merge(WordEntry('Gauss;-2-;-3-;-4-;Gauss'))
766 # >>> masse.merge(WordEntry('masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se'),
767 # ... allow_alternatives=True)
769 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[s-/-s]se;Mas-se;Mas-se
773 def merge(self
, other
, allow_alternatives
=False, prune
=True,
774 merge_comments
=False, start
=0, stop
=None):
777 islower
= self
.key().islower()
779 # `stop=None` oder `stop=0` bedeutet "keine Obergrenze".
780 stop
= stop
or len(self
)
781 for i
in range(start
, stop
):
785 if islower
!= o_i
.islower():
786 o_i
= toggle_case(o_i
)
791 if s_i
.lower() == o_i
.lower():
792 self
[i
] = toggle_case(o_i
)
793 elif allow_alternatives
:
794 self
[i
] = alternatives(s_i
, o_i
) # '[%s/%s]' % (s_i, o_i)
797 if merge_comments
and not conflict
and self
.comment
!= other
.comment
:
798 if allow_alternatives
and self
.comment
and other
.comment
:
799 self
.comment
+= ' / ' + other
.comment
801 self
.comment
= self
.comment
or other
.comment
804 raise AssertionError('Merge Error:\n %s\n %s'
805 % (str(self
), str(other
)))
815 # Teste Felder auf Konsistenz mit den Regeländerungen der Orthographiereform
816 # 1996, ändere Unstimmigkeiten `in place`.
818 # Die neuere Funktion `ableitung1901()`_, deckt mehr Ausnahmen ab aber
819 # funktioniert nur in eine Richtung.
821 # Falls ein generisches Feld von Änderung betroffen ist, schreibe die
822 # korrekten Trennungen in die spezifischen Felder.
824 # >>> entry = WordEntry('Würste;Wür-ste')
825 # >>> entry.regelaenderungen()
827 # Würste;-2-;Wür-ste;Würs-te
828 # >>> entry = WordEntry('Würste;Würs-te')
829 # >>> entry.regelaenderungen()
831 # Würste;-2-;Wür-ste;Würs-te
832 # >>> entry = WordEntry('Hecke;He-cke')
833 # >>> entry.regelaenderungen()
835 # Hecke;-2-;He{ck/k-k}e;He-cke
836 # >>> entry = WordEntry('Ligusterhecke;Ligu-ster=he{ck/k-k}e')
837 # >>> entry.regelaenderungen()
839 # Ligusterhecke;-2-;Ligu-ster=he{ck/k-k}e;Ligus-ter=he-cke
841 # Bei Änderungen der Schreibung wird das entsprechende Feld ausgekreuzt:
843 # >>> entry = WordEntry('Hass;Hass')
844 # >>> entry.regelaenderungen()
846 # Hass;-2-;-3-;Hass;Hass
847 # >>> entry = WordEntry('fasst;fasst')
848 # >>> entry.regelaenderungen()
850 # fasst;-2-;-3-;fasst;fasst
851 # >>> entry = WordEntry('Missbrauch;Miss<brauch')
852 # >>> entry.regelaenderungen()
854 # Missbrauch;-2-;-3-;Miss<brauch;Miss<brauch
855 # >>> entry = WordEntry('schlifffest;schliff=fest')
856 # >>> entry.regelaenderungen()
858 # schlifffest;-2-;-3-;schliff=fest
860 # Bei gleicher, nicht betroffener Trennung werden Felder zusammengefasst:
862 # >>> entry = WordEntry('austoben;-2-;aus<to-ben;aus<to-ben')
863 # >>> entry.regelaenderungen()
865 # austoben;aus<to-ben
867 # Achtung: nicht narrensicher -- Ausnahmen und Mehrdeutigkeiten werden nicht
870 # >>> entry = WordEntry('Boss;Boss # engl.') # korrekt
871 # >>> entry.regelaenderungen()
875 # >>> entry = WordEntry('Ästhesie;Äs-the-sie') # Trennung von "sth" erlaubt
876 # >>> entry.regelaenderungen()
878 # Ästhesie;Äs-the-sie
880 # >>> entry = WordEntry('Fluß;Fluß') # in de-1996 nur Fluss
881 # >>> entry.regelaenderungen()
887 def regelaenderungen(self
):
888 wort01
= self
.get('de-1901')
889 wort96
= self
.get('de-1996')
892 if wort01
is None or wort96
is None or 'engl.' in self
.comment
:
895 # Trennregeländerungen:
898 wort01
= wort01
.replace('-ck', '{ck/k-k}')
899 wort96
= wort96
.replace('{ck/k-k}', '-ck')
902 wort01
= re
.sub('(?<!s)s-t(?!h)', '-st', wort01
)
903 wort96
= wort96
.replace('-st', 's-t')
905 # kein Schluss-ss und sst in de-1901 (ungetrenntes "ss" nur in Ausnahmen)
906 # aber: 'ßt' und Schluß-ß auch in de-1996 möglich (langer Vokal)
911 # Dreikonsonantenregel:
912 if wort01
and re
.search(r
'(.)\1=\1', wort01
):
916 if wort01
== wort96
: # keine Regeländerung im Wort
922 self
.extend( ['']*(4-len(self
)) )
927 self
.extend( ['']*(4-len(self
)) )
932 self
.append(w_versal
)
938 # Klasse für Einträge (Zeilen) der Wortlisten im `5-Felder-Format`_ (Kurzform).
942 class ShortEntry(WordEntry
):
943 """Entry of the German hyphenation database file (5 fields)."""
950 # Ein vollständiger Eintrag enthält fünf, durch Semikolon getrennte, Felder
951 # für die Sprachvarianten `de`, `de-1901`, `de-CH`, `de-1901-x-versal` und
952 # `de-CH-1901` (Tags nach [BCP47]_).
954 # Jedes Feld enthält ein Wort mit Kennzeichnung der Trennstellen im Format
955 # der `Wortliste der deutschsprachigen Trennmustermannschaft`_, z.B.
956 # ``Pro<zent=zah-len``.
958 # Felder können weggelassen werden, wenn sich der Inhalt aus einem
959 # allgemeineren Feld gewinnen lässt (siehe `Ersatzregeln`_).
965 # Die Felder beschreiben die Schreibung und Trennung eines Wortes gemäß
966 # der Sprachvarianten:
968 # 1. `de`: `aktuelle Rechtschreibung`_ wie sie in Deutschland und Österreich
971 # Beispiele: ``Diens-te``, ``ba-cken``, ``Grü-ße``, ``Schluss=satz``
973 # 2. `de-1901`: `traditionelle Rechtschreibung`_ wie sie in Deutschland und
974 # Österreich von 1901 bis 1996 gültig war.
976 # Beispiele: ``Dien-ste``, ``ba{ck/k-k}en``, ``Grü-ße``, ``Schluß=satz``
978 # 3. `de-CH` oder `de-x-versal`: aktuelle Rechtschreibung wie sie in der
979 # Schweiz und bei ß-Ersatzschreibung angewendet wird.
981 # Beispiele: ``ba-cken``, ``Grüs-se``, ``Schluss=satz``
983 # 4. `de-1901-x-versal`: traditionelle Rechtschreibung mit
984 # ß-Ersatzschreibung wie sie in Deutschland und Österreich
985 # angewendet wurde (keine Trennung von „ss“ als Ersatz für „ß“).
987 # Beispiele: ``ba{ck/k-k}en``, ``Grü-sse``, ``Schluss=satz``
989 # 5. `de-CH-1901`: traditionelle Rechtschreibung wie sie in der Schweiz
990 # angewendet wurde (Trennung von „ss“ auch wenn es für „ß“ steht aber
991 # keine Dreikonsonantenregel für „ss=s“).
993 # Beispiele: ``ba{ck/k-k}en``, ``Grüs-se``, ``Schluss=satz``
999 # Feld 1 (`de`) ist ein Pflichtfeld, die anderen Felder können weggelassen
1000 # werden, wenn sich der Inhalt über regelmäßige Transformationen_ aus einem
1001 # anderen Feld gewinnen lässt:
1003 # ==================== ============= =======================
1004 # Feld Quelle Transformation
1005 # ==================== ============= =======================
1007 # 2 de-1901 1 de „Rechtschreibreversion_“
1008 # 3 de-CH 1 de SZ-Ersatz_
1009 # 4 de-1901-x-versal 2 de-1901 SZ-Ersatz_
1010 # 5 de-CH-1901 2 de-1901 SZ-Ersatz_
1011 # ==================== ============= =======================
1013 # Die Ersetzung erfolgt rekursiv (d.h. wenn Feld 2 nicht gegeben ist, können
1014 # die Felder 4 und 5 aus Feld 1 mit „Rechtschreibreversion_“ und SZ-Ersatz_
1017 # Wenn ein Eintrag in einer Sprachvariante leer bleiben soll, muss das
1018 # zugehörige Feld entsprechend markiert („ausgekreuzt“) werden.
1019 # (Da die ß-Ersatzschreibung für alle Wörter definiert ist, ist in einer
1020 # vollständigen Liste das Auskreuzen der Spalten 3…5 nicht erforderlich.)
1022 # Beispiele: ``-1-;de<pla-ziert``, ``auf<wän-dig;-2-;``.
1025 # Implementiert in `ShortEntry.getitem()`_.
1028 # Rechtschreibreversion
1029 # """""""""""""""""""""
1031 # Überführung eines Wortes in die Sprachvariante de-1901 durch
1032 # Anwendung der mit der Rechtschreibreform 1996 entfallenen Regeln:
1035 # Trenne nie "st". (Aber "s-th" ist trennbar.)
1037 # Trenne "ck" als "k-k".
1039 # "ß", "ßt" und "ßl" statt "ss", "sst" und "ssl" am Silbenende.
1040 # Dreikonsonantenregel_:
1041 # Von drei gleichen Konsonanten vor einem Selbstlaut entfällt einer,
1042 # taucht aber bei Worttrennung wieder auf.
1044 # Abweichende Änderungen müssen als Ausnahmen explizit erfasst werden.
1046 # Implementiert in `ableitung1901()`_.
1050 # Ersetze 'ß' mit 'ss', trenne je nach Sprachvarietät (siehe Feldbelegung_).
1052 # Implementiert in `versalschreibung()`_.
1059 # Durch die Ersatzregeln reicht für die meisten Einträge die Angabe des ersten
1062 # >>> from wortliste import ShortEntry
1063 # >>> entry = ShortEntry('Pass=stra-ße')
1065 # Eintrag vervollständigen mit `ShortEntry.complete()`_:
1067 # >>> entry.complete(); print(entry)
1068 # Pass=stra-ße;Paß=stra-ße;Pass=stras-se;Pass=stra-sse;Pass=stras-se
1070 # Eintrag kürzen mit `ShortEntry.prune()`_:
1072 # >>> entry.prune(); print(entry)
1075 # Wort mit Unregelmäßigkeiten:
1077 # >>> entry = ShortEntry('Boss;Boss;Boss;Boss;Boss # en.')
1078 # >>> entry.prune(); print(entry)
1088 # Tupel der Sprachbezeichner für die Felder im 5-Felder-Format_::
1090 feldnamen
= ("de", "de-1901", "de-CH", "de-1901-x-versal", "de-CH-1901")
1095 # Zuordnung von Sprachbezeichnern nach [BCP47]_ zu den Feldern.
1097 # In Python beginnt die Indexzählung mit Null::
1100 "de": 0, # Deutsch, aktuell
1103 "de-1996": 0, # Alias
1104 "de-1996": 0, # Alias
1105 "de-DE-1996": 0, # Alias
1106 "de-AT-1996": 0, # Alias
1107 "de-1901": 1, # traditionell (Reform 1901)
1108 "de-CH": 2, # ohne ß (Schweiz/versal) aktuell
1109 "de-x-versal": 2, # Alias
1110 "de-1996-x-versal": 2, # Alias
1111 "de-1901-x-versal": 3, # ohne ß (versal) traditionell
1112 "de-CH-1901": 4, # ohne ß (Schweiz) traditionell
1115 # Achtung: die Sprachtags `de` und `de-CH` sind in der Kurzform_ ein Alias für
1116 # die aktuelle Rechtschreibung aber in der Langform_ Bezeichner für separate
1117 # Felder mit allgemeingültigen Trennungen:
1119 # >>> print(ShortEntry.feldindizes['de-1901'], ShortEntry.feldindizes['de-CH'])
1121 # >>> print(ShortEntry.feldindizes['de'], ShortEntry.feldindizes['de-1996'])
1124 # >>> print(WordEntry.feldindizes['de'], WordEntry.feldindizes['de-1996'])
1137 # Das Argument `line` ist eine Zeichenkette (`string`, `unicode`) im
1138 # `5-Felder-Format`_ oder eine Instanz der Klasse WordEntry_.
1140 # >>> print(ShortEntry('Diens-te'))
1142 # >>> tpp = ShortEntry('Ur<laubs=tipp;-2-')
1146 # >>> print(abenddienste)
1147 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
1148 # >>> print(ShortEntry(abenddienste))
1151 # Achtung: Ein Lang-Eintrag mit nur zwei Feldern kann unregelmäßig sein und
1152 # daher im Kurzformat mehrere Felder benötigen (siehe Tests)
1155 def __init__(self
, line
, delimiter
=';', prune
=True):
1157 if isinstance(line
, WordEntry
):
1158 self
.comment
= line
.comment
# Kommentar
1159 self
._key
= line
.getitem(0) # Schlüssel cachen
1162 self
.append(line
.getitem(3)) # Deutsch, aktuell (Reform 1996)
1163 self
.append(line
.getitem(2)) # "traditionell" (Reform 1901)
1165 self
.append(line
.getitem(6)) # ohne ß (Schweiz oder versal) "aktuell"
1166 self
.append(line
.getitem(5)) # ohne ß (Schweiz oder versal) "traditionell"
1167 self
.append(line
.getitem(7)) # ohne ß (Schweiz) "traditionell" ("süssauer")
1168 elif 'ß' in self
._key
: # auskreuzen
1173 WordEntry
.__init
__(self
, line
, delimiter
)
1175 if prune
: # Felder zusammenfassen
1180 # >>> drs = str(ShortEntry('-1-;Dreß'))
1183 # >>> print(ShortEntry('# Testkommentar'))
1186 # In der Voreinstellung werden optionale Felder mit `prune()` gekürzt.
1187 # Mit der Option `prune=False` bleiben alle übergebenen Felder erhalten:
1189 # >>> print(ShortEntry('Fluss;Fluß'))
1191 # >>> print(ShortEntry('Fluss;Fluß', prune=False))
1194 # Wird dem Konstruktor eine WordEntry_-Instanz übergeben, so wird diese in das
1195 # Kurzformat überführt:
1197 # >>> print(ShortEntry(WordEntry('heute;heu-te')))
1200 # Bei Ausnahmen von der regelmäßigen Worttrennung werden bei Bedarf
1201 # zusätzliche Spalten belegt:
1203 # >>> print(ShortEntry(WordEntry('Amnesty;Am-nes-ty # en.')))
1204 # Am-nes-ty;Am-nes-ty # en.
1205 # >>> bss = ShortEntry(WordEntry('Boss;Boss # engl.'))
1209 # Bei Wörtern mit unterschiedlicher Schreibung in den verschiedenen
1210 # Sprachvarianten werden Felder, die im übergebenen `WordEntry` nicht belegt
1211 # sind als leer markiert:
1213 # >>> print(urlaubstipp)
1214 # Urlaubstipp;-2-;-3-;Ur<laubs=tipp
1215 # >>> print(ShortEntry(urlaubstipp))
1217 # >>> print(ShortEntry(WordEntry('Abfalllager;-2-;-3-;Ab<fall=la-ger')))
1218 # Ab<fall=la-ger;-2-
1220 # >>> dresz = WordEntry('Dreß;-2-;Dreß;-4-')
1221 # >>> print(ShortEntry(dresz))
1222 # -1-;Dreß;-3-;-4-;-5-
1223 # >>> g_ebene = WordEntry('Gaußebene;Gauß=ebe-ne')
1224 # >>> print(ShortEntry(g_ebene))
1225 # Gauß=ebe-ne;Gauß=ebe-ne;-3-;-4-;-5-
1226 # >>> print(auffrass)
1227 # auffrass;-2-;-3-;-4-;auf-frass
1228 # >>> print(ShortEntry(auffrass))
1229 # -1-;-2-;auf-frass;auf-frass;auf-frass
1230 # >>> fraesse = WordEntry('frässe;-2-;-3-;-4-;-5-;frä-sse;fräs-se;fräs-se')
1231 # >>> frs = ShortEntry(fraesse)
1233 # -1-;-2-;fräs-se;frä-sse;fräs-se
1234 # >>> loesz = WordEntry('Lößboden;Löß=bo-den')
1235 # >>> print(ShortEntry(loesz))
1236 # Löß=bo-den;Löß=bo-den;-3-;-4-;-5-
1237 # >>> loess = WordEntry('Lössboden;-2-;-3-;Löss=bo-den;Löss=bo-den')
1238 # >>> print(ShortEntry(loess))
1239 # Löss=bo-den;-2-;Löss=bo-den;Löss=bo-den;Löss=bo-den
1241 # >>> print(ShortEntry(WordEntry('Fussballliga;-2-;-3-;-4-;-5-;-6-;Fuss=ball==li-ga')))
1242 # -1-;-2-;Fuss=ball==li-ga
1243 # >>> print(ShortEntry(WordEntry('Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga;-7-')))
1244 # -1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga
1245 # >>> messignal = WordEntry('Messignal;-2-;-3-;-4-;-5-;-6-;-7-;Me{ss/ss=s}i-.gnal')
1246 # >>> print(ShortEntry(messignal))
1247 # -1-;-2-;-3-;-4-;Me{ss/ss=s}i-.gnal
1249 # >>> ShortEntry(WordEntry('# nur Kommentar'))
1250 # ShortEntry('# nur Kommentar')
1256 # Gib einen Schlüssel (ungetrenntes Wort) zurück.
1258 # Bei Einträgen im Kurzformat ist der Schlüssel nicht eindeutig: durch
1259 # Transformationen_ kann ein Wort in verschiedenen Schreibungen vorkommen.
1261 # Standardmäßig wird das erste nichtleere Feld ohne Trennzeichen verwendet:
1263 # >>> print(ShortEntry('Fluss;Fluß').key())
1265 # >>> print(ShortEntry('-1-;Dreß').key())
1268 # Die Auswahl kann über das `lang` Argument gesteuert werden:
1270 # >>> print(ShortEntry('-1-;Dreß').key('de-CH-1901'))
1273 # Der Schlüssel eines leeren Eintrags ist ein leerer String, der eines leeren
1274 # Kommenars das Kommentarzeichen:
1276 # >>> ShortEntry('').key(), ShortEntry('# toller Kommentar').key()
1281 def key(self
, lang
=None):
1282 """Erstelle einen Schlüssel (ungetrenntes Wort)."""
1284 return join_word(self
.get(lang
))
1292 return '#' # reiner Kommentar
1293 return '' # leerer Eintrag
1296 # .. _`ShortEntry.getitem()`:
1301 # Gib Feld `i` zurück. Wende bei Bedarf die Ersatzregeln_ an.
1303 # >>> entry = ShortEntry('Bu-ße')
1304 # >>> print(entry.getitem(1), entry.getitem(2), entry.getitem(4))
1305 # Bu-ße Bus-se Bus-se
1307 # >>> entry.getitem(5)
1308 # Traceback (most recent call last):
1310 # IndexError: list index out of range
1312 # Mit `subsititute=True` wird der Feldinhalt ignoriert und immer nach
1313 # Ersatzregel bestimmt:
1315 # >>> entry = ShortEntry('Bu-ße;Reue;Bu-sse')
1316 # >>> print(entry.getitem(1), entry.getitem(1, substitute=True))
1322 def getitem(self
, i
, substitute
=False):
1323 """Return item ``i`` or a subsititute"""
1327 except IndexError: # Feld i nicht vorhanden
1329 raise ValueError('leerer Eintrag')
1331 raise # maximal 5 Felder im Kurzformat
1333 # Rekursion: wähle das generischere Feld
1334 ersatzfelder
= (None, 0, 0, 1, 1)
1335 word
= self
.getitem(ersatzfelder
[i
])
1337 # Bei leerem Feld ("ausgekreuzt") ist keine Transformation nötig
1341 # Rechschreibreform (s-t, ck, Dreikonsonantenregel)
1342 if i
== 1: # de-1901
1343 return ableitung1901(word
)
1345 # Versalschreibung: ß -> ss
1347 word
= versalschreibung(word
, self
.feldnamen
[i
])
1354 # Gib Trennmuster für Sprachvariante zurück (ggf. über Transformationen_).
1358 # ohne Transformation
1360 # >>> aalbst = ShortEntry('Aal=be<stand # Test')
1361 # >>> aalbst.get('de')
1363 # >>> aalbst.get('de-1996')
1365 # >>> aalbst.get('de-1901')
1367 # >>> aalbst.get('de-x-versal')
1369 # >>> aalbst.get('de-1901-x-versal')
1371 # >>> aalbst.get('de-1996-x-versal')
1373 # >>> aalbst.get('de-CH-1901')
1378 # >>> dst = ShortEntry('Diens-te')
1379 # >>> print(dst.get('de'), dst.get('de-1901'), dst.get('de-1996'))
1380 # Diens-te Dien-ste Diens-te
1381 # >>> print(ShortEntry('Es-te').get('de-1901'))
1383 # >>> sck = ShortEntry('Stre-cke')
1384 # >>> print(sck.get('de'), sck.get('de-1901'))
1385 # Stre-cke Stre{ck/k-k}e
1389 # >>> abus = ShortEntry('ab<bü-ßen')
1390 # >>> print(abus.get('de'), abus.get('de-CH'))
1391 # ab<bü-ßen ab<büs-sen
1392 # >>> print(abus.get('de-1901-x-versal'), abus.get('de-CH-1901'))
1393 # ab<bü-ssen ab<büs-sen
1395 # >>> print(ShortEntry('passt').get('de-1901'))
1397 # >>> print(ShortEntry('passt').get('de-1901-x-versal'))
1399 # >>> rs = ShortEntry('-1-;-2-;Russ') # versal für Ruß
1400 # >>> print(rs.get('de-x-versal'))
1402 # >>> rs.get('de-1901-x-versal')
1405 # >>> entry = ShortEntry('süß=sau-er')
1406 # >>> print(entry.get('de-CH'), entry.get('de-1901-x-versal'), entry.get('de-CH-1901'))
1407 # süss=sau-er süss=sau-er süss=sau-er
1409 # >>> ShortEntry('Pro<zess=en-.de').get('de-CH-1901')
1411 # >>> pstr = ShortEntry('Pass=stra-ße')
1412 # >>> print(pstr.get('de-1996'), pstr.get('de-1901'))
1413 # Pass=stra-ße Paß=stra-ße
1414 # >>> print(pstr.get('de-x-versal'), pstr.get('de-1901-x-versal'))
1415 # Pass=stras-se Pass=stra-sse
1417 # Test: Wenn keine Ersatzschreibung vorliegt, wird auch in traditioneller
1418 # Versalschreibung s-s getrennt:
1420 # >>> print(ShortEntry('Bu-ße').get('de-1901-x-versal'))
1422 # >>> print(ShortEntry('Bus-se').get('de-1901-x-versal'))
1425 # Geerbt von `WordEntry.get()`_.
1427 # .. _ShortEntry.complete():
1432 # Eintrag vervollständigen (alle 5 Felder ausfüllen).
1434 # >>> dst.complete()
1436 # Diens-te;Dien-ste;Diens-te;Dien-ste;Dien-ste
1437 # >>> aalbst.complete()
1439 # Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand # Test
1440 # >>> abus.complete()
1442 # ab<bü-ßen;ab<bü-ßen;ab<büs-sen;ab<bü-ssen;ab<büs-sen
1443 # >>> bss.complete()
1445 # Boss;Boss;Boss;Boss;Boss # engl.
1446 # >>> frs.complete()
1448 # -1-;-2-;fräs-se;frä-sse;fräs-se
1449 # >>> tpp.complete()
1451 # Ur<laubs=tipp;-2-;Ur<laubs=tipp;-4-;-5-
1453 # >>> entry = ShortEntry('# toller Hecht')
1454 # >>> entry.complete()
1461 for i
in range(len(self
), 5):
1463 field
= self
.getitem(i
)
1464 except ValueError: # leerer Eintrag
1472 # Gib eine vervollständigte Kopie des Eintrags zurück.
1474 # >>> entry = ShortEntry('-1-;-2-;sties-se').completed()
1476 # ShortEntry('-1-;-2-;sties-se;-4-;-5-')
1477 # >>> [field for field in entry]
1478 # ['', '', 'sties-se', '', '']
1479 # >>> print(pstr.completed())
1480 # Pass=stra-ße;Paß=stra-ße;Pass=stras-se;Pass=stra-sse;Pass=stras-se
1481 # >>> print(ShortEntry('Miss<er<.folg').completed())
1482 # Miss<er<.folg;Miß<er<.folg;Miss<er<.folg;Miss<er<.folg;Miss<er<.folg
1484 # Geerbt von `WordEntry.completed()`_.
1492 # Felder weglassen, wenn sich der Inhalt durch Ersatzregeln_ gewinnen läßt:
1494 # >>> aalbst.prune()
1496 # Aal=be<stand # Test
1510 # Auch das „Auskreuzen“ wird weitergereicht:
1512 # Wenn ein Wort in "traditioneller" Rechtschreibung nicht existiert, reicht
1513 # das "Auskreuzen" von Feld 2:
1515 # >>> entry = ShortEntry('Tipp;-2-;Tipp;-4-;-5-')
1520 # Wenn ein Wort in aktueller Rechtschreibung nicht existiert, reicht das
1521 # "Auskreuzen" von Feld 1:
1523 # >>> entry = ShortEntry('-1-;Rauh=nacht;-3-;Rauh=nacht')
1528 # Felder werden nicht gekürzt, wenn die Rekonstruktion einen anderen Wert
1533 # -1-;-2-;fräs-se;frä-sse;fräs-se
1535 # >>> entry = ShortEntry('-1-;Dreß;-3-;-4-;-5-')
1538 # -1-;Dreß;-3-;-4-;-5-
1540 # Mit ``drop_sz=True`` werden die drei letzten Felder (ß-Ersatzschreibung)
1543 # * ß-Ersatzschreibung ist immer definiert, daher kann eigentlich nichts falsch
1546 # * Die "nachlässige" Wandlung garantiert nicht die exakte Reproduktion der
1547 # Ausgangsliste nach einem "Rundtrip":
1549 # Mit `ShortEntry.complete()`_ werden die letzten Spalten ausgefüllt, auch
1550 # wenn sie im Original "ausgekreuzt" waren.
1552 # Bei Wandlung ins Langformat können zusätzliche Einträge mit
1553 # ß-Ersatzschreibung entstehen.
1555 # Die Auszeichnung von Mehrdeutigkeiten (z.B. „Mas-se/Ma-sse“ in
1556 # de-1901-x-versal) geht verloren.
1558 # >>> entry.prune(drop_sz=True)
1561 # >>> entry.complete()
1563 # -1-;Dreß;-3-;Dress;Dress
1567 def prune(self
, drop_sz
=False):
1568 if len(self
) == 1: # bereits kompakt
1571 while len(self
) > 2:
1573 for i
in range(len(self
)-1, 0, -1):
1575 rekonstruktion
= self
.getitem(i
)
1576 if wort
!= rekonstruktion
:
1577 # print(tag, repr(self), wort, rekonstruktion)
1580 if len(self
) == 1 and not self
[0]:
1587 # Einträge zusammenlegen.
1589 # Das als Argument übergebene WordEntry Objekt wird in dem eigenen Eintrag
1592 # Leere („ausgekreuzte“) Felder werden überschrieben:
1594 # >>> entry = ShortEntry('be<wusst;-2-')
1595 # >>> entry.merge(ShortEntry('-1-;be<wußt'))
1599 # Identische Felder bleiben erhalten. Alle Felder die über Transformationen_
1600 # rekonstruiert werden können werden entfernt:
1602 # >>> entry = ShortEntry('Maß=nah-me # < nehmen')
1603 # >>> entry.merge(ShortEntry('-1-;-2-;Mass=nah-me'))
1605 # Maß=nah-me # < nehmen
1606 # >>> entry = ShortEntry('-1-;-2-;Mass=nah-me')
1607 # >>> entry.merge(ShortEntry('Maß=nah-me # < nehmen'), merge_comments=True)
1609 # Maß=nah-me # < nehmen
1611 # Es sei den die Option ``prune`` ist ``False``:
1613 # >>> entry.merge(ShortEntry('Maß=nah-me'), prune=False)
1615 # Maß=nah-me;Maß=nah-me;Mass=nah-me;Mass=nah-me;Mass=nah-me # < nehmen
1617 # Bei Konflikten wird ein AssertionError erzeugt:
1619 # >>> entry = ShortEntry('be<wusst;-2-')
1620 # >>> entry.merge(ShortEntry('-1-;-2-;ver<bor-gen'))
1621 # Traceback (most recent call last):
1623 # AssertionError: Merge Error:
1624 # be<wusst;-2-;be<wusst;-4-;-5-
1625 # -1-;-2-;ver<bor-gen;-4-;-5-
1627 # Geerbt von `WordEntry.merge()`_
1633 # Gib eine Liste von WordEntry_ Instanzen (8-Spalten-Format_) zurück.
1635 # >>> mse = ShortEntry('Mas-se')
1636 # >>> mse.wordentries()
1637 # [WordEntry('Masse;Mas-se')]
1639 # Im Langformat gibt es für jede Schreibung einen separaten Eintrag:
1641 # >>> mas = ShortEntry('Ma-ße')
1642 # >>> for e in mas.wordentries():
1645 # Masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se
1647 # >>> entry = ShortEntry('Löss')
1648 # >>> for e in entry.wordentries():
1650 # Löss;-2-;-3-;Löss;Löss
1655 def wordentries(self
, prune
=True):
1658 if self
.comment
: # leerer Kommentar
1659 return [WordEntry('# '+self
.comment
)]
1660 return [WordEntry('')]
1662 entries
= [] # liste für WordEntry Einträge (Rückgabeobjekt)
1663 words
= {} # dictionary für WordEntry Einträge
1664 # Zuordnung der Indizes: ShortEntry[s] == WordEntry[l]
1665 indices
= (3, 2, 6, 5, 7)
1667 for s
,l
in enumerate(indices
):
1668 word
= self
.getitem(s
)
1670 continue # Leerfelder überspringen
1672 key
= join_word(word
)
1673 # WordEntry Instanz heraussuchen oder erzeugen:
1677 entry
= WordEntry(key
)
1678 entry
.comment
= self
.comment
1680 entries
.append(entry
)
1681 # Eintrag in entry[j]:
1682 entry
.setitem(l
, word
)
1684 # Auffüllen und Komprimieren
1685 for entry
in entries
:
1686 while len(entry
) < 8:
1693 # Hilfsfunktion für Tests:
1695 # >>> def print_langform(line, prune=True):
1696 # ... for e in ShortEntry(line).wordentries(prune):
1701 # >>> print_langform('ba-den')
1703 # >>> print_langform('Wes-te')
1704 # Weste;-2-;We-ste;Wes-te
1705 # >>> print_langform('Toll-patsch;-2-')
1706 # Tollpatsch;-2-;-3-;Toll-patsch
1707 # >>> print_langform('-1-;rau-he')
1708 # rauhe;-2-;rau-he;-4-
1710 # Unterschiedliche Schreibungen:
1712 # >>> print_langform('Ab<fall=la-ger # Rechtschreibänderung')
1713 # Abfalllager;-2-;-3-;Ab<fall=la-ger # Rechtschreibänderung
1714 # Abfallager;-2-;Ab<fa{ll/ll=l}a-.ger;-4- # Rechtschreibänderung
1716 # >>> print_langform('Ab<guss')
1717 # Abguss;-2-;-3-;Ab<guss;Ab<guss
1718 # Abguß;-2-;Ab<guß;-4-
1720 # >>> print_langform('groß')
1722 # gross;-2-;-3-;-4-;gross
1724 # >>> print_langform('gro-ßen')
1726 # grossen;-2-;-3-;-4-;-5-;gro-ssen;gros-sen;gros-sen
1728 # >>> print_langform('Spaß')
1730 # Spass;-2-;-3-;-4-;Spass
1731 # >>> print_langform('Spass')
1732 # Spass;-2-;-3-;Spass;Spass
1734 # >>> print_langform('spa-ßen')
1736 # spassen;-2-;-3-;-4-;-5-;spa-ssen;spas-sen;spas-sen
1738 # >>> print_langform('ab<bü-ßen')
1740 # abbüssen;-2-;-3-;-4-;-5-;ab<bü-ssen;ab<büs-sen;ab<büs-sen
1742 # >>> print_langform('Biss')
1743 # Biss;-2-;-3-;Biss;Biss
1745 # >>> print_langform('Boss;Boss # engl.')
1748 # >>> print_langform('Pass=sys-tem')
1749 # Passsystem;-2-;-3-;Pass=sys-tem;-5-;Pass=sy-stem;Pass=sys-tem
1750 # Paßsystem;-2-;Paß=sy-stem;-4-
1752 # >>> print_langform('Press=saft')
1753 # Presssaft;-2-;-3-;Press=saft;Press=saft
1754 # Preßsaft;-2-;Preß=saft;-4-
1756 # >>> print_langform('Pro<gramm==maß=nah-me')
1757 # Programmmaßnahme;-2-;-3-;Pro<gramm==maß=nah-me
1758 # Programmaßnahme;-2-;Pro<gra{mm/mm==m}aß=nah-me;-4-
1759 # Programmmassnahme;-2-;-3-;-4-;-5-;-6-;Pro<gramm==mass=nah-me
1760 # Programmassnahme;-2-;-3-;-4-;-5-;Pro<gra{mm/mm==m}ass=nah-me;-7-
1762 # >>> print_langform('Pass=stra-ße')
1763 # Passstraße;-2-;-3-;Pass=stra-ße
1764 # Paßstraße;-2-;Paß=stra-ße;-4-
1765 # Passstrasse;-2-;-3-;-4-;-5-;Pass=stra-sse;Pass=stras-se;Pass=stras-se
1767 # >>> print_langform('Pro<zess=en-.de;Pro<zeß=en-de;Pro<zess=en-.de')
1768 # Prozessende;-2-;-3-;Pro<zess=en-.de;Pro<zess=en-.de
1769 # Prozeßende;-2-;Pro<zeß=en-de;-4-
1771 # Explizit nur eine Schreibung:
1773 # >>> print_langform('Abend=dress;Abend=dress')
1774 # Abenddress;Abend=dress
1775 # >>> print_langform('-1-;Abend=dreß')
1776 # Abenddreß;-2-;Abend=dreß;-4-
1777 # Abenddress;-2-;-3-;-4-;-5-;Abend=dress;-7-
1779 # Nur Versalschreibung:
1781 # >>> print_langform('-1-;-2-;Fuss;Fuss;Fuss')
1782 # Fuss;-2-;-3-;-4-;Fuss
1783 # >>> line = 'Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga'
1784 # >>> print(ShortEntry(WordEntry(line), prune=False))
1785 # -1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga
1786 # >>> print_langform('-1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga')
1787 # Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga;-7-
1788 # >>> print_langform('-1-;-2-;Fuss=ball==li-ga')
1789 # Fussballliga;-2-;-3-;-4-;-5-;-6-;Fuss=ball==li-ga
1793 # >>> print_langform('# holla')
1803 # Ableitung von de-1901 aus de-1996 (Reversion der Reform 1996).
1805 # Mit keep_key == True werden nur Änderungen vorgenommen, die die
1806 # Schreibung des (ungetrennten) Wortes nicht verändern.
1808 # >>> from wortliste import ableitung1901
1812 def ableitung1901(wort
, keep_key
=False):
1813 """Reverse regular changes of the 1996 orthography reform."""
1816 # Trennregeländerungen
1817 # ~~~~~~~~~~~~~~~~~~~~
1819 # Diese Regeln ändern nicht das Wort, nur die Trennmöglichkeiten.
1821 # Alternativtrennungen
1822 # """"""""""""""""""""
1823 # (siehe Trennstile.txt)::
1825 # wort = fremdwortsilben(wort) # Wahltrennungen wie no-b-le
1826 wort
= morphemisch(wort
) # Wahltrennungen wie He-li-ko<p-ter
1831 # K75: Trenne nie st.
1833 # Ersetze 's-t' mit '-st':
1835 # >>> ableitung1901('Diens-te')
1837 # >>> print(ableitung1901('wuss-te', keep_key=True))
1840 # Aber Trennung von s-theta erlaubt (nach K74):
1842 # >>> print(ableitung1901('Äs-thet'))
1847 wort
= re
.sub('(?<!s)s-t(?!h)', '-st', wort
)
1849 # Keine Trennung nach nur einem Buchstaben am Wortanfang:
1851 # >>> print(ableitung1901('Es-te'))
1853 # >>> print(ableitung1901('Nord=os-ten'))
1855 # >>> print(ableitung1901('Po-ly<es-ter'))
1858 # Im Wort sind Einvokalsilben erlaubt:
1860 # >>> print(ableitung1901('the>is-tisch'))
1862 # >>> print(ableitung1901('neu-es-te'))
1867 wort
= re
.sub('((?<=[=<].)|(?<=^.))-', r
'·', wort
)
1873 # K76: Trenne 'ck' als 'k-k'.
1875 # Ersetze '-ck' mit '{ck/k-k}':
1877 # >>> ableitung1901('Ha-cke')
1879 # >>> ableitung1901('A·cker')
1881 # >>> ableitung1901('Got-tes=a·cker')
1882 # 'Got-tes=a{ck/k-k}er'
1883 # >>> ableitung1901('Aal=beck')
1885 # >>> ableitung1901('be<spick-te')
1890 wort
= re
.sub('[-·]ck', '{ck/k-k}', wort
)
1896 # Rechtschreibänderungen
1897 # ~~~~~~~~~~~~~~~~~~~~~~
1899 # Diese Regeln ändern die Schreibung des ungetrennten Worts (und somit den
1900 # Schlüssel im Langformat der Wortliste).
1905 # K38: Kein "ss" und "sst" am Silbenende (ungetrenntes "ss" nur in Ausnahmen)
1906 # (Andererseit ist 'ßt' und Schluss-ß auch in de-1996 möglich (langer Vokal).)
1908 # Ersetze ungetrenntes 'ss' mit 'ß':
1910 # >>> print(ableitung1901('passt'))
1912 # >>> print(ableitung1901('Hass'))
1914 # >>> print(ableitung1901('Fass=brau-se'))
1916 # >>> print(ableitung1901('wuss-te'))
1919 # ß steht für inlautendes ss, wenn ein 'e' ausfällt (und der Ausfall nicht
1920 # durch Apostroph angedeutet wird)
1922 # >>> print(ableitung1901('wäss-rig'))
1924 # >>> print(ableitung1901('an<ge<mess-ner'))
1926 # >>> print(ableitung1901('duss-lig'))
1928 # >>> print(ableitung1901('bissl'))
1931 # Keine Wandlung zu "ß":
1932 # getrenntes Doppel-s (s-s)
1934 # >>> print(ableitung1901('Was-ser'))
1937 # Vokal folgt (Fremdwörter):
1938 # >>> print(ableitung1901('Com-tesse'))
1941 # Großbuchstabe folgt
1943 # >>> print(ableitung1901('WissZeitVG')) # Abkürzung
1946 # Drei oder mehr 's' = Lautmalerei → erhalten:
1948 # >>> print(ableitung1901('pssst'))
1953 wort
= re
.sub('(?<=[^s])ss(?=[^aeiouyäöüA-Zs]|$)', 'ß', wort
)
1955 # Unterdrückung der Trennstelle nach "…ß=er" und "…ß=en" nicht nötig:
1957 # >>> print(ableitung1901('hass=er<.füllt'))
1962 wort
= re
.sub('ß(=+)e([rn][<-]+)\.', r
'ß\1e\2', wort
)
1964 # Dreikonsonantenregel
1965 # """"""""""""""""""""
1967 # K78: Zusammensetzungen, bei denen von drei zusammenstoßenden gleichen
1968 # Konsonanten einer entfällt (K15), schreibt man bei Silbentrennung wieder
1969 # mit allen drei Konsonanten.
1971 # Ersetze 'xx=x' mit '{xx/xx=x}' (für alle Konsonanten vor Selbstlaut)
1973 # >>> print(ableitung1901('Kipp=pflug'))
1975 # >>> print(ableitung1901('Kipp=punkt'))
1977 # >>> print(ableitung1901('Ab<fall=la-ger'))
1978 # Ab<fa{ll/ll=l}a-.ger
1979 # >>> print(ableitung1901('All<lie-be'))
1981 # >>> print(ableitung1901('hell>licht'))
1983 # >>> print(ableitung1901('Pro<gramm==maß=nah-me'))
1984 # Pro<gra{mm/mm==m}aß=nah-me
1988 wort
= re
.sub(r
'([bfglmnprt])\1([<=>]+)\1(?=[aeiouyäöü])',
1989 r
'{\1\1/\1\1\2\1}', wort
)
1991 # Unterdrücken der Trennung nach nur einem Buchstaben::
1993 wort
= re
.sub(r
'(?<=[<=>].}[aeiouyäöü])([-<])\.?', r
'\1.', wort
)
1999 # Ein-Vokal-Silben auch schon 1901 erlaubt:
2001 # >>> print(ableitung1901('ver<knäu-e-le'))
2004 # versalschreibung()
2005 # ------------------
2006 # Ersetze 'ß' mit 'ss', trenne je nach Sprachvarietät `lang`:
2008 # >>> from wortliste import versalschreibung
2009 # >>> print(versalschreibung('paßt'))
2011 # >>> print(versalschreibung('Dar-ßer'))
2013 # >>> print(versalschreibung('bü-ßen', 'de-CH'))
2015 # >>> print(versalschreibung('bü-ßen', 'de-1996-x-versal'))
2017 # >>> print(versalschreibung('bü-ßen', 'de-CH-1901'))
2019 # >>> print(versalschreibung('ä·ßen', 'de-CH-1901'))
2021 # >>> print(versalschreibung('auf<äßen', 'de-CH-1901'))
2023 # >>> print(versalschreibung('auf<eßt', 'de-CH-1901'))
2025 # >>> print(versalschreibung('Groß=se-gel', 'de-1901-x-versal'))
2027 # >>> print(versalschreibung('Groß=se-gel', 'de-CH-1901'))
2029 # >>> print(versalschreibung('Paß=sy-ste-me', 'de-CH-1901'))
2031 # >>> print(versalschreibung('Pro<zeß=en-de', 'de-CH-1901'))
2033 # >>> print(versalschreibung('Pro<zess=en-.de', 'de-CH-1901'))
2035 # >>> print(versalschreibung('Fluß==sy-stem', 'de-CH-1901'))
2037 # >>> print(versalschreibung('Meß==sen-der', 'de-CH-1901'))
2042 def versalschreibung(wort
, lang
='de'):
2047 wort
= wort
.replace('ß', 'ss')
2049 # Trennung von Ersatz-ss in de-CH und de-1996 nach Sprechsilbenregel::
2051 if '1901-x-versal' not in lang
:
2052 wort
= wort
.replace('·ss', 's-s')
2053 # wort = re.sub('(?<=[aeiouyäöü])-\.?ss', 's-s', wort)
2054 wort
= re
.sub('-\.?ss(?=[aeiouyäöü])', 's-s', wort
)
2055 wort
= re
.sub('(?<=^[aeiouyäöü])ss(?=[aeiouyäöü])', 's-s', wort
)
2056 wort
= re
.sub('(?<=[=<][aeiouyäöü])ss(?=[aeiouyäöü])', 's-s', wort
)
2058 # Unterdrückung irreführender Trennung::
2060 wort
= re
.sub('ss(=+)(en|er)([<-])\.?', r
'ss\1\2\3.', wort
)
2062 # Dreikonsonantenregel für Ersatz-ss in de-CH-1901::
2064 if 'CH-1901-x-dreikonsonanten' in lang
:
2065 wort
= re
.sub('ss(=+)s(?=[aeiouyäöü])', r
'{ss/ss\1s}', wort
)
2066 # Unterdrücken der Trennung nach nur einem Buchstaben und irreführender Trennungen
2067 wort
= re
.sub(r
'(?<=[=>]s}[aeiouyäöü])([-<])\.?', r
'\1.', wort
)
2068 # wort = re.sub(r'(?<===s}[aeiouyäöü])([-<])\.?', r'\1.', wort) # Reißverschus=sy-.stem
2069 wort
= re
.sub('(?<=[=>]s})(en|er)([<-])\.?', r
'\1\2.', wort
)
2074 # Kurzformat in Langformat
2075 # ------------------------
2077 # Zusätzlich benötigte Felder werden automatisch erzeugt. Ein Kurzeintrag kann
2078 # mehrere Langeinträge ergeben:
2080 # >>> from wortliste import short2long
2081 # >>> for line in short2long(["Diens-te", "Ge<biss"]):
2083 # Dienste;-2-;Dien-ste;Diens-te
2084 # Gebiss;-2-;-3-;Ge<biss;Ge<biss
2085 # Gebiß;-2-;Ge<biß;-4-
2087 # >>> for line in short2long(["Ge<schoss", "Ge<schoß # österr."]):
2089 # Geschoss;-2-;-3-;Ge<schoss;Ge<schoss
2090 # Geschoß;Ge<schoß # österr.
2096 def short2long(lines
, sort
=True, prune
=True):
2097 """Convert sequence of lines in ShortEntry format to WordEntry instances.
2100 # Sammeln in Liste und Dictionary:
2101 words
= {} # zum Zusammenfassen
2105 shortentry
= ShortEntry(line
)
2106 shortentry
.complete()
2107 for entry
in shortentry
.wordentries(prune
=False):
2108 key
= entry
.key().lower()
2109 try: # Eintrag mit gleichem Schlüssel vorhanden?
2110 altentry
= words
[key
]
2111 except KeyError: # nein -> neuer Eintrag
2113 entries
.append(entry
)
2116 if entry
[3]: # de-1996 non-empty
2117 entry
.merge(altentry
, prune
=False, allow_alternatives
=True)
2118 # Alternativen Eintrag "in-place" ersetzen:
2119 for i
, word
in enumerate(entry
):
2121 altentry
.comment
= entry
.comment
2123 altentry
.merge(entry
, prune
=False, allow_alternatives
=True)
2124 except AssertionError as e
:
2125 sys
.stderr
.write(str(e
)+'\n')
2126 entries
.append(entry
)
2127 except IndexError: # Leerer Eintrag (Kommentar)
2128 entries
.append(entry
)
2132 for entry
in entries
:
2136 entries
.sort(key
=sortkey_duden
)
2142 # Kommentare bleiben erhalten:
2144 # >>> for line in short2long(['Aal=an-geln', '# toller Kommentar'],
2147 # Aalangeln;Aal=an-geln
2148 # # toller Kommentar
2150 # Beim Sortieren werden Kommenare an den Beginn geschrieben.
2151 # >>> for line in short2long(['# erster Kommentar',
2152 # ... 'Aal=an-geln',
2153 # ... '# zweiter Kommentar']):
2155 # # erster Kommentar
2156 # # zweiter Kommentar
2157 # Aalangeln;Aal=an-geln
2159 # >>> for line in short2long(['Fluss=schiff=fahrt']):
2161 # Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-
2162 # Flußschiffahrt;-2-;Fluß=schi{ff/ff=f}ahrt;-4-
2163 # Flussschifffahrt;-2-;-3-;Fluss=schiff=fahrt
2166 # Langformat in Kurzformat
2167 # ------------------------
2169 # Optionale Felder werden weggelassen, wenn sie mit dem automatisch erzeugten
2170 # Inhalt übereinstimmen:
2172 # >>> from wortliste import long2short
2173 # >>> for entry in long2short(["Dienste;-2-;Dien-ste;Diens-te"]):
2177 # Zusammengehörige Einträge werden zusammengefasst:
2179 # >>> for line in long2short(["Großanlass;-2-;-3-;Groß=an<lass",
2180 # ... "Großanlaß;-2-;Groß=an<laß;-4-",
2181 # ... "Grossanlass;-2-;-3-;-4-;Gross=an<lass"]):
2188 # Bei einigen Wörtern ist die ß-ss-Beziehung nicht eindeutig:
2190 # ======= ======== =========
2191 # de1901 de-1996 de-CH
2192 # ======= ======== =========
2195 # Geschoß Geschoß Geschoss
2196 # Geschoß Geschoss Geschoss
2197 # ======= ======== =========
2199 # Daher kann es vorkommen, dass ein Langform-Eintrag Beiträge von
2200 # verschiedenen Kurzformen erhält. So erzeugen, z.B. sowohl
2201 # „Masse“ als auch „Maße“ einen Langeintrag mit Schlüssel „Masse“:
2203 # >>> for entry in short2long(['Mas-se']): print(entry)
2205 # >>> for entry in short2long(['Ma-ße']): print(entry)
2206 # Masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se
2209 # In der Langform werden Alternativen in ein Feld geschrieben:
2211 # >>> for entry in short2long(['Mas-se', 'Ma-ße']):
2213 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[s-/-s]se;Mas-se;Mas-se
2216 # Sind die Alternativen bereits in der Quelle, bleiben sie in der Kurzform
2219 # >>> ml = ['Masse;-2-;Mas-se;Mas-se;-5-;Ma[-s/s-]se;Mas-se;Mas-se',
2221 # >>> for entry in long2short(ml):
2223 # Mas-se;Mas-se;Mas-se;Ma[-s/s-]se
2224 # Ma-ße;Ma-ße;Mas-se;Ma[-s/s-]se
2226 # Zurück in die Langform:
2228 # >>> for entry in short2long(['Mas-se;Mas-se;Mas-se;Ma[-s/s-]se',
2229 # ... 'Ma-ße;Ma-ße;-3-;Ma[-s/s-]se']):
2231 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[-s/s-]se;Mas-se;Mas-se
2238 def long2short(lines
, prune
=True, drop_sz
=False):
2239 """Convert sequence of 8-column lines to ShortEntry instances."""
2240 words
= {} # Einträge mit `de`
2241 words_x
= {} # Einträge ohne `de`
2242 words_merged
= set() # Einträge die vollständig in andere einsortiert wurden
2243 entries
= [] # Rückgabewert: Liste der Kurzeinträge
2245 # Zeilen Einlesen, Wandeln und Sammeln::
2248 longentry
= WordEntry(line
)
2249 entry
= ShortEntry(longentry
, prune
=False)
2250 key
= entry
.key().lower() # Schlüssel ohne Großschreibung
2252 if not entry
: # reiner Kommentar oder leerer Eintrag
2254 entries
.append(entry
)
2257 # Einträge mit leerem ersten Feld werden später einsortiert:
2258 if prune
and not entry
[0]:
2259 # print(key, "in words_x eintragen")
2260 words_x
[key
] = entry
2263 # Eintrag in `dictionary` und Liste:
2265 entries
.append(entry
)
2267 # Straffen (Weglassen von Feldern/Einträgen wenn möglich),
2268 # es sei denn, der Aufruf erfolgte mit `prune=False`::
2273 for entry
in entries
:
2277 while i
< len(entry
):
2278 # for i in range(1, len(entry)):
2279 if not entry
[i
]: # Feld ausgekreuzt
2280 key_i
= join_word(entry
.getitem(i
, substitute
=True)).lower()
2281 # print("Auffüllen", i, key_i, str(entry))
2283 co_entry
= words_x
[key_i
]
2284 entry
.merge(co_entry
, prune
=False, start
=1)
2285 words_merged
.add(key_i
)
2286 # del words_x[key_i]
2287 except (KeyError, AssertionError):
2289 co_entry
= words
[key_i
]
2290 entry
.merge(co_entry
, prune
=False, start
=1)
2291 words_merged
.add(key_i
)
2292 except (KeyError, AssertionError):
2296 # Anhängen aller Einträge mit leerem `de`-Feld, die nicht in
2297 # einen zugehörigen Eintrag einsortiert wurden an die Liste::
2299 for key
, entry
in words_x
.items():
2300 # print(key, str(entry))
2301 if key
in words_merged
:
2304 if len(entry
) > 2 and not entry
[3]: # de-1901-x-versal
2305 key
= join_word(entry
.getitem(3, substitute
=True)).lower()
2307 co_entry
= words_x
[key
]
2308 entry
.merge(co_entry
, prune
=False, start
=3)
2309 words_merged
.add(key
)
2310 except (KeyError, AssertionError):
2312 co_entry
= words
[key
]
2313 entry
.merge(co_entry
, prune
=False, start
=3)
2314 # print(key, str(co_entry))
2315 except (KeyError, AssertionError):
2318 entries
.append(entry
)
2320 for entry
in entries
:
2321 entry
.prune(drop_sz
)
2327 # Separate Einträge ß-Schreibungen zusammenfassen:
2329 # Ein von 2 ß wird zu ss in de-1996:
2331 # >>> for entry in long2short([
2332 # ... 'Passstrasse;-2-;-3-;-4-;-5-;Pass=stra-sse;Pass=stras-se;Pass=stras-se',
2333 # ... 'Passstraße;-2-;-3-;Pass=stra-ße',
2334 # ... 'Paßstraße;-2-;Paß=stra-ße;-4-']):
2340 # >>> for line in long2short([
2341 # ... "Grossserie;-2-;-3-;-4-;-5-;Gross=se-rie;Gross=se-rie",
2342 # ... "Großserie;Groß=se-rie"]):
2346 # kein ß in de-1996:
2348 # >>> for line in long2short([
2349 # ... "Basssaite;-2-;-3-;Bass=sai-te;-5-;Bass=sai-te;Bass=sai-te",
2350 # ... "Baßsaite;-2-;Baß=sai-te;-4-"]):
2354 # ß und Dreikonsonantenregel:
2356 # >>> for line in long2short([
2357 # ... "Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-",
2358 # ... "Flußschiffahrt;-2-;Fluß=schi{ff/ff=f}ahrt;-4-",
2359 # ... "Flussschifffahrt;-2-;-3-;Fluss=schiff=fahrt"]):
2361 # Fluss=schiff=fahrt
2363 # Zusätzliche Variante (Fremdwort vs. Lehnwort) in de-1901:
2365 # >>> for entry in short2long(['Boss;Boss # en.']): print(entry)
2367 # >>> for entry in long2short(['Boss;Boss # en.']): print(entry)
2369 # >>> for entry in long2short(['Boss;Boss # en.', 'Boß;-2-;Boß;-3- # < en.']):
2374 # Alternativschreibung in de-1996 (Geschoß, Löß):
2376 # >>> for entry in long2short(['Geschoss;-2-;-3-;Ge<schoss;Ge<schoss',
2377 # ... 'Geschoß;Ge<schoß # österr. auch de-1996']):
2380 # Ge<schoß # österr. auch de-1996
2382 # Eigennamen auf -ss:
2384 # >>> for entry in long2short(['Vossstrasse;-2-;-3-;-4-;-5-;Voss=stra-sse;Voss=stras-se;Voss=stras-se',
2385 # ... 'Vossstraße;Voss=stra-ße',
2386 # ... 'Voßstraße;Voß=stra-ße']):
2388 # Voss=stra-ße;Voss=stra-ße
2393 # >>> long2short(['# toller Kommentar'], prune=False)
2394 # [ShortEntry('# toller Kommentar')]
2395 # >>> long2short(['# toller Kommentar'], prune=True)
2396 # [ShortEntry('# toller Kommentar')]
2405 # Trennzeichen entfernen::
2407 def join_word(wort
, assert_complete
=False):
2409 # Einfache Trennzeichen:
2411 # === =================================== ==========================
2412 # = Trennstelle an Wortfugen Wort=fu-ge
2413 # < Trennstelle nach Präfix Vor<sil-be
2414 # > Trennstelle vor Suffix Freund>schaf-ten
2415 # \- Nebentrennstelle ge-hen
2416 # . unerwünschte/ungünstige Trennstelle Fett=em<.bo-li.en
2417 # · Randtrennstelle für Notentext O·bo·e
2418 # === =================================== ==========================
2422 wort
= re
.sub('[-=<>.·]', '', wort
)
2424 # Spezielle Trennungen für die traditionelle Rechtschreibung
2425 # (siehe ../../dokumente/README.wortliste)::
2427 if '{' in wort
or '}' in wort
:
2428 wort
= wort
.replace('{ck/kk}', 'ck')
2429 wort
= wort
.replace('{ck/k', 'k')
2430 wort
= wort
.replace('k}', 'k')
2431 # Konsonanthäufungen an Wortfuge: '{xx/xxx}' -> 'xx':
2432 wort
= re
.sub(r
'\{(.)\1/\1\1\1\}', r
'\1\1', wort
)
2433 # schon getrennt: ('{xx/xx' -> 'xx' und 'x}' -> 'x'):
2434 wort
= re
.sub(r
'\{(.)\1/\1\1$', r
'\1\1', wort
)
2435 wort
= re
.sub(r
'^(.)\}', r
'\1', wort
)
2437 # Trennstellen in doppeldeutigen Wörtern::
2439 if '[' in wort
or ']' in wort
:
2440 wort
= re
.sub(r
'\[(.*)/\1\]', r
'\1', wort
)
2442 wort
= re
.sub(r
'\[([^/\[]+)$', r
'\1', wort
)
2443 wort
= re
.sub(r
'^([^/\]]+)\]', r
'\1', wort
)
2445 # Test auf verbliebene komplexe Trennstellen::
2448 for spez
in '[{/}]':
2450 raise AssertionError('Spezialtrennung %s, %s' %
2458 # Zerlege ein Wort mit Trennzeichen in eine Liste von Silben und eine Liste
2461 # >>> from wortliste import zerlege
2463 # >>> zerlege('Haupt=stel-le')
2464 # (['Haupt', 'stel', 'le'], ['=', '-'])
2465 # >>> zerlege('Ge<samt=be<triebs=rats==chef')
2466 # (['Ge', 'samt', 'be', 'triebs', 'rats', 'chef'], ['<', '=', '<', '=', '=='])
2467 # >>> zerlege('an<stands>los')
2468 # (['an', 'stands', 'los'], ['<', '>'])
2469 # >>> zerlege('An<al.pha-bet')
2470 # (['An', 'al', 'pha', 'bet'], ['<', '.', '-'])
2475 silben
= re
.split('[-·._<>=]+', wort
)
2476 trennzeichen
= re
.split('[^-·._|<>=]+', wort
)
2477 return silben
, [tz
for tz
in trennzeichen
if tz
]
2482 # Gib einen String mit Trennmarkierung für abweichende Trennungen in
2483 # mehrdeutigen Wörtern zurück:
2485 # >>> from wortliste import alternatives
2486 # >>> alternatives('Mas-se', 'Ma-sse')
2488 # >>> alternatives('Ma-sse', 'Mas-se')
2493 def alternatives(wort1
, wort2
):
2495 wort1
= [c
for c
in wort1
]
2496 wort2
= [c
for c
in wort2
]
2499 for c1
, c2
in zip(wort1
, wort2
):
2503 for c1
, c2
in zip(wort1
.__reversed__(), wort2
.__reversed__()):
2508 return ''.join(pre
+ ['['] + wort1
[len(pre
):-len(post
)] + ['/']
2509 + wort2
[len(pre
):-len(post
)] + [']'] + post
)
2515 # Großschreibung in Kleinschreibung wandeln und umgekehrt
2517 # Diese Version funktioniert auch für Wörter mit Trennzeichen (während
2518 # str.title() nach jedem Trennzeichen wieder groß anfängt)
2520 # >>> from wortliste import toggle_case
2521 # >>> toggle_case('Ha-se')
2523 # >>> toggle_case('arm')
2525 # >>> toggle_case('frei=bier')
2527 # >>> toggle_case('L}a-ger')
2530 # Keine Änderung bei Wörtern mit Großbuchstaben im Inneren:
2532 # >>> toggle_case('USA')
2534 # >>> toggle_case('iRFD')
2537 # >>> toggle_case('gri[f-f/{ff/ff')
2539 # >>> toggle_case('Gri[f-f/{ff/ff')
2544 def toggle_case(wort
):
2546 key
= join_word(wort
, assert_complete
=True)
2547 except AssertionError:
2552 return wort
[0].upper() + wort
[1:]
2559 # Schlüssel für die alphabetische Sortierung gemäß Duden-Regeln.
2561 # >>> from wortliste import sortkey_duden
2562 # >>> sortkey_duden(["Abflußröhren"])
2563 # 'abflussrohren a*bflu*szroehren'
2564 # >>> sortkey_duden(["Abflußrohren"])
2565 # 'abflussrohren a*bflu*szro*hren'
2566 # >>> sortkey_duden(["Abflussrohren"])
2569 # >>> s = sorted([["Abflußröhren"], ["Abflußrohren"], ["Abflussrohren"]],
2570 # ... key=sortkey_duden)
2571 # >>> print(', '.join(e[0] for e in s))
2572 # Abflussrohren, Abflußrohren, Abflußröhren
2576 # Ligaturen auflösen und andere "normalisierende" Ersetzungen für den
2577 # (Haupt-)Sortierschlüssel (Akzente werden über ``unicodedata.normalize``
2586 # "Zweitschlüssel" zur Unterscheidung von Umlauten/SZ und Basisbuchstaben::
2588 umschrift_subkey
= {
2600 # Argument ist ein Eintrag im WordEntry oder ShortEntry Format oder
2601 # ein (Unicode) String:
2603 # >>> print(sortkey_duden(weste)) # WordEntry
2605 # >>> print(sortkey_duden(dst)) # ShortEntry
2608 # >>> print(sortkey_duden('Stra-ße'))
2610 # >>> print(sortkey_duden(['Büh-ne']))
2612 # >>> print(sortkey_duden('Weiß=flog;Weiß=flog;-3-;-4-;-5-'))
2613 # weissflog weiszflo*g
2614 # >>> print(sortkey_duden('-1-;Meß=sen-der;-3-;-4-;-5-'))
2615 # messsender meszsender
2616 # >>> print(sortkey_duden('As-sen # (geogr. und Eigen-) Name\n'))
2618 # >>> print(sortkey_duden('aßen;aßen;-3-;-4-;-5-\n'))
2620 # >>> print(sortkey_duden('äßen\n'))
2625 def sortkey_duden(entry
):
2627 # ggf. ungetrenntes Wort extrahieren oder generieren::
2629 if isinstance(entry
, list):
2632 except AttributeError:
2634 if len(entry
) == 1: # ein Muster pro Zeile, siehe z.B. pre-1901
2635 key
= join_word(key
)
2637 match
= re
.search('^[-0-9;]*([^;\s]+)', entry
) # erstes volles Feld
2639 key
= match
.group(1)
2642 key
= join_word(key
)
2643 key
= re
.sub('[0-9;]', '', key
)
2645 # Großschreibung ignorieren:
2647 # Der Duden sortiert Wörter, die sich nur in der Großschreibung unterscheiden
2648 # "klein vor groß" (ASCII sortiert "groß vor klein"). In der
2649 # `Trennmuster-Wortliste` kommen Wörter nur mit der häufiger anzutreffenden
2650 # Großschreibung vor, denn der TeX-Trennalgorithmus ignoriert Großschreibung.
2659 skey
= key
.replace('ß', 'ss')
2661 # Restliche Akzente weglassen: Wandeln in Darstellung von Buchstaben mit
2662 # Akzent als "Grundzeichen + kombinierender Akzent". Anschließend alle
2663 # nicht-ASCII-Zeichen ignorieren::
2665 skey
= skey
.translate(umschrift_skey
)
2666 skey
= unicodedata
.normalize('NFKD', skey
)
2667 skey
= skey
.encode('ascii', 'ignore').decode()
2669 # "Zweitschlüssel" für das eindeutige Einsortieren von Wörtern mit
2670 # gleichem Schlüssel (Masse/Maße, waren/wären, ...):
2672 # * "*" nach aou für die Unterscheidung Grund-/Umlaut
2678 subkey
= key
.translate(umschrift_subkey
)
2679 skey
= '%s %s' % (skey
,subkey
)
2681 # Gib den Sortierschlüssel zurück::
2690 # Vergleiche zwei Sequenzen von `WordEntries` (genauer: alle Objekte, die
2691 # sich sinnvoll zu Unicode wandeln lassen).
2695 # >>> from wortliste import udiff
2696 # >>> print(udiff([abbeissen, aalbestand], [abbeissen,dresz], 'alt', 'neu'))
2700 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
2701 # -Aalbestand;Aal=be<stand # Test
2702 # +Dreß;-2-;Dreß;-4-
2704 # >>> udiff([abbeissen, aalbestand], [abbeissen, aalbestand], 'alt', 'neu')
2709 def udiff(a
, b
, fromfile
='', tofile
='',
2710 fromfiledate
='', tofiledate
='', n
=1):
2712 a
= [str(entry
).rstrip() for entry
in a
]
2713 b
= [str(entry
).rstrip() for entry
in b
]
2715 diff
= '\n'.join(difflib
.unified_diff(a
, b
, fromfile
, tofile
,
2716 fromfiledate
, tofiledate
, n
, lineterm
=''))
2725 # Anwenden der Trennstile auf ein Wort:
2727 # >>> from wortliste import run_filters
2729 # >>> run_filters(['keine_flattervokale'], 'Psy-ch<i-a-trie')
2731 # >>> run_filters(['keine_flattervokale'], 'An<woh-ner=in<.i-ti-a-ti-ve')
2732 # 'An<woh-ner=in<.i-tia-ti-ve'
2734 # Test: Trennungen wie bisher im 'dehyphen-exptl' TeX-Paket:
2736 # >>> run_filters(['morphemisch', 'standard'], 'Psy-ch<i-a-trie')
2738 # >>> run_filters(['morphemisch', 'standard'], 'An<woh-ner=in<.i-ti-a-ti-ve')
2739 # 'An<woh-ner=initia-ti-ve'
2741 # >>> run_filters(['standard'], 'Text=il<..lu-stra-ti-.on')
2742 # 'Text=illu-stra-tion'
2744 # >>> run_filters(['morphemisch'], 'Psy-ch<i-a-trie')
2746 # >>> run_filters(['morphemisch'], 'An<woh-ner=in<.i-ti-a-ti-ve')
2747 # 'An<woh-ner=in<.i·ti-a-ti-ve'
2749 # >>> run_filters(['morphemisch'], 'ge-r<i-a-tri-sche')
2750 # 'ger<i·a-tri-sche'
2751 # >>> run_filters(['syllabisch','regelsilben'], 'ge-r<i-a-tri-sche')
2752 # 'ge-ri-at-ri-sche'
2754 # >>> run_filters(['regelsilben'], 'abs-trakt')
2757 # >>> run_filters(['morphemisch'], 'Fern=ab<.i-tur')
2759 # >>> run_filters(['syllabisch','regelsilben'], 'Fern=ab<.i-tur')
2761 # >>> run_filters(['morphemisch', 'standard'], 'Fern=ab<.i-tur')
2766 def run_filters(styles
, word
):
2767 """Apply a sequence of hyphenation `styles` to `word`."""
2769 for style
in styles
:
2771 word
= getattr(stilfilter
, style
)(word
)
2772 except AttributeError:
2774 raise ValueError('Trennstil %s nicht definiert. Siehe "--stilliste"' % style
)
2778 # normalize_language_tag(tag)
2779 # ---------------------------
2781 # Normalisierung und Expansion von Sprachtags nach [BCP47]_
2783 # >>> from wortliste import normalize_language_tag
2784 # >>> normalize_language_tag('de_AT-1901')
2785 # ['de-AT-1901', 'de-AT', 'de-1901', 'de']
2787 # >>> normalize_language_tag('de') # Deutsch, allgemeingültig
2789 # >>> normalize_language_tag('de_1901') # traditionell (Reform 1901)
2791 # >>> normalize_language_tag('de_1996') # reformiert (Reform 1996)
2793 # >>> normalize_language_tag('de_CH') # ohne ß (Schweiz oder versal)
2795 # >>> normalize_language_tag('de-x-versal') # versal
2796 # ['de-x-versal', 'de']
2797 # >>> normalize_language_tag('de-1901-x-versal') # versal
2798 # ['de-1901-x-versal', 'de-1901', 'de-x-versal', 'de']
2799 # >>> normalize_language_tag('de_CH-1996') # Schweiz traditionell (süssauer)
2800 # ['de-CH-1996', 'de-CH', 'de-1996', 'de']
2802 # 'de': 1, # Deutsch, allgemeingültig
2803 # 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
2804 # 'de-1996': 3, # reformierte Reformschreibung (1996)
2805 # 'de-x-versal': 4, # ohne ß (Schweiz oder versal) allgemein
2806 # # 'de-CH': 4, # Alias
2807 # 'de-1901-x-versal': 5, # ohne ß (Schweiz oder versal) "traditionell"
2808 # 'de-1996-x-versal': 6, # ohne ß (Schweiz oder versal) "reformiert"
2809 # # 'de-CH-1996': 6, # Alias
2810 # 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
2815 def normalize_language_tag(tag
):
2816 """Return a list of normalized combinations for a `BCP 47` language tag.
2819 tag
= tag
.replace('_','-')
2820 # split (except singletons, which mark the following tag as non-standard):
2821 tag
= re
.sub(r
'-([a-zA-Z0-9])-', r
'-\1_', tag
)
2823 subtags
= [subtag
.replace('_', '-') for subtag
in tag
.split('-')]
2824 base_tag
= [subtags
.pop(0)]
2825 # find all combinations of subtags
2826 for n
in range(len(subtags
), 0, -1):
2827 # for tags in unique_combinations(subtags, n):
2828 for tags
in itertools
.combinations(subtags
, n
):
2829 taglist
.append('-'.join(base_tag
+list(tags
)))
2840 # Iterator über mehrere Eingabedateien.
2841 # Ergibt Sequenz von Zeilen als Unicode-Strings::
2843 def filelines(names
):
2848 infile
= open(name
, encoding
='utf-8')
2856 # Iterator über Worliste-Datei(en)
2858 # Ausgabe: Nach Trennregeln der Sprachvariante getrenntes Wort.
2863 def sprachauszug(infiles
, language
="de-1996", entry_class
=WordEntry
, verbose
=False):
2865 for line
in filelines(infiles
):
2867 # Zeile lesen und in WordEntry oder ShortEntry Objekt wandeln::
2869 if not line
or line
.startswith('#'):
2873 entry
= entry_class(line
)
2875 # Wort in der gewünschten Sprachvarietät aussuchen::
2877 word
= entry
.get(language
)
2889 # Teste Übereinstimmung des ungetrennten Wortes in Feld 1 mit den
2890 # Trennmustern nach Entfernen der Trennmarker. Schreibe Inkonsistenzen auf die
2893 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
2895 def test_keys(wortliste
):
2896 print("Teste Schlüssel-Trennmuster-Übereinstimmung:")
2898 for entry
in wortliste
:
2899 if isinstance(entry
, ShortEntry
):
2900 print("Wortliste im Kurzformat: überspringe Schlüssel-Test.")
2902 # Test der Übereinstimmung ungetrenntes/getrenntes Wort
2905 for wort
in entry
[1:]:
2906 if not wort
: # leere Felder
2908 if key
!= join_word(wort
):
2910 print(" key '%s' != join_word('%s')" % (key
, wort
),)
2911 if key
.lower() == join_word(wort
).lower():
2912 print(" Abgleich der Großschreibung mit"
2913 "`prepare-patch.py grossabgleich`.", end
=' ')
2918 # Finde Doppeleinträge (teste, ob jeder Schlüssel nur einmal vorkommt).
2919 # Schreibe Inkonsistenzen auf die Standardausgabe.
2921 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
2923 def test_uniqueness(wortliste
):
2927 for entry
in wortliste
:
2931 print("da ", str(words
[key
]))
2932 print("neu", str(entry
))
2934 print("%d Doppeleinträge." % doppelte
)
2936 # Teste die Wandlung einer Zeile im "wortliste"-Format in eine
2937 # ``WordEntry``-Instanz und zurück::
2939 def test_str_entry_str_conversion(wordfile
):
2941 for line
in filelines([wordfile
.name
]):
2942 entry
= WordEntry(line
)
2943 if line
== str(entry
):
2947 print('+', str(entry
))
2949 print(OK
, "Einträge rekonstruiert")
2952 # Teste Vervollständigung und Zusammenfassung von Einträgen::
2954 def test_completion_pruning(entries
):
2956 for entry
in entries
:
2957 new
= copy
.copy(entry
)
2961 patch
= udiff(entries
, reko
, 'wortliste', 'neu')
2965 print("alle Einträge rekonstruiert")
2967 def test_doppelte(entries
):
2969 langs
= ("de", "de-1901", "de-CH", "de-1901-x-versal", "de-CH-1901")
2973 for entry
in entries
:
2976 word
= entry
.get(lang
)
2977 key
= join_word(word
)
2978 if word
and (key
in words
[lang
]):
2979 oldword
= words
[lang
][key
].get(lang
)
2980 if oldword
and (word
!= oldword
):
2982 print("%-16s" % lang
, oldword
, '!=', word
)
2983 print(" "*18, str(words
[lang
][key
].pruned()))
2984 print(" "*18, str(entry
.pruned()))
2985 words
[lang
][key
] = entry
2986 print("%d Doppeleinträge." % doppelte
)
2990 # Aufruf von der Kommandozeile
2991 # ============================
2995 if __name__
== '__main__':
2997 print("Test der Werkzeuge und inneren Konsistenz der Wortliste")
2999 # Ein WordFile Dateiobjekt::
3002 wordfile
= WordFile(sys
.argv
[1], format
='auto')
3004 wordfile
= WordFile('../../../wortliste')
3005 # wordfile = WordFile('../../../wlst', format='auto') # wortliste im Kurzformat
3006 # wordfile = WordFile('neu.todo')
3007 # wordfile = WordFile('neu-kurz.todo', format='f5')
3008 # wordfile = WordFile('korrektur.todo', format='auto')
3010 # Format bestimmen::
3012 print("Datei: '%s'" % wordfile
.name
, "Format:", wordfile
.format
)
3014 # Liste der Datenfelder (die Klasseninstanz als Argument für `list` liefert
3015 # den Iterator über die Felder, `list` macht daraus eine Liste)::
3017 wordlist
= list(wordfile
)
3018 print(len(wordlist
), "Zeilen")
3019 print(wordlist
[0], type(wordlist
[0]))
3021 # Ein Wörterbuch (dict Instanz)::
3023 wordfile
.seek(0) # Pointer zurücksetzen
3024 words
= wordfile
.asdict()
3025 print(len(words
), "Wörterbucheinträge")
3027 # Test auf Doppeleinträge::
3029 if len(words
) != len(wordlist
) and wordfile
.format
!= "f5":
3030 test_uniqueness(wordlist
)
3032 # Teste Schlüssel-Trennmuster-Übereinstimmung::
3034 if isinstance(wordlist
[0], WordEntry
):
3037 # Teste Eintrags-Konsistenz im Kurzformat::
3039 # if isinstance(wordlist[0], ShortEntry):
3040 # test_doppelte(wordlist)
3042 # Teste Komplettieren/Zusammenfassen der Einträge::
3044 # test_completion_pruning(wordlist)
3050 # sprache = 'de-1901' # traditionell
3051 sprache
= 'de-1996' # Reformschreibung
3052 # sprache = 'de-x-versal' # ohne ß (Schweiz oder versal) allgemein
3053 # sprache = 'de-1901-x-versal' # ohne ß (Schweiz oder versal) "traditionell"
3054 # sprache = 'de-1996-x-versal' # ohne ß (Schweiz oder versal) "reformiert"
3055 # sprache = 'de-CH-1901' # ohne ß (Schweiz) "traditionell" ("süssauer")
3057 # worte = [entry.get(sprache) for entry in wordlist]
3058 # worte = [wort for wort in worte if wort]
3059 # print(len(worte), "Einträge für Sprachvariante", sprache)
3061 # Zeilenrekonstruktion::
3063 # test_str_entry_str_conversion(wordfile)
3066 # Teste Konsistenz der Auszeichnung::
3068 # schwankungsfaelle = {}
3069 # words = [entry.get(sprache) for entry in wordlist]
3070 # for word in words:
3072 # match = re.search('[<=]([^<=]*[aeiouyäöü]\.[aeiouyäöü])[^<=]', word)
3074 # match = re.search('^(.*[aeiouyäöü]\.[aeiouyäöü])[^<=]', word)
3076 # key = match.group(1).lower()
3077 # key = key[:-2] + '-' + key[-1]
3078 # schwankungsfaelle[key] = word
3079 # print(len(schwankungsfaelle), "Schwankungsfälle (von </= bis eins nach .)")
3080 # # for key, value in schwankungsfaelle.items():
3081 # # print("%15s" % key, ":", value)
3082 # print("Inkonsistenzien:")
3083 # for word in words:
3084 # for key in schwankungsfaelle:
3085 # if (word.startswith(key.lower())
3086 # or '=' + key in word
3087 # or '<' + key in word):
3088 # print(key[:-2] + '*' + key[-1], schwankungsfaelle[key], word)
3094 # .. [BCP47] A. Phillips und M. Davis, (Editoren.),
3095 # `Tags for Identifying Languages`, http://www.rfc-editor.org/rfc/bcp/bcp47.txt
3097 # .. _aktuelle Rechtschreibung:
3099 # .. [Rechtschreibregeln] Rat für deutsche Rechtschreibung,
3100 # `Deutsche Rechtschreibung – Regeln und Wörterverzeichnis`,
3101 # http://www.rechtschreibrat.com/regeln-und-woerterverzeichnis/
3103 # .. _traditionelle Rechtschreibung:
3105 # .. [Duden1991] Wissenschaftlicher Rat der Dudenredaktion (Editoren),
3106 # `Duden: Rechtschreibung der deutschen Sprache`,
3107 # Dudenverlag Mannheim, 1991.
3109 # .. _Wortliste der deutschsprachigen Trennmustermannschaft:
3110 # ../../../dokumente/README.wortliste
3112 # .. _wortliste: ../../../wortliste