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 from collections
import defaultdict
# Wörterbuch mit Default
39 # Klasse zum Lesen und Schreiben der `Wortliste`::
48 # Das Argument ``format`` ist "f8", "f5", oder "auto":
50 # :f8: das originale, maximal 8-spaltige Wortlisten-Format (Langform_),
51 # :f5: das neue, maximal 5-spaltige Wortlisten-Format (Kurzform_),
52 # :auto: bestimme das Format automatisch.
56 def __init__(self
, name
, mode
='r', encoding
='utf8', format
='f8'):
58 self
.encoding
= encoding
59 file.__init
__(self
, name
, mode
)
60 # Dateiformat bestimmen und die Eintrags-Klasse setzen:
61 self
.set_entry_class(format
)
63 # Klasse zum Verarbeiten der Zeilen setzen.
64 # Mit self.format == "auto", werden die ersten Zeilen der Datei untersucht
65 # und der Pointer zurückgesetzt.
69 def set_entry_class(self
, format
, search_limit
=1000):
71 assert(format
in ('f8', 'f5', 'auto'))
72 self
.format
= format
# default
73 self
.entry_class
= WordEntry
# default
76 # Format anhand des Dateiinhalts bestimmen:
79 # nur ein Feld oder erstes Feld leer oder mit Trennzeichen:
80 if len(e
) == 1 or e
and re
.search(u
'[-=<>]', e
[0]):
83 # mehr als 5 Felder: 8-Felder-Format (Langform)
90 if self
.format
== 'f5':
91 self
.entry_class
= ShortEntry
93 self
.seek(0) # Dateizeiger zurücksetzen
99 # Die spezielle Funktion `__iter__` wird aufgerufen wenn über eine
100 # Klasseninstanz iteriert wird.
101 # Sie liefert einen Iterator über die "geparsten" Zeilen (Datenfelder)
105 entry_class
= self
.entry_class
107 line
= self
.next().rstrip().decode(self
.encoding
)
108 yield entry_class(line
)
114 # Gib ein `dictionary` (assoziatives Array) mit ungetrenntem Wort als
115 # Schlüssel (`key`) und den Datenfeldern als `value` zurück::
121 if key
== u
'#': # reiner Kommentar
123 words
[u
'#'].append(self
.comment
)
125 words
[u
'#'] = self
.comment
133 # Schreibe eine Liste von `unicode` Strings (Zeilen ohne Zeilenendezeichen)
134 # in die Datei `destination`::
136 def writelines(self
, lines
, destination
, encoding
=None):
137 outfile
= codecs
.open(destination
, 'w',
138 encoding
=(encoding
or self
.encoding
))
139 outfile
.writelines(lines
)
144 # Schreibe eine Liste von Einträgen (WordEntry_ oder ShortEntry_ Objekten) in
145 # die Datei `destination`::
147 def write_entries(self
, wortliste
, destination
, encoding
=None):
148 lines
= (unicode(entry
) for entry
in wortliste
)
149 self
.writelines(lines
, destination
, encoding
)
155 # Klasse für Einträge (Zeilen) der Wortliste_ in Langform_.
156 # Jede Zeile enthält einen Eintrag mit durch Semikolon „;“
157 # getrennten Feldern.
161 # >>> from wortliste import WordEntry
163 # >>> aalbestand = WordEntry(u'Aalbestand;Aal=be<stand # Test')
164 # >>> print aalbestand
165 # Aalbestand;Aal=be<stand # Test
168 # .. _8-Spalten-Format:
170 # Bedeutung der Felder im 8-Spalten-Format (Langform):
173 # 2. Wort mit Trennungen, falls für alle Varianten identisch,
175 # 3. falls Feld 2 leer, Trennung nach traditioneller Rechtschreibung
176 # 4. falls Feld 2 leer, Trennung nach reformierter Rechtschreibung (2006)
177 # 5. falls Feld 2 leer, Trennung für Wortform, die entweder in
178 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird
179 # und für traditionelle und reformierte Rechtschreibung identisch ist
180 # 6. falls Feld 5 leer, Trennung für Wortform, die entweder in
181 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
182 # traditionelle Rechtschreibung
183 # 7. falls Feld 5 leer, Trennung für Wortform, die entweder in
184 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
185 # reformierte Rechtschreibung (2006)
186 # 8. falls Feld 5 leer, Trennung nach (deutsch)schweizerischer
187 # Rechtschreibung; insbesondere Wörter mit "sss" gefolgt von
188 # einem Vokal, die wie andere Dreifachkonsonanten gehandhabt wurden
189 # (also anders, als der Duden früher vorgeschrieben hat), z.B.
194 class WordEntry(list):
199 # feldnamen, feldindizes
200 # ~~~~~~~~~~~~~~~~~~~~~~
202 # Benennung der Felder mit Sprachkürzeln (Tags nach [BCP47]_).
203 # (Die Zählung der Indizes beginnt in Python bei 0.)::
205 feldnamen
= ('key', 'de', 'de-1901', 'de-1996', 'de-x-versal',
206 'de-1901-x-versal', 'de-1996-x-versal', 'de-CH-1901')
209 'key': 0, # Schlüssel
210 'de': 1, # Deutsch, allgemeingültig
211 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
212 'de-1996': 3, # reformierte Reformschreibung (1996)
213 'de-x-GROSS': 4, # ohne ß (Schweiz oder versal) allgemein
214 'de-x-versal': 4, # Alias
215 # 'de-CH': 4, # Alias
216 'de-1901-x-GROSS': 5, # ohne ß (Schweiz oder versal) "traditionell"
217 'de-1901-x-versal': 5, # Alias
218 'de-1996-x-GROSS': 6, # ohne ß (Schweiz oder versal) "reformiert"
219 'de-1996-x-versal': 6, # Alias
220 # 'de-CH-1996': 6, # Alias
221 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
228 # Bei der Auswahl eines Wortes in einer Rechtschreibvariante werden
229 # "generische" Felder gewählt, falls das "Originalfeld" fehlt. Die folgenden
230 # Tupel listen die Ersatzfelder für das ensprechende Originalfeld.
232 # Bsp: Ersatz-Felder für 'de-CH-1901':
234 # >>> for i in WordEntry.ersatzfelder[WordEntry.feldindizes['de-CH-1901']]:
235 # ... print WordEntry.feldnamen[i]
243 # Ersatzindizes Index Nr Name
244 ersatzfelder
= (tuple(), # 0 1 key
246 (1,), # 2 3 'de-1901'
247 (1,), # 3 4 'de-1996'
248 (1,), # 4 5 'de-x-versal'
249 (4, 2, 1), # 5 6 'de-1901-x-versal'
250 (4, 3, 1), # 6 7 'de-1996-x-versal'
251 (5, 4, 2, 1), # 7 8 'de-CH-1901'
258 # Kommentar, Vorgabe ist ein leerer String::
268 def __init__(self
, line
, delimiter
=u
';'):
270 self
.delimiter
= delimiter
272 # eventuell vorhandenen Kommentar abtrennen und speichern::
275 line
, comment
= line
.split(u
'#', 1)
276 self
.comment
= comment
.strip()
278 # print (line, self.comment)
280 if not line
: # kein Inhalt
283 # Zerlegen in Datenfelder, in Liste eintragen::
285 for field
in line
.split(delimiter
):
286 if field
.startswith(u
'-'):
292 # Rückverwandlung in String
293 # -------------------------
295 # Erzeugen eines Eintrag-Strings (ohne Zeilenendezeichen) aus der Liste der
296 # Datenfelder und dem Kommentar
301 # >>> unicode(aalbestand)
302 # u'Aalbestand;Aal=be<stand # Test'
304 # >>> grse = WordEntry(u'Grüße;Grü-ße')
305 # >>> print unicode(grse)
308 # >>> leerkommentar = WordEntry(u'# Testkommentar ')
309 # >>> leerkommentar, unicode(leerkommentar)
310 # (WordEntry(u'# Testkommentar'), u'# Testkommentar')
314 def __unicode__(self
):
315 # Nummerieren leerer Felder:
316 fields
= [field
or u
'-%d-' % (i
+1)
317 for (i
, field
) in enumerate(self
)]
318 line
= u
';'.join(fields
)
320 line
+= u
' # ' + self
.comment
327 # >>> print type(str(grse)), str(grse).decode('utf8')
328 # <type 'str'> Grüße;Grü-ße
333 return unicode(self
).encode('utf8')
337 # >>> print type(repr(grse)), repr(grse).decode('utf8')
338 # <type 'str'> WordEntry(u'Grüße;Grü-ße')
343 s
= u
'%s(u\'%s\')' % (self
.__class
__.__name
__, self
)
344 return s
.encode('utf8')
349 # Bestimme den Index des zur Sprachvariante gehörenden Datenfeldes unter
350 # Verwendung der Ersatzfelder:
352 # >>> aalbestand.lang_index('de')
354 # >>> aalbestand.lang_index('de-1901')
356 # >>> aalbestand.lang_index('de-1996')
358 # >>> aalbestand.lang_index('de-x-versal')
360 # >>> aalbestand.lang_index('de-1901-x-versal')
362 # >>> aalbestand.lang_index('de-1996-x-versal')
364 # >>> abbeissen = WordEntry(
365 # ... u'abbeissen;-2-;-3-;-4-;-5-;ab<bei-ssen;ab<beis-sen;ab<beis-sen')
366 # >>> print abbeissen.lang_index('de')
368 # >>> print abbeissen.lang_index('de-x-versal')
370 # >>> abbeissen.lang_index('de-1901-x-versal')
372 # >>> abbeissen.lang_index('de-1996-x-versal')
374 # >>> abbeissen.lang_index('de-CH-1901')
376 # >>> urlaubstipp = WordEntry(u'Urlaubstipp;-2-;-3-;Ur<laubs=tipp')
377 # >>> print urlaubstipp.lang_index('de')
379 # >>> print urlaubstipp.lang_index('de-1901')
381 # >>> print urlaubstipp.lang_index('de-1996')
383 # >>> print urlaubstipp.lang_index('de-x-versal')
385 # >>> print urlaubstipp.lang_index('de-1901-x-versal')
390 def lang_index(self
, lang
):
392 if lang
not in self
.feldindizes
:
393 raise ValueError('Sprachvariante "%s" nicht in %s'
394 % (lang
, self
.feldindizes
.keys()))
396 # Einfacher Fall: eine allgemeine Schreibweise::
399 if u
'ß' in self
[0] and ('CH' in lang
or 'versal' in lang
):
404 # Spezielle Schreibung::
407 i
= self
.feldindizes
[lang
]
410 if lang
== 'de-CH-1901':
411 return self
.lang_index('de-1901-x-versal')
412 # Allgemeine Schweiz/versal Schreibung:
413 if i
> 4 and len(self
) == 5:
415 # versal == normal (kein sz):
416 if len(self
) == 4 and u
'ß' not in self
[0]:
417 if lang
in ('de-CH-1901', 'de-1901-x-versal'):
418 return self
.lang_index('de-1901')
419 elif lang
in ('de-CH-1996', 'de-1996-x-versal'):
420 return self
.lang_index('de-1996')
421 return None # Feld nicht vorhanden
423 if not feld
: # leeres Feld
431 # Gib einen Schlüssel (ungetrenntes Wort) zurück.
433 # Bei Einträgen im Langform_ ist der Schlüssel gleich dem Inhalt des ersten
434 # Feldes. Die Funktion dient zur Kompatibilität mit ShortEntry_.
436 # >>> print WordEntry(u'Fluss;Quatsch').key()
438 # >>> print WordEntry(u'Dreß;-1-;Dreß;-3-').key()
441 # Das `lang` Argument wird ignoriert, da der Schlüssel (im Gegensatz zur
442 # Kurzform_) stets eindeutig ist:
444 # >>> print WordEntry(u'Dreß;-1-;Dreß;-3-').key('de-1996')
447 # Der Schlüssel eines leeren Eintrags ist ein leerer String, der eines leeren
448 # Kommentars das Kommentarzeichen:
450 # >>> WordEntry(u'').key(), WordEntry(u'# toller Kommentar').key()
455 def key(self
, lang
=None):
456 """Gib den Schlüssel (ungetrenntes Wort) zurück."""
459 except IndexError: # reiner Kommentar oder leerer Eintrag
468 # Gib Feld ``i`` oder Ersatzfeld zurück.
471 def getitem(self
, i
):
472 """Return item ``i`` or a subsititute"""
475 except IndexError: # Feld i nicht vorhanden
477 # Ersatzregeln anwenden
478 for _i
in self
.ersatzfelder
[i
]:
481 except IndexError: # Feld i nicht vorhanden
482 continue # nächsten `tag` versuchen
483 # Spezialfall: in Versalschreibung ungültige Ersatz-Wörter
484 if i
>= 4 and _i
<4 and u
'ß' in word
:
489 # .. _WordEntry.get():
494 # Trennmuster für eine Sprachvariante ausgeben:
496 # >>> aalbestand.get('de')
498 # >>> aalbestand.get('de-1901')
500 # >>> aalbestand.get('de-1996')
502 # >>> aalbestand.get('de-x-versal')
504 # >>> aalbestand.get('de-1901-x-versal')
506 # >>> aalbestand.get('de-1996-x-versal')
508 # >>> aalbestand.get('de-CH-1901')
511 # >>> weste = WordEntry(u'Weste;-2-;We-ste;Wes-te')
512 # >>> weste.get('de')
514 # >>> weste.get('de-1901')
516 # >>> weste.get('de-1996')
519 # >>> abbeissen.get('de')
521 # >>> abbeissen.get('de-x-versal')
523 # >>> abbeissen.get('de,de-x-versal')
525 # >>> abbeissen.get('de-1901-x-versal')
527 # >>> abbeissen.get('de,de-1901,de-1901-x-versal')
529 # >>> abbeissen.get('de-CH-1901')
535 for lang
in tags
.split(','):
536 word
= self
.getitem(self
.feldindizes
[lang
])
547 # Trennmuster für Sprachvariante setzen:
549 # >>> abbeissen.set('test', 'de-1901-x-versal')
550 # >>> print abbeissen
551 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
553 # >>> abbeissen.set('test', 'de-1901')
554 # Traceback (most recent call last):
556 # IndexError: kann kein leeres Feld setzen
558 # >>> abbeissen.set('test', 'de-1901,de-1901-x-versal')
559 # >>> print abbeissen
560 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
564 def set(self
, word
, tags
):
565 for lang
in tags
.split(','):
566 i
= self
.lang_index(lang
)
573 raise IndexError, "kann kein leeres Feld setzen"
580 # >>> entry = WordEntry(u'test;test')
581 # >>> entry.setitem(4, u's-x')
583 # test;test;-3-;-4-;s-x
584 # >>> entry.setitem(3, u'sz')
586 # test;test;-3-;sz;s-x
590 def setitem(self
, i
, word
):
591 while len(self
) < i
+1:
599 # Alle Felder setzen:
601 # >>> print WordEntry(u'Ruhe;Ru-he').completed()
602 # Ruhe;Ru-he;Ru-he;Ru-he;Ru-he;Ru-he;Ru-he;Ru-he
604 # >>> print str(aalbestand), len(aalbestand)
605 # Aalbestand;Aal=be<stand # Test 2
606 # >>> print aalbestand.completed()
607 # Aalbestand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand # Test
609 # >>> auffrass = WordEntry(u'auffrass;-2-;-3-;-4-;auf-frass')
610 # >>> print auffrass.completed()
611 # auffrass;-2-;-3-;-4-;auf-frass;auf-frass;auf-frass;auf-frass
613 # >>> fresssack= WordEntry(u'Fresssack;-2-;-3-;Fress=sack;Fress=sack')
614 # >>> print fresssack.completed()
615 # Fresssack;-2-;-3-;Fress=sack;Fress=sack;Fress=sack;Fress=sack;Fress=sack
617 # >>> line = u'Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-'
618 # >>> print WordEntry(line).completed()
619 # Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-;Fluss=schi{ff/ff=f}ahrt
621 # >>> line = u'Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße'
622 # >>> ackerstrasse = WordEntry(line).completed()
623 # >>> print unicode(ackerstrasse)
624 # Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße;-5-;-6-;-7-;-8-
629 for i
in range(len(self
),8):
630 self
.append(self
.getitem(i
))
632 # .. _WordEntry.completed():
637 # Gib eine vervollständigte Kopie des Eintrags zurück.
641 """Return expanded copy of self."""
642 neu
= copy
.copy(self
)
646 # .. _ShortEntry.prune():
653 # Felder für Sprachvarianten zusammenfassen, wenn gleich:
655 # >>> aalbestand.prune()
656 # >>> print aalbestand
657 # Aalbestand;Aal=be<stand # Test
658 # >>> auffrass.prune()
660 # auffrass;-2-;-3-;-4-;auf-frass
661 # >>> entry = WordEntry(u'distanziert;-2-;di-stan-ziert;di-stan-ziert')
664 # distanziert;di-stan-ziert
665 # >>> entry = WordEntry(u'Gauss;-2-;Gauss;Gauss;Gauss')
669 # >>> fresssack.prune()
670 # >>> print fresssack
671 # Fresssack;-2-;-3-;Fress=sack;Fress=sack
673 # >>> masse = WordEntry(u'Masse;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se')
678 # Aber nicht, wenn die Trennstellen sich unterscheiden:
680 # >>> abenddienste = WordEntry(
681 # ... u'Abenddienste;-2-;Abend=dien-ste;Abend=diens-te')
682 # >>> abenddienste.prune()
683 # >>> print abenddienste
684 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
685 # >>> ackerstrasse.prune()
686 # >>> print unicode(ackerstrasse)
687 # Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße
688 # >>> entry = WordEntry(
689 # ... u'Schlammmasse;-2-;-3-;Schlamm=mas-se;-5-;-6-;Schlamm=mas-se;-8-')
692 # Schlammmasse;-2-;-3-;Schlamm=mas-se
693 # >>> entry = WordEntry(
694 # ... u'Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-')
697 # Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-
703 if self
[7] == self
[5]: # de-CH-1901 gleich versal-1901
706 if self
[6] == self
[5]:
707 self
[4] = self
[5] # umschreiben auf versal-allgemein
710 elif (not(self
[4] or self
[5] or self
[6])
711 or self
[5] == self
[2] and self
[6] == self
[3]):
712 # alle leer oder ohne ß-Ersetzung
719 elif self
[1] == self
[4]: # de-x-versal == de
723 elif self
[2] == self
[3] == self
[4]:
724 self
[1] = self
[2] # Umschreiben auf de (allgemein)
729 if self
[3] == self
[2]: # de-1996 == de-1901
730 self
[1] = self
[2] # Umschreiben auf de (allgemein)
743 # Gib eine gekürzte Kopie des Eintrags zurück.
747 """Return pruned copy of self."""
748 neu
= copy
.copy(self
)
752 # .. _WordEntry.merge():
757 # Einträge zusammenfassen:
759 # >>> entry = WordEntry(u'Dienste;-2-;Dien-ste')
760 # >>> entry.merge(WordEntry(u'Dienste;-2-;-3-;diens-te'))
761 # >>> print unicode(entry)
762 # Dienste;-2-;Dien-ste;Diens-te
763 # >>> entry = WordEntry(u'Abenddress;Abend=dress')
764 # >>> entry.merge(WordEntry(u'Abenddress;-2-;-3-;-4-;Abend=dress'))
765 # >>> print unicode(entry)
766 # Abenddress;Abend=dress
767 # >>> entry = WordEntry(u'Gauss;Gauss')
768 # >>> entry.merge(WordEntry(u'Gauss;-2-;-3-;-4-;Gauss'))
769 # >>> print unicode(entry)
771 # >>> masse.merge(WordEntry(u'masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se'),
772 # ... allow_alternatives=True)
774 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[s-/-s]se;Mas-se;Mas-se
778 def merge(self
, other
, allow_alternatives
=False, prune
=True,
779 merge_comments
=False, start
=0, stop
=None):
782 islower
= self
.key().islower()
784 # `stop=None` oder `stop=0` bedeutet "keine Obergrenze".
785 stop
= stop
or len(self
)
786 for i
in range(start
, stop
):
790 if islower
!= o_i
.islower():
791 o_i
= toggle_case(o_i
)
799 if s_i
.lower() == o_i
.lower():
800 self
[i
] = toggle_case(o_i
)
801 elif allow_alternatives
:
802 self
[i
] = alternatives(s_i
, o_i
) #u'[%s/%s]' % (s_i, o_i)
806 if merge_comments
and not conflict
and self
.comment
!= other
.comment
:
807 if allow_alternatives
and self
.comment
and other
.comment
:
808 self
.comment
+= u
' / ' + other
.comment
810 self
.comment
= self
.comment
or other
.comment
813 raise AssertionError(u
'Merge Error:\n %s\n %s'
814 % (unicode(self
), unicode(other
)))
824 # Teste Felder auf Konsistenz mit den Regeländerungen der Orthographiereform
825 # 1996, ändere Unstimmigkeiten `in place`.
827 # Die neuere Funktion `ableitung1901()`_, deckt mehr Ausnahmen ab aber
828 # funktioniert nur in eine Richtung.
830 # Falls ein generisches Feld von Änderung betroffen ist, schreibe die
831 # korrekten Trennungen in die spezifischen Felder.
833 # >>> entry = WordEntry(u'Würste;Wür-ste')
834 # >>> entry.regelaenderungen()
835 # >>> print unicode(entry)
836 # Würste;-2-;Wür-ste;Würs-te
837 # >>> entry = WordEntry(u'Würste;Würs-te')
838 # >>> entry.regelaenderungen()
839 # >>> print unicode(entry)
840 # Würste;-2-;Wür-ste;Würs-te
841 # >>> entry = WordEntry(u'Hecke;He-cke')
842 # >>> entry.regelaenderungen()
843 # >>> print unicode(entry)
844 # Hecke;-2-;He{ck/k-k}e;He-cke
845 # >>> entry = WordEntry(u'Ligusterhecke;Ligu-ster=he{ck/k-k}e')
846 # >>> entry.regelaenderungen()
847 # >>> print unicode(entry)
848 # Ligusterhecke;-2-;Ligu-ster=he{ck/k-k}e;Ligus-ter=he-cke
850 # Bei Änderungen der Schreibung wird das entsprechende Feld ausgekreuzt:
852 # >>> entry = WordEntry(u'Hass;Hass')
853 # >>> entry.regelaenderungen()
854 # >>> print unicode(entry)
855 # Hass;-2-;-3-;Hass;Hass
856 # >>> entry = WordEntry(u'fasst;fasst')
857 # >>> entry.regelaenderungen()
858 # >>> print unicode(entry)
859 # fasst;-2-;-3-;fasst;fasst
860 # >>> entry = WordEntry(u'Missbrauch;Miss<brauch')
861 # >>> entry.regelaenderungen()
862 # >>> print unicode(entry)
863 # Missbrauch;-2-;-3-;Miss<brauch;Miss<brauch
864 # >>> entry = WordEntry(u'schlifffest;schliff=fest')
865 # >>> entry.regelaenderungen()
866 # >>> print unicode(entry)
867 # schlifffest;-2-;-3-;schliff=fest
869 # Bei gleicher, nicht betroffener Trennung werden Felder zusammengefasst:
871 # >>> entry = WordEntry(u'austoben;-2-;aus<to-ben;aus<to-ben')
872 # >>> entry.regelaenderungen()
873 # >>> print unicode(entry)
874 # austoben;aus<to-ben
876 # Achtung: nicht narrensicher -- Ausnahmen und Mehrdeutigkeiten werden nicht
879 # >>> entry = WordEntry(u'Boss;Boss # engl.') # korrekt
880 # >>> entry.regelaenderungen()
881 # >>> print unicode(entry)
884 # >>> entry = WordEntry(u'Ästhesie;Äs-the-sie') # Trennung von "sth" erlaubt
885 # >>> entry.regelaenderungen()
886 # >>> print unicode(entry)
887 # Ästhesie;Äs-the-sie
889 # >>> entry = WordEntry(u'Fluß;Fluß') # in de-1996 nur Fluss
890 # >>> entry.regelaenderungen()
891 # >>> print unicode(entry)
896 def regelaenderungen(self
):
897 wort01
= self
.get('de-1901')
898 wort96
= self
.get('de-1996')
901 if wort01
is None or wort96
is None or 'engl.' in self
.comment
:
904 # Trennregeländerungen:
907 wort01
= wort01
.replace(u
'-ck', u
'{ck/k-k}')
908 wort96
= wort96
.replace(u
'{ck/k-k}', u
'-ck')
911 wort01
= re
.sub(u
'(?<!s)s-t(?!h)', u
'-st', wort01
)
912 wort96
= wort96
.replace(u
'-st', u
's-t')
914 # kein Schluss-ss und sst in de-1901 (ungetrenntes "ss" nur in Ausnahmen)
915 # aber: 'ßt' und Schluß-ß auch in de-1996 möglich (langer Vokal)
920 # Dreikonsonantenregel:
921 if wort01
and re
.search(ur
'(.)\1=\1', wort01
):
925 if wort01
== wort96
: # keine Regeländerung im Wort
931 self
.extend( ['']*(4-len(self
)) )
936 self
.extend( ['']*(4-len(self
)) )
941 self
.append(w_versal
)
947 # Klasse für Einträge (Zeilen) der Wortlisten im `5-Felder-Format`_ (Kurzform).
951 class ShortEntry(WordEntry
):
952 """Entry of the German hyphenation database file (5 fields)."""
959 # Ein vollständiger Eintrag enthält fünf, durch Semikolon getrennte, Felder
960 # für die Sprachvarianten `de`, `de-1901`, `de-CH`, `de-1901-x-versal` und
961 # `de-CH-1901` (Tags nach [BCP47]_).
963 # Jedes Feld enthält ein Wort mit Kennzeichnung der Trennstellen im Format
964 # der `Wortliste der deutschsprachigen Trennmustermannschaft`_, z.B.
965 # ``Pro<zent=zah-len``.
967 # Felder können weggelassen werden, wenn sich der Inhalt aus einem
968 # allgemeineren Feld gewinnen lässt (siehe `Ersatzregeln`_).
974 # Die Felder beschreiben die Schreibung und Trennung eines Wortes gemäß
975 # der Sprachvarianten:
977 # 1. `de`: `aktuelle Rechtschreibung`_ wie sie in Deutschland und Österreich
980 # Beispiele: ``Diens-te``, ``ba-cken``, ``Grü-ße``, ``Schluss=satz``
982 # 2. `de-1901`: `traditionelle Rechtschreibung`_ wie sie in Deutschland und
983 # Österreich von 1901 bis 1996 gültig war.
985 # Beispiele: ``Dien-ste``, ``ba{ck/k-k}en``, ``Grü-ße``, ``Schluß=satz``
987 # 3. `de-CH` oder `de-x-versal`: aktuelle Rechtschreibung wie sie in der
988 # Schweiz und bei ß-Ersatzschreibung angewendet wird.
990 # Beispiele: ``ba-cken``, ``Grüs-se``, ``Schluss=satz``
992 # 4. `de-1901-x-versal`: traditionelle Rechtschreibung mit
993 # ß-Ersatzschreibung wie sie in Deutschland und Österreich
994 # angewendet wurde (keine Trennung von „ss“ als Ersatz für „ß“).
996 # Beispiele: ``ba{ck/k-k}en``, ``Grü-sse``, ``Schluss=satz``
998 # 5. `de-CH-1901`: traditionelle Rechtschreibung wie sie in der Schweiz
999 # angewendet wurde (Trennung von „ss“ auch wenn es für „ß“ steht aber
1000 # keine Dreikonsonantenregel für „ss=s“).
1002 # Beispiele: ``ba{ck/k-k}en``, ``Grüs-se``, ``Schluss=satz``
1008 # Feld 1 (`de`) ist ein Pflichtfeld, die anderen Felder können weggelassen
1009 # werden, wenn sich der Inhalt über regelmäßige Transformationen_ aus einem
1010 # anderen Feld gewinnen lässt:
1012 # ==================== ============= =======================
1013 # Feld Quelle Transformation
1014 # ==================== ============= =======================
1016 # 2 de-1901 1 de „Rechtschreibreversion_“
1017 # 3 de-CH 1 de SZ-Ersatz_
1018 # 4 de-1901-x-versal 2 de-1901 SZ-Ersatz_
1019 # 5 de-CH-1901 2 de-1901 SZ-Ersatz_
1020 # ==================== ============= =======================
1022 # Die Ersetzung erfolgt rekursiv (d.h. wenn Feld 2 nicht gegeben ist, können
1023 # die Felder 4 und 5 aus Feld 1 mit „Rechtschreibreversion_“ und SZ-Ersatz_
1026 # Wenn ein Eintrag in einer Sprachvariante leer bleiben soll, muss das
1027 # zugehörige Feld entsprechend markiert („ausgekreuzt“) werden.
1028 # (Da die ß-Ersatzschreibung für alle Wörter definiert ist, ist in einer
1029 # vollständigen Liste das Auskreuzen der Spalten 3…5 nicht erforderlich.)
1031 # Beispiele: ``-1-;de<pla-ziert``, ``auf<wän-dig;-2-;``.
1034 # Implementiert in `ShortEntry.getitem()`_.
1037 # Rechtschreibreversion
1038 # """""""""""""""""""""
1040 # Überführung eines Wortes in die Sprachvariante de-1901 durch
1041 # Anwendung der mit der Rechtschreibreform 1996 entfallenen Regeln:
1044 # Trenne nie "st". (Aber "s-th" ist trennbar.)
1046 # Trenne "ck" als "k-k".
1048 # "ß", "ßt" und "ßl" statt "ss", "sst" und "ssl" am Silbenende.
1049 # Dreikonsonantenregel_:
1050 # Von drei gleichen Konsonanten vor einem Selbstlaut entfällt einer,
1051 # taucht aber bei Worttrennung wieder auf.
1053 # Abweichende Änderungen müssen als Ausnahmen explizit erfasst werden.
1055 # Implementiert in `ableitung1901()`_.
1059 # Ersetze 'ß' mit 'ss', trenne je nach Sprachvarietät (siehe Feldbelegung_).
1061 # Implementiert in `versalschreibung()`_.
1068 # Durch die Ersatzregeln reicht für die meisten Einträge die Angabe des ersten
1071 # >>> from wortliste import ShortEntry
1072 # >>> entry = ShortEntry(u'Pass=stra-ße')
1074 # Eintrag vervollständigen mit `ShortEntry.complete()`_:
1076 # >>> entry.complete(); print unicode(entry)
1077 # Pass=stra-ße;Paß=stra-ße;Pass=stras-se;Pass=stra-sse;Pass=stras-se
1079 # Eintrag kürzen mit `ShortEntry.prune()`_:
1081 # >>> entry.prune(); print unicode(entry)
1084 # Wort mit Unregelmäßigkeiten:
1086 # >>> entry = ShortEntry(u'Boss;Boss;Boss;Boss;Boss # en.')
1087 # >>> entry.prune(); print unicode(entry)
1097 # Tupel der Sprachbezeichner für die Felder im 5-Felder-Format_::
1099 feldnamen
= ("de", "de-1901", "de-CH", "de-1901-x-versal", "de-CH-1901")
1104 # Zuordnung von Sprachbezeichnern nach [BCP47]_ zu den Feldern.
1106 # In Python beginnt die Indexzählung mit Null::
1109 "de": 0, # Deutsch, aktuell
1112 "de-1996": 0, # Alias
1113 "de-1996": 0, # Alias
1114 "de-DE-1996": 0, # Alias
1115 "de-AT-1996": 0, # Alias
1116 "de-1901": 1, # traditionell (Reform 1901)
1117 "de-CH": 2, # ohne ß (Schweiz/versal) aktuell
1118 "de-x-versal": 2, # Alias
1119 "de-1996-x-versal": 2, # Alias
1120 "de-1901-x-versal": 3, # ohne ß (versal) traditionell
1121 "de-CH-1901": 4, # ohne ß (Schweiz) traditionell
1124 # Achtung: die Sprachtags `de` und `de-CH` sind in der Kurzform_ ein Alias für
1125 # die aktuelle Rechtschreibung aber in der Langform_ Bezeichner für separate
1126 # Felder mit allgemeingültigen Trennungen:
1128 # >>> print ShortEntry.feldindizes['de-1901'], ShortEntry.feldindizes['de-CH']
1130 # >>> print ShortEntry.feldindizes['de'], ShortEntry.feldindizes['de-1996']
1133 # >>> print WordEntry.feldindizes['de'], WordEntry.feldindizes['de-1996']
1146 # Das Argument `line` ist eine Zeichenkette (`string`, `unicode`) im
1147 # `5-Felder-Format`_ oder eine Instanz der Klasse WordEntry_.
1149 # >>> print ShortEntry(u'Diens-te')
1151 # >>> tpp = ShortEntry(u'Ur<laubs=tipp;-2-')
1155 # >>> print abenddienste
1156 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
1157 # >>> print ShortEntry(abenddienste)
1160 # Achtung: Ein Lang-Eintrag mit nur zwei Feldern kann unregelmäßig sein und
1161 # daher im Kurzformat mehrere Felder benötigen (siehe Tests)
1164 def __init__(self
, line
, delimiter
=';', prune
=True):
1166 if isinstance(line
, WordEntry
):
1167 self
.comment
= line
.comment
# Kommentar
1168 self
._key
= line
.getitem(0) # Schlüssel cachen
1171 self
.append(line
.getitem(3)) # Deutsch, aktuell (Reform 1996)
1172 self
.append(line
.getitem(2)) # "traditionell" (Reform 1901)
1174 self
.append(line
.getitem(6)) # ohne ß (Schweiz oder versal) "aktuell"
1175 self
.append(line
.getitem(5)) # ohne ß (Schweiz oder versal) "traditionell"
1176 self
.append(line
.getitem(7)) # ohne ß (Schweiz) "traditionell" ("süssauer")
1177 elif u
'ß' in self
._key
: # auskreuzen
1182 WordEntry
.__init
__(self
, line
, delimiter
)
1184 if prune
: # Felder zusammenfassen
1189 # >>> drs = unicode(ShortEntry(u'-1-;Dreß'))
1192 # >>> print ShortEntry(u'# Testkommentar')
1195 # In der Voreinstellung werden optionale Felder mit `prune()` gekürzt.
1196 # Mit der Option `prune=False` bleiben alle übergebenen Felder erhalten:
1198 # >>> print unicode(ShortEntry(u'Fluss;Fluß'))
1200 # >>> print unicode(ShortEntry(u'Fluss;Fluß', prune=False))
1203 # Wird dem Konstruktor eine WordEntry_-Instanz übergeben, so wird diese in das
1204 # Kurzformat überführt:
1206 # >>> print ShortEntry(WordEntry(u'heute;heu-te'))
1209 # Bei Ausnahmen von der regelmäßigen Worttrennung werden bei Bedarf
1210 # zusätzliche Spalten belegt:
1212 # >>> print ShortEntry(WordEntry(u'Amnesty;Am-nes-ty # en.'))
1213 # Am-nes-ty;Am-nes-ty # en.
1214 # >>> bss = ShortEntry(WordEntry(u'Boss;Boss # engl.'))
1218 # Bei Wörtern mit unterschiedlicher Schreibung in den verschiedenen
1219 # Sprachvarianten werden Felder, die im übergebenen `WordEntry` nicht belegt
1220 # sind als leer markiert:
1222 # >>> print urlaubstipp
1223 # Urlaubstipp;-2-;-3-;Ur<laubs=tipp
1224 # >>> print ShortEntry(urlaubstipp)
1226 # >>> print unicode(ShortEntry(WordEntry(u'Abfalllager;-2-;-3-;Ab<fall=la-ger')))
1227 # Ab<fall=la-ger;-2-
1229 # >>> dresz = WordEntry(u'Dreß;-2-;Dreß;-4-')
1230 # >>> print unicode(ShortEntry(dresz))
1231 # -1-;Dreß;-3-;-4-;-5-
1232 # >>> g_ebene = WordEntry(u'Gaußebene;Gauß=ebe-ne')
1233 # >>> print unicode(ShortEntry(g_ebene))
1234 # Gauß=ebe-ne;Gauß=ebe-ne;-3-;-4-;-5-
1235 # >>> print auffrass
1236 # auffrass;-2-;-3-;-4-;auf-frass
1237 # >>> print ShortEntry(auffrass)
1238 # -1-;-2-;auf-frass;auf-frass;auf-frass
1239 # >>> fraesse = WordEntry(u'frässe;-2-;-3-;-4-;-5-;frä-sse;fräs-se;fräs-se')
1240 # >>> frs = ShortEntry(fraesse)
1241 # >>> print unicode(frs)
1242 # -1-;-2-;fräs-se;frä-sse;fräs-se
1243 # >>> loesz = WordEntry(u'Lößboden;Löß=bo-den')
1244 # >>> print unicode(ShortEntry(loesz))
1245 # Löß=bo-den;Löß=bo-den;-3-;-4-;-5-
1246 # >>> loess = WordEntry(u'Lössboden;-2-;-3-;Löss=bo-den;Löss=bo-den')
1247 # >>> print unicode(ShortEntry(loess))
1248 # Löss=bo-den;-2-;Löss=bo-den;Löss=bo-den;Löss=bo-den
1250 # >>> print ShortEntry(WordEntry(u'Fussballliga;-2-;-3-;-4-;-5-;-6-;Fuss=ball==li-ga'))
1251 # -1-;-2-;Fuss=ball==li-ga
1252 # >>> print ShortEntry(WordEntry(u'Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga;-7-'))
1253 # -1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga
1254 # >>> messignal = WordEntry(u'Messignal;-2-;-3-;-4-;-5-;-6-;-7-;Me{ss/ss=s}i-.gnal')
1255 # >>> print unicode(ShortEntry(messignal))
1256 # -1-;-2-;-3-;-4-;Me{ss/ss=s}i-.gnal
1258 # >>> ShortEntry(WordEntry(u'# nur Kommentar'))
1259 # ShortEntry(u'# nur Kommentar')
1265 # Gib einen Schlüssel (ungetrenntes Wort) zurück.
1267 # Bei Einträgen im Kurzformat ist der Schlüssel nicht eindeutig: durch
1268 # Transformationen_ kann ein Wort in verschiedenen Schreibungen vorkommen.
1270 # Standardmäßig wird das erste nichtleere Feld ohne Trennzeichen verwendet:
1272 # >>> print ShortEntry(u'Fluss;Fluß').key()
1274 # >>> print ShortEntry(u'-1-;Dreß').key()
1277 # Die Auswahl kann über das `lang` Argument gesteuert werden:
1279 # >>> print ShortEntry(u'-1-;Dreß').key('de-CH-1901')
1282 # Der Schlüssel eines leeren Eintrags ist ein leerer String, der eines leeren
1283 # Kommenars das Kommentarzeichen:
1285 # >>> ShortEntry(u'').key(), ShortEntry(u'# toller Kommentar').key()
1290 def key(self
, lang
=None):
1291 """Erstelle einen Schlüssel (ungetrenntes Wort)."""
1293 return join_word(self
.get(lang
))
1301 return u
'#' # reiner Kommentar
1302 return u
'' # leerer Eintrag
1305 # .. _`ShortEntry.getitem()`:
1310 # Gib Feld `i` zurück. Wende bei Bedarf die Ersatzregeln_ an.
1312 # >>> entry = ShortEntry(u'Bu-ße')
1313 # >>> print entry.getitem(1), entry.getitem(2), entry.getitem(4)
1314 # Bu-ße Bus-se Bus-se
1316 # >>> entry.getitem(5)
1317 # Traceback (most recent call last):
1319 # IndexError: list index out of range
1321 # Mit `subsititute=True` wird der Feldinhalt ignoriert und immer nach
1322 # Ersatzregel bestimmt:
1324 # >>> entry = ShortEntry(u'Bu-ße;Reue;Bu-sse')
1325 # >>> print entry.getitem(1), entry.getitem(1, substitute=True)
1331 def getitem(self
, i
, substitute
=False):
1332 """Return item ``i`` or a subsititute"""
1336 except IndexError: # Feld i nicht vorhanden
1338 raise ValueError('leerer Eintrag')
1340 raise # maximal 5 Felder im Kurzformat
1342 # Rekursion: wähle das generischere Feld
1343 ersatzfelder
= (None, 0, 0, 1, 1)
1344 word
= self
.getitem(ersatzfelder
[i
])
1346 # Bei leerem Feld ("ausgekreuzt") ist keine Transformation nötig
1350 # Rechschreibreform (s-t, ck, Dreikonsonantenregel)
1351 if i
== 1: # de-1901
1352 return ableitung1901(word
)
1354 # Versalschreibung: ß -> ss
1356 word
= versalschreibung(word
, self
.feldnamen
[i
])
1363 # Gib Trennmuster für Sprachvariante zurück (ggf. über Transformationen_).
1367 # ohne Transformation
1369 # >>> aalbst = ShortEntry(u'Aal=be<stand # Test')
1370 # >>> aalbst.get('de')
1372 # >>> aalbst.get('de-1996')
1374 # >>> aalbst.get('de-1901')
1376 # >>> aalbst.get('de-x-versal')
1378 # >>> aalbst.get('de-1901-x-versal')
1380 # >>> aalbst.get('de-1996-x-versal')
1382 # >>> aalbst.get('de-CH-1901')
1387 # >>> dst = ShortEntry(u'Diens-te')
1388 # >>> print dst.get('de'), dst.get('de-1901'), dst.get('de-1996')
1389 # Diens-te Dien-ste Diens-te
1390 # >>> print ShortEntry(u'Es-te').get('de-1901')
1392 # >>> sck = ShortEntry(u'Stre-cke')
1393 # >>> print sck.get('de'), sck.get('de-1901')
1394 # Stre-cke Stre{ck/k-k}e
1398 # >>> abus = ShortEntry(u'ab<bü-ßen')
1399 # >>> print abus.get('de'), abus.get('de-CH')
1400 # ab<bü-ßen ab<büs-sen
1401 # >>> print abus.get('de-1901-x-versal'), abus.get('de-CH-1901')
1402 # ab<bü-ssen ab<büs-sen
1404 # >>> print ShortEntry(u'passt').get('de-1901')
1406 # >>> print ShortEntry(u'passt').get('de-1901-x-versal')
1408 # >>> rs = ShortEntry(u'-1-;-2-;Russ') # versal für Ruß
1409 # >>> print rs.get('de-x-versal')
1411 # >>> rs.get('de-1901-x-versal')
1414 # >>> entry = ShortEntry(u'süß=sau-er')
1415 # >>> print entry.get('de-CH'), entry.get('de-1901-x-versal'), entry.get('de-CH-1901')
1416 # süss=sau-er süss=sau-er süss=sau-er
1418 # >>> ShortEntry(u'Pro<zess=en-.de').get('de-CH-1901')
1419 # u'Pro<zess=en-.de'
1420 # >>> pstr = ShortEntry(u'Pass=stra-ße')
1421 # >>> print pstr.get('de-1996'), pstr.get('de-1901')
1422 # Pass=stra-ße Paß=stra-ße
1423 # >>> print pstr.get('de-x-versal'), pstr.get('de-1901-x-versal')
1424 # Pass=stras-se Pass=stra-sse
1426 # Test: Wenn keine Ersatzschreibung vorliegt, wird auch in traditioneller
1427 # Versalschreibung s-s getrennt:
1429 # >>> print ShortEntry(u'Bu-ße').get('de-1901-x-versal')
1431 # >>> print ShortEntry(u'Bus-se').get('de-1901-x-versal')
1434 # Geerbt von `WordEntry.get()`_.
1436 # .. _ShortEntry.complete():
1441 # Eintrag vervollständigen (alle 5 Felder ausfüllen).
1443 # >>> dst.complete()
1445 # Diens-te;Dien-ste;Diens-te;Dien-ste;Dien-ste
1446 # >>> aalbst.complete()
1448 # Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand # Test
1449 # >>> abus.complete()
1450 # >>> print unicode(abus)
1451 # ab<bü-ßen;ab<bü-ßen;ab<büs-sen;ab<bü-ssen;ab<büs-sen
1452 # >>> bss.complete()
1453 # >>> print unicode(bss)
1454 # Boss;Boss;Boss;Boss;Boss # engl.
1455 # >>> frs.complete()
1456 # >>> print unicode(frs)
1457 # -1-;-2-;fräs-se;frä-sse;fräs-se
1458 # >>> tpp.complete()
1460 # Ur<laubs=tipp;-2-;Ur<laubs=tipp;-4-;-5-
1462 # >>> entry = ShortEntry(u'# toller Hecht')
1463 # >>> entry.complete()
1464 # >>> print unicode(entry)
1470 for i
in range(len(self
), 5):
1472 field
= self
.getitem(i
)
1473 except ValueError: # leerer Eintrag
1481 # Gib eine vervollständigte Kopie des Eintrags zurück.
1483 # >>> entry = ShortEntry(u'-1-;-2-;sties-se').completed()
1485 # ShortEntry(u'-1-;-2-;sties-se;-4-;-5-')
1486 # >>> [field for field in entry]
1487 # [u'', u'', u'sties-se', u'', u'']
1488 # >>> print unicode(pstr.completed())
1489 # Pass=stra-ße;Paß=stra-ße;Pass=stras-se;Pass=stra-sse;Pass=stras-se
1491 # Geerbt von `WordEntry.completed()`_.
1499 # Felder weglassen, wenn sich der Inhalt durch Ersatzregeln_ gewinnen läßt:
1501 # >>> aalbst.prune()
1503 # Aal=be<stand # Test
1508 # >>> print unicode(abus)
1511 # >>> print unicode(bss)
1517 # Auch das „Auskreuzen“ wird weitergereicht:
1519 # Wenn ein Wort in "traditioneller" Rechtschreibung nicht existiert, reicht
1520 # das "Auskreuzen" von Feld 2:
1522 # >>> entry = ShortEntry(u'Tipp;-2-;Tipp;-4-;-5-')
1527 # Wenn ein Wort in aktueller Rechtschreibung nicht existiert, reicht das
1528 # "Auskreuzen" von Feld 1:
1530 # >>> entry = ShortEntry(u'-1-;Rauh=nacht;-3-;Rauh=nacht')
1535 # Felder werden nicht gekürzt, wenn die Rekonstruktion einen anderen Wert
1539 # >>> print unicode(frs)
1540 # -1-;-2-;fräs-se;frä-sse;fräs-se
1542 # >>> entry = ShortEntry(u'-1-;Dreß;-3-;-4-;-5-')
1544 # >>> print unicode(entry)
1545 # -1-;Dreß;-3-;-4-;-5-
1547 # Mit ``drop_sz=True`` werden die drei letzten Felder (ß-Ersatzschreibung)
1550 # * ß-Ersatzschreibung ist immer definiert, daher kann eigentlich nichts falsch
1553 # * Die "nachlässige" Wandlung garantiert nicht die exakte Reproduktion der
1554 # Ausgangsliste nach einem "Rundtrip":
1556 # Mit `ShortEntry.complete()`_ werden die letzten Spalten ausgefüllt, auch
1557 # wenn sie im Original "ausgekreuzt" waren.
1559 # Bei Wandlung ins Langformat können zusätzliche Einträge mit
1560 # ß-Ersatzschreibung entstehen.
1562 # Die Auszeichnung von Mehrdeutigkeiten (z.B. „Mas-se/Ma-sse“ in
1563 # de-1901-x-versal) geht verloren.
1565 # >>> entry.prune(drop_sz=True)
1566 # >>> print unicode(entry)
1568 # >>> entry.complete()
1569 # >>> print unicode(entry)
1570 # -1-;Dreß;-3-;Dress;Dress
1574 def prune(self
, drop_sz
=False):
1575 if len(self
) == 1: # bereits kompakt
1578 while len(self
) > 2:
1580 for i
in range(len(self
)-1, 0, -1):
1582 rekonstruktion
= self
.getitem(i
)
1583 if wort
!= rekonstruktion
:
1584 # print tag, repr(self), wort, rekonstruktion
1587 if len(self
) == 1 and not self
[0]:
1594 # Einträge zusammenlegen.
1596 # Das als Argument übergebene WordEntry Objekt wird in dem eigenen Eintrag
1599 # Leere („ausgekreuzte“) Felder werden überschrieben:
1601 # >>> entry = ShortEntry(u'be<wusst;-2-')
1602 # >>> entry.merge(ShortEntry(u'-1-;be<wußt'))
1603 # >>> print unicode(entry)
1606 # Identische Felder bleiben erhalten. Alle Felder die über Transformationen_
1607 # rekonstruiert werden können werden entfernt:
1609 # >>> entry = ShortEntry(u'Maß=nah-me # < nehmen')
1610 # >>> entry.merge(ShortEntry(u'-1-;-2-;Mass=nah-me'))
1611 # >>> print unicode(entry)
1612 # Maß=nah-me # < nehmen
1613 # >>> entry = ShortEntry(u'-1-;-2-;Mass=nah-me')
1614 # >>> entry.merge(ShortEntry(u'Maß=nah-me # < nehmen'), merge_comments=True)
1615 # >>> print unicode(entry)
1616 # Maß=nah-me # < nehmen
1618 # Es sei den die Option ``prune`` ist ``False``:
1620 # >>> entry.merge(ShortEntry(u'Maß=nah-me'), prune=False)
1621 # >>> print unicode(entry)
1622 # Maß=nah-me;Maß=nah-me;Mass=nah-me;Mass=nah-me;Mass=nah-me # < nehmen
1624 # Bei Konflikten wird ein AssertionError erzeugt:
1626 # >>> entry = ShortEntry(u'be<wusst;-2-')
1627 # >>> entry.merge(ShortEntry(u'-1-;-2-;ver<bor-gen'))
1628 # Traceback (most recent call last):
1630 # AssertionError: Merge Error:
1631 # be<wusst;-2-;be<wusst;-4-;-5-
1632 # -1-;-2-;ver<bor-gen;-4-;-5-
1634 # Geerbt von `WordEntry.merge()`_
1640 # Gib eine Liste von WordEntry_ Instanzen (8-Spalten-Format_) zurück.
1642 # >>> mse = ShortEntry(u'Mas-se')
1643 # >>> mse.wordentries()
1644 # [WordEntry(u'Masse;Mas-se')]
1646 # Im Langformat gibt es für jede Schreibung einen separaten Eintrag:
1648 # >>> mas = ShortEntry(u'Ma-ße')
1649 # >>> for e in mas.wordentries():
1650 # ... print unicode(e)
1652 # Masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se
1654 # >>> entry = ShortEntry(u'Löss')
1655 # >>> for e in entry.wordentries():
1656 # ... print unicode(e)
1657 # Löss;-2-;-3-;Löss;Löss
1662 def wordentries(self
, prune
=True):
1665 if self
.comment
: # leerer Kommentar
1666 return [WordEntry(u
'# '+self
.comment
)]
1667 return [WordEntry(u
'')]
1669 entries
= [] # liste für WordEntry Einträge (Rückgabeobjekt)
1670 words
= {} # dictionary für WordEntry Einträge
1671 # Zuordnung der Indizes: ShortEntry[s] == WordEntry[l]
1672 indices
= (3, 2, 6, 5, 7)
1674 for s
,l
in enumerate(indices
):
1675 word
= self
.getitem(s
)
1677 continue # Leerfelder überspringen
1679 key
= join_word(word
)
1680 # WordEntry Instanz heraussuchen oder erzeugen:
1684 entry
= WordEntry(key
)
1685 entry
.comment
= self
.comment
1687 entries
.append(entry
)
1688 # Eintrag in entry[j]:
1689 entry
.setitem(l
, word
)
1691 # Auffüllen und Komprimieren
1692 for entry
in entries
:
1693 while len(entry
) < 8:
1700 # Hilfsfunktion für Tests:
1702 # >>> def print_langform(line, prune=True):
1703 # ... for e in ShortEntry(line).wordentries(prune):
1704 # ... print unicode(e)
1708 # >>> print_langform(u'ba-den')
1710 # >>> print_langform(u'Wes-te')
1711 # Weste;-2-;We-ste;Wes-te
1712 # >>> print_langform(u'Toll-patsch;-2-')
1713 # Tollpatsch;-2-;-3-;Toll-patsch
1714 # >>> print_langform(u'-1-;rau-he')
1715 # rauhe;-2-;rau-he;-4-
1717 # Unterschiedliche Schreibungen:
1719 # >>> print_langform(u'Ab<fall=la-ger # Rechtschreibänderung')
1720 # Abfalllager;-2-;-3-;Ab<fall=la-ger # Rechtschreibänderung
1721 # Abfallager;-2-;Ab<fa{ll/ll=l}a-.ger;-4- # Rechtschreibänderung
1723 # >>> print_langform(u'Ab<guss')
1724 # Abguss;-2-;-3-;Ab<guss;Ab<guss
1725 # Abguß;-2-;Ab<guß;-4-
1727 # >>> print_langform(u'groß')
1729 # gross;-2-;-3-;-4-;gross
1731 # >>> print_langform(u'gro-ßen')
1733 # grossen;-2-;-3-;-4-;-5-;gro-ssen;gros-sen;gros-sen
1735 # >>> print_langform(u'Spaß')
1737 # Spass;-2-;-3-;-4-;Spass
1738 # >>> print_langform(u'Spass')
1739 # Spass;-2-;-3-;Spass;Spass
1741 # >>> print_langform(u'spa-ßen')
1743 # spassen;-2-;-3-;-4-;-5-;spa-ssen;spas-sen;spas-sen
1745 # >>> print_langform(u'ab<bü-ßen')
1747 # abbüssen;-2-;-3-;-4-;-5-;ab<bü-ssen;ab<büs-sen;ab<büs-sen
1749 # >>> print_langform(u'Biss')
1750 # Biss;-2-;-3-;Biss;Biss
1752 # >>> print_langform(u'Boss;Boss # engl.')
1755 # >>> print_langform(u'Pass=sys-tem')
1756 # Passsystem;-2-;-3-;Pass=sys-tem;-5-;Pass=sy-stem;Pass=sys-tem
1757 # Paßsystem;-2-;Paß=sy-stem;-4-
1759 # >>> print_langform(u'Press=saft')
1760 # Presssaft;-2-;-3-;Press=saft;Press=saft
1761 # Preßsaft;-2-;Preß=saft;-4-
1763 # >>> print_langform(u'Pro<gramm==maß=nah-me')
1764 # Programmmaßnahme;-2-;-3-;Pro<gramm==maß=nah-me
1765 # Programmaßnahme;-2-;Pro<gra{mm/mm==m}aß=nah-me;-4-
1766 # Programmmassnahme;-2-;-3-;-4-;-5-;-6-;Pro<gramm==mass=nah-me
1767 # Programmassnahme;-2-;-3-;-4-;-5-;Pro<gra{mm/mm==m}ass=nah-me;-7-
1769 # >>> print_langform(u'Pass=stra-ße')
1770 # Passstraße;-2-;-3-;Pass=stra-ße
1771 # Paßstraße;-2-;Paß=stra-ße;-4-
1772 # Passstrasse;-2-;-3-;-4-;-5-;Pass=stra-sse;Pass=stras-se;Pass=stras-se
1774 # >>> print_langform(u'Pro<zess=en-.de;Pro<zeß=en-de;Pro<zess=en-.de')
1775 # Prozessende;-2-;-3-;Pro<zess=en-.de;Pro<zess=en-.de
1776 # Prozeßende;-2-;Pro<zeß=en-de;-4-
1778 # Explizit nur eine Schreibung:
1780 # >>> print_langform(u'Abend=dress;Abend=dress')
1781 # Abenddress;Abend=dress
1782 # >>> print_langform(u'-1-;Abend=dreß')
1783 # Abenddreß;-2-;Abend=dreß;-4-
1784 # Abenddress;-2-;-3-;-4-;-5-;Abend=dress;-7-
1786 # Nur Versalschreibung:
1788 # >>> print_langform(u'-1-;-2-;Fuss;Fuss;Fuss')
1789 # Fuss;-2-;-3-;-4-;Fuss
1790 # >>> line = u'Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga'
1791 # >>> print ShortEntry(WordEntry(line), prune=False)
1792 # -1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga
1793 # >>> print_langform(u'-1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga')
1794 # Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga;-7-
1795 # >>> print_langform(u'-1-;-2-;Fuss=ball==li-ga')
1796 # Fussballliga;-2-;-3-;-4-;-5-;-6-;Fuss=ball==li-ga
1800 # >>> print_langform(u'# holla')
1810 # Ableitung von de-1901 aus de-1996 (Reversion der Reform 1996).
1812 # Mit keep_key == True werden nur Änderungen vorgenommen, die die
1813 # Schreibung des (ungetrennten) Wortes nicht verändern.
1815 # >>> from wortliste import ableitung1901
1819 def ableitung1901(wort
, keep_key
=False):
1820 """Reverse regular changes of the 1996 orthography reform."""
1823 # Trennregeländerungen
1824 # ~~~~~~~~~~~~~~~~~~~~
1826 # Diese Regeln ändern nicht das Wort, nur die Trennmöglichkeiten.
1828 # Alternativtrennungen auswählen::
1830 wort
= fremdwortsilben(wort
)
1831 wort
= verblasst(wort
)
1836 # K75: Trenne nie st.
1838 # Ersetze 's-t' mit '-st':
1840 # >>> ableitung1901(u'Diens-te')
1842 # >>> print ableitung1901(u'wuss-te', keep_key=True)
1845 # Aber Trennung von s-theta erlaubt (nach K74):
1847 # >>> print ableitung1901(u'Äs-thet')
1852 wort
= re
.sub(u
'(?<!s)s-t(?!h)', u
'-st', wort
)
1858 # K76: Trenne 'ck' als 'k-k'.
1860 # Ersetze '-ck' mit '{ck/k-k}':
1862 # >>> ableitung1901(u'Ha-cke')
1864 # >>> ableitung1901(u'Acker')
1866 # >>> ableitung1901(u'Got-tes=acker')
1867 # u'Got-tes=a{ck/k-k}er'
1868 # >>> ableitung1901(u'Aal=beck')
1870 # >>> ableitung1901(u'be<spick-te')
1875 wort
= wort
.replace(u
'-ck', u
'{ck/k-k}')
1876 wort
= re
.sub(u
'(?<=[AEIOUYÄÖÜaeiouyäöü])ck(?=[aeiouyäöü])',
1879 # Keine Trennung nach nur einem Buchstaben am Wortanfang:
1881 # >>> ableitung1901(u'Es-te')
1883 # >>> print ableitung1901(u'Nord=os-ten')
1885 # >>> print ableitung1901(u'Po-ly<es-ter')
1888 # Im Wort sind Einvokalsilben erlaubt (aber nicht immer günstig):
1890 # >>> print ableitung1901(u'the-is-tisch')
1895 wort
= re
.sub(u
'((?<=[=<].)|(?<=^.))-', ur
'', wort
)
1900 # Rechtschreibänderungen
1901 # ~~~~~~~~~~~~~~~~~~~~~~
1903 # Diese Regeln ändern die Schreibung des ungetrennten Worts (und somit den
1904 # Schlüssel im Langformat der Wortliste).
1909 # K38: Kein "ss" und "sst" am Silbenende (ungetrenntes "ss" nur in Ausnahmen)
1910 # (Andererseit ist 'ßt' und Schluss-ß auch in de-1996 möglich (langer Vokal).)
1912 # Ersetze ungetrenntes 'ss' mit 'ß':
1914 # >>> print ableitung1901(u'passt')
1916 # >>> print ableitung1901(u'Hass')
1918 # >>> print ableitung1901(u'Fass=brau-se')
1920 # >>> print ableitung1901(u'wuss-te')
1923 # ß steht für inlautendes ss, wenn ein 'e' ausfällt (und der Ausfall nicht
1924 # durch Apostroph angedeutet wird)
1926 # >>> print ableitung1901(u'wäss-rig')
1928 # >>> print ableitung1901(u'an<ge<mess-ner')
1930 # >>> print ableitung1901(u'duss-lig')
1932 # >>> print ableitung1901(u'bissl')
1935 # Keine Wandlung zu "ß":
1936 # getrenntes Doppel-s (s-s)
1938 # >>> print ableitung1901(u'Was-ser')
1941 # Vokal folgt (Fremdwörter):
1942 # >>> print ableitung1901(u'Com-tesse')
1945 # Großbuchstabe folgt
1947 # >>> print ableitung1901(u'WissZeitVG') # Abkürzung
1950 # Drei oder mehr 's' = Lautmalerei → erhalten:
1952 # >>> print ableitung1901(u'pssst')
1957 wort
= re
.sub(u
'(?<=[^s])ss(?=[^aeiouyäöüA-Zs]|$)', ur
'ß', wort
)
1959 # Unterdrückung der Trennstelle nach "…ß=er" und "…ß=en" nicht nötig:
1961 # >>> print ableitung1901(u'hass=er<.füllt')
1966 wort
= re
.sub(ur
'ß(=+)e([rn][<-]+)\.', ur
'ß\1e\2', wort
)
1968 # Dreikonsonantenregel
1969 # """"""""""""""""""""
1971 # K78: Zusammensetzungen, bei denen von drei zusammenstoßenden gleichen
1972 # Konsonanten einer entfällt (K15), schreibt man bei Silbentrennung wieder
1973 # mit allen drei Konsonanten.
1975 # Ersetze 'xx=x' xit '{xx/xx=x}' (für alle Konsonanten vor Selbstlaut)
1977 # >>> print ableitung1901(u'Kipp=pflug')
1979 # >>> print ableitung1901(u'Kipp=punkt')
1981 # >>> print ableitung1901(u'Ab<fall=la-ger')
1982 # Ab<fa{ll/ll=l}a-.ger
1983 # >>> print ableitung1901(u'All<lie-be')
1985 # >>> print ableitung1901(u'hell>licht')
1987 # >>> print ableitung1901(u'Pro<gramm==maß=nah-me')
1988 # Pro<gra{mm/mm==m}aß=nah-me
1992 wort
= re
.sub(ur
'([bfglmnprt])\1([<=>]+)\1(?=[aeiouyäöü])',
1993 ur
'{\1\1/\1\1\2\1}', wort
)
1995 # Unterdrücken der Trennung nach nur einem Buchstaben::
1997 wort
= re
.sub(ur
'(?<=[=>].}[aeiouyäöü])([-<])\.?', ur
'\1.', wort
)
2004 # Ein-Vokal-Silben auch schon 1901 erlaubt:
2006 # >>> print ableitung1901(u'ver<knäu-e-le')
2010 # versalschreibung()
2011 # ------------------
2012 # Ersetze 'ß' mit 'ss', trenne je nach Sprachvarietät `lang`:
2014 # >>> from wortliste import versalschreibung
2015 # >>> print versalschreibung(u'paßt')
2017 # >>> print versalschreibung(u'Dar-ßer')
2019 # >>> print versalschreibung(u'bü-ßen', 'de-CH')
2021 # >>> print versalschreibung(u'bü-ßen', 'de-1996-x-versal')
2023 # >>> print versalschreibung(u'bü-ßen', 'de-CH-1901')
2025 # >>> print versalschreibung(u'äßen', 'de-CH-1901')
2027 # >>> print versalschreibung(u'auf<äßen', 'de-CH-1901')
2029 # >>> print versalschreibung(u'auf<eßt', 'de-CH-1901')
2031 # >>> print versalschreibung(u'Groß=se-gel', 'de-1901-x-versal')
2033 # >>> print versalschreibung(u'Groß=se-gel', 'de-CH-1901')
2035 # >>> print versalschreibung(u'Paß=sy-ste-me', 'de-CH-1901')
2037 # >>> print versalschreibung(u'Pro<zeß=en-de', 'de-CH-1901')
2039 # >>> print versalschreibung(u'Pro<zess=en-.de', 'de-CH-1901')
2041 # >>> print versalschreibung(u'Fluß==sy-stem', 'de-CH-1901')
2043 # >>> print versalschreibung(u'Meß==sen-der', 'de-CH-1901')
2048 def versalschreibung(wort
, lang
='de'):
2050 if not u
'ß' in wort
:
2053 wort
= wort
.replace(u
'ß', u
'ss')
2055 # Trennung von Ersatz-ss in de-CH und de-1996 nach Sprechsilbenregel::
2057 if '1901-x-versal' not in lang
:
2058 # wort = re.sub(u'(?<=[aeiouyäöü])-\.?ss', u's-s', wort)
2059 wort
= re
.sub(u
'-\.?ss(?=[aeiouyäöü])', u
's-s', wort
)
2060 wort
= re
.sub(u
'(?<=^[aeiouyäöü])ss(?=[aeiouyäöü])', u
's-s', wort
)
2061 wort
= re
.sub(u
'(?<=[=<][aeiouyäöü])ss(?=[aeiouyäöü])', u
's-s', wort
)
2063 # Unterdrückung irreführender Trennung::
2065 wort
= re
.sub(u
'ss(=+)(en|er)([<-])\.?', ur
'ss\1\2\3.', wort
)
2067 # Dreikonsonantenregel für Ersatz-ss in de-CH-1901::
2069 if 'CH-1901-x-dreikonsonanten' in lang
:
2070 wort
= re
.sub(u
'ss(=+)s(?=[aeiouyäöü])', ur
'{ss/ss\1s}', wort
)
2071 # Unterdrücken der Trennung nach nur einem Buchstaben und irreführender Trennungen
2072 wort
= re
.sub(ur
'(?<=[=>]s}[aeiouyäöü])([-<])\.?', ur
'\1.', wort
)
2073 # wort = re.sub(ur'(?<===s}[aeiouyäöü])([-<])\.?', ur'\1.', wort) # Reißverschus=sy-.stem
2074 wort
= re
.sub(u
'(?<=[=>]s})(en|er)([<-])\.?', ur
'\1\2.', wort
)
2079 # Kurzformat in Langformat
2080 # ------------------------
2082 # Zusätzlich benötigte Felder werden automatisch erzeugt. Ein Kurzeintrag kann
2083 # mehrere Langeinträge ergeben:
2085 # >>> from wortliste import short2long
2086 # >>> for line in short2long([u"Diens-te", u"Ge<biss"]):
2087 # ... print unicode(line)
2088 # Dienste;-2-;Dien-ste;Diens-te
2089 # Gebiss;-2-;-3-;Ge<biss;Ge<biss
2090 # Gebiß;-2-;Ge<biß;-4-
2092 # >>> for line in short2long([u"Ge<schoss", u"Ge<schoß # österr."]):
2093 # ... print unicode(line)
2094 # Geschoss;-2-;-3-;Ge<schoss;Ge<schoss
2095 # Geschoß;Ge<schoß # österr.
2101 def short2long(lines
, sort
=True, prune
=True):
2102 """Convert sequence of lines in ShortEntry format to WordEntry instances.
2105 # Sammeln in Liste und Dictionary:
2106 words
= {} # zum Zusammenfassen
2110 shortentry
= ShortEntry(line
)
2111 shortentry
.complete()
2112 for entry
in shortentry
.wordentries(prune
=False):
2113 key
= entry
.key().lower()
2114 try: # Eintrag mit gleichem Schlüssel vorhanden?
2115 altentry
= words
[key
]
2116 except KeyError: # nein -> neuer Eintrag
2118 entries
.append(entry
)
2121 if entry
[3]: # de-1996 non-empty
2122 entry
.merge(altentry
, prune
=False, allow_alternatives
=True)
2123 # Alternativen Eintrag "in-place" ersetzen:
2124 for i
, word
in enumerate(entry
):
2126 altentry
.comment
= entry
.comment
2128 altentry
.merge(entry
, prune
=False, allow_alternatives
=True)
2129 except AssertionError as e
:
2130 sys
.stderr
.write(unicode(e
).encode('utf8')+'\n')
2131 entries
.append(entry
)
2132 except IndexError: # Leerer Eintrag (Kommentar)
2133 entries
.append(entry
)
2137 for entry
in entries
:
2141 entries
.sort(key
=sortkey_duden
)
2147 # Kommentare bleiben erhalten:
2149 # >>> for line in short2long([u'Aal=an-geln', u'# toller Kommentar'],
2151 # ... print unicode(line)
2152 # Aalangeln;Aal=an-geln
2153 # # toller Kommentar
2155 # Beim Sortieren werden Kommenare an den Beginn geschrieben.
2156 # >>> for line in short2long([u'# erster Kommentar',
2157 # ... u'Aal=an-geln',
2158 # ... u'# zweiter Kommentar']):
2159 # ... print unicode(line)
2160 # # erster Kommentar
2161 # # zweiter Kommentar
2162 # Aalangeln;Aal=an-geln
2165 # Langformat in Kurzformat
2166 # ------------------------
2168 # Optionale Felder werden weggelassen, wenn sie mit dem automatisch erzeugten
2169 # Inhalt übereinstimmen:
2171 # >>> from wortliste import long2short
2172 # >>> for entry in long2short([u"Dienste;-2-;Dien-ste;Diens-te"]):
2173 # ... print unicode(entry)
2176 # Zusammengehörige Einträge werden zusammengefasst:
2178 # >>> for line in long2short([u"Großanlass;-2-;-3-;Groß=an<lass",
2179 # ... u"Großanlaß;-2-;Groß=an<laß;-4-",
2180 # ... u"Grossanlass;-2-;-3-;-4-;Gross=an<lass"]):
2181 # ... print unicode(line)
2187 # Bei einigen Wörtern ist die ß-ss-Beziehung nicht eindeutig:
2189 # ======= ======== =========
2190 # de1901 de-1996 de-CH
2191 # ======= ======== =========
2194 # Geschoß Geschoß Geschoss
2195 # Geschoß Geschoss Geschoss
2196 # ======= ======== =========
2198 # Daher kann es vorkommen, dass ein Langform-Eintrag Beiträge von
2199 # verschiedenen Kurzformen erhält. So erzeugen, z.B. sowohl
2200 # „Masse“ als auch „Maße“ einen Langeintrag mit Schlüssel „Masse“:
2202 # >>> for entry in short2long([u'Mas-se']): print unicode(entry)
2204 # >>> for entry in short2long([u'Ma-ße']): print unicode(entry)
2205 # Masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se
2208 # In der Langform werden Alternativen in ein Feld geschrieben:
2210 # >>> for entry in short2long([u'Mas-se', u'Ma-ße']):
2211 # ... print unicode(entry)
2212 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[s-/-s]se;Mas-se;Mas-se
2215 # Sind die Alternativen bereits in der Quelle, bleiben sie in der Kurzform
2218 # >>> ml = [u'Masse;-2-;Mas-se;Mas-se;-5-;Ma[-s/s-]se;Mas-se;Mas-se',
2219 # ... u'Maße;Ma-ße']
2220 # >>> for entry in long2short(ml):
2221 # ... print unicode(entry)
2222 # Mas-se;Mas-se;Mas-se;Ma[-s/s-]se
2223 # Ma-ße;Ma-ße;Mas-se;Ma[-s/s-]se
2225 # Zurück in die Langform:
2227 # >>> for entry in short2long([u'Mas-se;Mas-se;Mas-se;Ma[-s/s-]se',
2228 # ... u'Ma-ße;Ma-ße;-3-;Ma[-s/s-]se']):
2229 # ... print unicode(entry)
2230 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[-s/s-]se;Mas-se;Mas-se
2237 def long2short(lines
, prune
=True, drop_sz
=False):
2238 """Convert sequence of 8-column lines to ShortEntry instances."""
2239 words
= {} # Einträge mit `de`
2240 words_x
= {} # Einträge ohne `de`
2241 words_merged
= set() # Einträge die vollständig in andere einsortiert wurden
2242 entries
= [] # Rückgabewert: Liste der Kurzeinträge
2244 # Zeilen Einlesen, Wandeln und Sammeln::
2247 longentry
= WordEntry(line
)
2248 entry
= ShortEntry(longentry
, prune
=False)
2249 key
= entry
.key().lower() # Schlüssel ohne Großschreibung
2251 if not entry
: # reiner Kommentar oder leerer Eintrag
2253 entries
.append(entry
)
2256 # Einträge mit leerem ersten Feld werden später einsortiert:
2257 if prune
and not entry
[0]:
2258 words_x
[key
] = entry
2261 # Eintrag in `dictionary` und Liste:
2263 entries
.append(entry
)
2265 # Straffen (Weglassen von Feldern/Einträgen wenn möglich),
2266 # es sei denn, der Aufruf erfolgte mit `prune=False`::
2271 for entry
in entries
:
2274 for i
in range(1, len(entry
)):
2275 if not entry
[i
]: # Feld ausgekreuzt
2276 key_i
= join_word(entry
.getitem(i
, substitute
=True)).lower()
2279 co_entry
= words_x
[key_i
]
2280 entry
.merge(co_entry
, prune
=False, start
=1)
2281 words_merged
.add(key_i
)
2282 # del words_x[key_i]
2283 except (KeyError, AssertionError):
2285 co_entry
= words
[key_i
]
2286 entry
.merge(co_entry
, prune
=False, start
=1)
2287 words_merged
.add(key_i
)
2288 except (KeyError, AssertionError):
2291 # Anhängen aller Einträge mit leerem `de`-Feld, die nicht in
2292 # einen zugehörigen Eintrag einsortiert wurden an die Liste::
2294 for key
, entry
in words_x
.iteritems():
2295 # print key, unicode(entry)
2296 if key
in words_merged
:
2299 if len(entry
) > 2 and not entry
[3]: # de-1901-x-versal
2300 key
= join_word(entry
.getitem(3, substitute
=True)).lower()
2302 co_entry
= words_x
[key
]
2303 entry
.merge(co_entry
, prune
=False, start
=3)
2304 words_merged
.add(key
)
2305 except (KeyError, AssertionError):
2307 co_entry
= words
[key
]
2308 entry
.merge(co_entry
, prune
=False, start
=3)
2309 # print key, unicode(co_entry)
2310 except (KeyError, AssertionError):
2313 entries
.append(entry
)
2315 for entry
in entries
:
2316 entry
.prune(drop_sz
)
2322 # Separate Einträge ß-Schreibungen zusammenfassen:
2324 # Ein von 2 ß wird zu ss in de-1996:
2326 # >>> for entry in long2short([
2327 # ... u'Passstrasse;-2-;-3-;-4-;-5-;Pass=stra-sse;Pass=stras-se;Pass=stras-se',
2328 # ... u'Passstraße;-2-;-3-;Pass=stra-ße',
2329 # ... u'Paßstraße;-2-;Paß=stra-ße;-4-']):
2330 # ... print unicode(entry)
2335 # >>> for line in long2short([
2336 # ... u"Grossserie;-2-;-3-;-4-;-5-;Gross=se-rie;Gross=se-rie",
2337 # ... u"Großserie;Groß=se-rie"]):
2338 # ... print unicode(line)
2341 # kein ß in de-1996:
2343 # >>> for line in long2short([
2344 # ... u"Basssaite;-2-;-3-;Bass=sai-te;-5-;Bass=sai-te;Bass=sai-te",
2345 # ... u"Baßsaite;-2-;Baß=sai-te;-4-"]):
2346 # ... print unicode(line)
2349 # Zusätzliche Variante (Fremdwort vs. Lehnwort) in de-1901:
2351 # >>> for entry in short2long([u'Boss;Boss # en.']): print unicode(entry)
2353 # >>> for entry in long2short([u'Boss;Boss # en.']): print unicode(entry)
2355 # >>> for entry in long2short([u'Boss;Boss # en.', u'Boß;-2-;Boß;-3- # < en.']):
2356 # ... print unicode(entry)
2360 # Alternativschreibung in de-1996 (Geschoß, Löß):
2362 # >>> for entry in long2short([u'Geschoss;-2-;-3-;Ge<schoss;Ge<schoss',
2363 # ... u'Geschoß;Ge<schoß # österr. auch de-1996']):
2364 # ... print unicode(entry)
2366 # Ge<schoß # österr. auch de-1996
2368 # Eigennamen auf -ss:
2370 # >>> for entry in long2short([u'Vossstrasse;-2-;-3-;-4-;-5-;Voss=stra-sse;Voss=stras-se;Voss=stras-se',
2371 # ... u'Vossstraße;Voss=stra-ße',
2372 # ... u'Voßstraße;Voß=stra-ße']):
2373 # ... print unicode(entry)
2374 # Voss=stra-ße;Voss=stra-ße
2379 # >>> long2short(['# toller Kommentar'], prune=False)
2380 # [ShortEntry(u'# toller Kommentar')]
2381 # >>> long2short(['# toller Kommentar'], prune=True)
2382 # [ShortEntry(u'# toller Kommentar')]
2388 # Funktionen, die einen Trennstil (oder einen Aspekt eines Trennstils)
2389 # implementieren (siehe auch dokumentation/Trennstile.txt).
2394 # Entferne Alternativtrennungen bei einfachen und suffigierten Fremdwörtern:
2396 # Regelwerk (1996) § 112:
2397 # In Fremdwörtern können die Verbindungen aus Buchstaben für einen
2398 # Konsonanten + l, n oder r entweder entsprechend § 110 getrennt werden,
2399 # oder sie kommen ungetrennt auf die neue Zeile.
2401 # >>> from wortliste import fremdwortsilben
2403 # >>> fremdwoerter = (u'no-b-le Zy-k-lus Ma-g-net Fe-b-ru-ar '
2404 # ... u'Hy-d-rant Ar-th-ri-tis')
2405 # >>> for wort in fremdwoerter.split():
2406 # ... print wort, '->', fremdwortsilben(wort)
2408 # Zy-k-lus -> Zy-klus
2409 # Ma-g-net -> Ma-gnet
2410 # Fe-b-ru-ar -> Fe-bru-ar
2411 # Hy-d-rant -> Hy-drant
2412 # Ar-th-ri-tis -> Ar-thri-tis
2414 # >>> for wort in fremdwoerter.split():
2415 # ... print wort, '->', fremdwortsilben(wort, 'modern')
2417 # Zy-k-lus -> Zyk-lus
2418 # Ma-g-net -> Mag-net
2419 # Fe-b-ru-ar -> Feb-ru-ar
2420 # Hy-d-rant -> Hyd-rant
2421 # Ar-th-ri-tis -> Arth-ri-tis
2425 def fremdwortsilben(wort
, style
='etymologisch'):
2426 """Select in-word hyphenation of foreign words."""
2427 if style
== "modern": # Sprechsilbenregel
2428 return re
.sub(u
'-([bcdfgkptv]|th|st)-(?=[lrn])', u
'\\1-', wort
)
2429 else: # etymologisch
2430 return re
.sub(u
'-([bcdfgkptv]|th|st)-(?=[lrn])', u
'-\\1', wort
)
2431 # Versuch: auch Alternativtrennung nach führendem Vokal:
2432 # Ap-ri-kose -> Apri-kose aber auch Ad-ler -> Adler (!)
2433 # return re.sub(u'(-|^[AEIOUÄÖÜaeiouäöü])([bcdfgkptv]|th|st)-(?=[lrn])',
2438 # K86 Untrennbar sind in Fremdwörtern die Verbindungen von Verschluß- und
2439 # Reibelauten mit l und r, ...
2441 # >>> fremdwoerter = (u'Pu-b-li-kum flexi-b-ler Zy-k-lone Qua-d-rat '
2442 # ... u'Spek-t-rum manö-v-rieren')
2443 # >>> for wort in fremdwoerter.split():
2444 # ... print wort, '->', fremdwortsilben(wort)
2445 # Pu-b-li-kum -> Pu-bli-kum
2446 # flexi-b-ler -> flexi-bler
2447 # Zy-k-lone -> Zy-klone
2448 # Qua-d-rat -> Qua-drat
2449 # Spek-t-rum -> Spek-trum
2450 # manö-v-rieren -> manö-vrieren
2452 # die Lautfolge st+r bleibt ungetrennt, wenn keine Wortfuge vorliegt.
2454 # >>> fremdwoerter = u'Di-st-rikt Magi-st-rat laku-st-risch ab-st-rakt'
2455 # >>> for wort in fremdwoerter.split():
2456 # ... print wort, '->', fremdwortsilben(wort)
2457 # Di-st-rikt -> Di-strikt
2458 # Magi-st-rat -> Magi-strat
2459 # laku-st-risch -> laku-strisch
2460 # ab-st-rakt -> ab-strakt
2462 # K 87 Untrennbar ist die Konsonantenverbindung "gn".
2464 # >>> fremdwoerter = u'Ma-g-net Pro-g-nose Si-g-net'
2465 # >>> for wort in fremdwoerter.split():
2466 # ... print wort, '->', fremdwortsilben(wort)
2467 # Ma-g-net -> Ma-gnet
2468 # Pro-g-nose -> Pro-gnose
2469 # Si-g-net -> Si-gnet
2471 # Keine Übergeneralisierung:
2473 # >>> woerter = u'Seg-ler bast-le Ad-ler'
2474 # >>> for wort in woerter.split():
2475 # ... print wort, '->', fremdwortsilben(wort)
2476 # Seg-ler -> Seg-ler
2477 # bast-le -> bast-le
2480 # wegen Übergeneralisierung nicht möglich:
2481 # Ap-ri-kose -> Apri-kose
2482 # ig-no-rie-ren -> igno-rie-ren
2488 # Entferne Alternativtrennungen nach §113 (verblasste Etymologie).
2490 # Regelwerk (1996) §113:
2491 # Wörter, die sprachhistorisch oder von der Herkunftssprache her gesehen
2492 # Zusammensetzungen oder Präfigierungen sind, aber nicht mehr als solche
2493 # empfunden oder erkannt werden, kann man entweder nach § 108 oder nach §
2494 # 109 bis § 112 trennen.
2496 # >>> from wortliste import verblasst
2497 # >>> blasse = (u'hi-n<auf he-r<an da-r<um Chry-s<an-the-me Hek-t<ar '
2498 # ... u'He-li-ko<p-ter in-te-r>es-sant Li-n<oleum Pä-d<a-go-gik')
2499 # >>> for wort in blasse.split():
2500 # ... print wort, '->', verblasst(wort)
2501 # hi-n<auf -> hin<auf
2504 # Chry-s<an-the-me -> Chrys<an-the-me
2505 # Hek-t<ar -> Hekt<ar
2506 # He-li-ko<p-ter -> He-li-ko<pter
2507 # in-te-r>es-sant -> in-ter>es-sant
2508 # Li-n<oleum -> Lin<oleum
2509 # Pä-d<a-go-gik -> Päd<ago-gik
2511 # >>> for wort in blasse.split():
2512 # ... print wort, '->', verblasst(wort, 'modern')
2513 # hi-n<auf -> hi-nauf
2516 # Chry-s<an-the-me -> Chry-san-the-me
2517 # Hek-t<ar -> Hek-tar
2518 # He-li-ko<p-ter -> He-li-kop-ter
2519 # in-te-r>es-sant -> in-te-res-sant
2520 # Li-n<oleum -> Li-noleum
2521 # Pä-d<a-go-gik -> Pä-da-go-gik
2523 # Ersetze, wenn zwischen Haupttrennstelle und Nebentrennstelle nur ein
2525 # (Die Haupttrennstelle kann vor oder nach der Nebentrennstelle liegen.)
2528 def verblasst(wort
, style
='etymologisch'):
2529 """Select hyphenation of foreign words with obscure etymology."""
2530 if style
== "modern": # Sprechsilbenregel
2531 wort
= re
.sub(u
'[<>=]+[.]*(.[-.]+)', u
'\\1', wort
)
2532 wort
= re
.sub(u
'([-.]+.)[<>=]+[.]*', u
'\\1', wort
)
2533 else: # etymologisch
2534 wort
= re
.sub(u
'([<>=]+[.]*.)[-.]+', u
'\\1', wort
)
2535 wort
= re
.sub(u
'[-.]+(.[<>=]+)', u
'\\1', wort
)
2542 # K44 „ſ“ (langes s) steht in Fremdwörtern...
2544 # >>> blasse = (u'tran<s-pirieren tran<s-zendent ab<s-tinent '
2545 # ... u'Ab<s-zess Pro-s<odie')
2546 # >>> for wort in blasse.split():
2547 # ... print wort, '->', verblasst(wort)
2548 # tran<s-pirieren -> tran<spirieren
2549 # tran<s-zendent -> tran<szendent
2550 # ab<s-tinent -> ab<stinent
2551 # Ab<s-zess -> Ab<szess
2552 # Pro-s<odie -> Pros<odie
2554 # Trennstellen können als ungünstig markiert sein:
2556 # >>> blasse = (u'Bür-ger=in<.i-ti-a-ti-ve Pä-..d<e-..rast')
2557 # >>> for wort in blasse.split():
2558 # ... print wort, '->', verblasst(wort)
2559 # Bür-ger=in<.i-ti-a-ti-ve -> Bür-ger=in<.iti-a-ti-ve
2560 # Pä-..d<e-..rast -> Päd<erast
2561 # >>> for wort in blasse.split():
2562 # ... print wort, '->', verblasst(wort, 'modern')
2563 # Bür-ger=in<.i-ti-a-ti-ve -> Bür-ger=ini-ti-a-ti-ve
2564 # Pä-..d<e-..rast -> Pä-..de-..rast
2570 # Füge Trennmöglichkeiten am Wortanfang und -ende zu, die nach K79 (bzw. §107
2571 # E2 des Regelwerkes) verboten sind aber in Notentexten gebraucht werden.
2573 # >>> from wortliste import scoretext
2574 # >>> scoretext(u'Abend')
2576 # >>> scoretext(u'Ra-dio')
2579 # Das gleiche gilt für Trennmöglichkeiten am Anfang/Ende von Teilwörtern:
2581 # >>> scoretext(u'Eis=ano-ma-lie')
2582 # u'Eis=a.no-ma-lie'
2583 # >>> scoretext(u'Ra-dio<phon')
2588 # >>> scoretext(u'Ai-chin-ger'), scoretext(u'Ai-da')
2589 # (u'Ai-chin-ger', u'A.i-da')
2590 # >>> scoretext(u'Ma-rie'), scoretext(u'Li-nie')
2591 # (u'Ma-rie', u'Li-ni.e')
2592 # >>> scoretext(u'Ta-too'), scoretext(u'Zoo<lo-gie')
2593 # (u'Ta-too', u'Zo.o<lo-gie')
2594 # >>> scoretext(u'A-pnoe'), scoretext(u'O-boe')
2595 # (u'A-pnoe', u'O-bo.e')
2596 # >>> scoretext(u'Plaque'), scoretext(u'treue')
2597 # (u'Plaque', u'treu.e')
2598 # >>> scoretext(u'Fon-due=pfan-ne'), scoretext(u'Aue')
2599 # (u'Fon-due=pfan-ne', u'Au.e')
2600 # >>> scoretext(u'Ge-nie'), scoretext(u'Iphi-ge-nie')
2601 # (u'Ge-nie', u'I.phi-ge-nie')
2602 # >>> scoretext(u'Ago-nie'), scoretext(u'Be-go-nie')
2603 # (u'A.go-nie', u'Be-go-ni.e')
2604 # >>> scoretext(u'Kom-pa-nie'), scoretext(u'Kas-ta-nie'), scoretext(u'Ge-ra-nie')
2605 # (u'Kom-pa-nie', u'Kas-ta-ni.e', u'Ge-ra-ni.e')
2607 # ungelöst: Knie / Kni.e # pl.
2610 def scoretext(word
):
2611 """Mark up leading one-letter syllables."""
2612 # Führender Vokal, gefolgt von Silbenanfang
2613 # (optionaler Konsonant (auch ch/ck/ph/rh/sch/sh/th) + Vokal)
2614 for match
in re
.finditer(u
'(^|[<=])([aeiouäöü])((.|ch|ck|ph|sch|th)?[aeiouäöü])',
2615 word
, flags
=re
.IGNORECASE
):
2616 # print match.groups()
2617 # Ausnahmen: Doppellaute, Diphtonge (außer A-i-da), Umlaute
2618 if (re
.search(u
'(aa|ae|ai|au|äu|ei|eu|oe|oo|ou|ue)',
2619 match
.group(0), flags
=re
.IGNORECASE
)
2620 and word
!= u
'Ai-da' or word
== u
'io-ta' ):
2622 word
= ''.join((word
[:match
.start()], match
.expand(u
'\\1\\2.\\3'),
2623 scoretext(word
[match
.end():])))
2625 # zwei Vokale am Wortende
2626 for match
in re
.finditer(u
'([aeiouäöü])([aeiouäöü])([<>=]|$)', word
):
2627 # if re.search(u'(oo)', match.group(0)):
2628 # if 'ie' in match.group(0) and re.search(u'([a]-nie)', word, flags=re.IGNORECASE):
2629 # sys.stderr.write(word+' '+match.group(0)+'\n')
2630 # Ausnahmen: Doppellaute, Diphtonge, Umlaute (außer "Oboe")
2631 if (re
.search(u
'(aa|ae|ai|au|äu|ee|ei|eu|oe|oi|ou|ui)', match
.group(0))
2632 and not word
[:match
.end()].endswith(u
'waii') # ! Hawaii
2633 and not word
[:match
.end()].endswith(u
'boe')): # ! Oboe
2635 # Ausnahmen mit Ausnahmen:
2636 # …oo außer zoo<, ...
2637 if 'oo' in match
.group(0) and match
.group(0) != 'oo<':
2639 # …ie außer "-(l)inie", Iphigenie, Kastanie, Geranie, Begonie
2640 if ('ie' in match
.group(0)
2641 and not word
[:match
.end()].endswith(u
'i-nie') # Linie,
2642 and not word
[:match
.end()].endswith(u
'ta-nie') # Kastanie,
2643 and not word
[:match
.end()].endswith(u
'ra-nie') # Geranie,
2644 and not word
[:match
.end()].endswith(u
'e-go-nie') # Begonie != Agonie
2645 and not word
== u
'Iphi-ge-nie'):
2647 # …ue (Plaque, Re-vue, vogue) außer "-tue, -aue, ... -äue"
2648 if 'ue' in match
.group(0) and re
.search(u
'[^aeät]ue([<>=]|$)',
2649 word
[:match
.end()], flags
=re
.IGNORECASE
):
2652 word
= ''.join((word
[:match
.start()], match
.expand(u
'\\1.\\2\\3'),
2653 scoretext(word
[match
.end():])))
2658 # keine_einzelvokale()
2659 # --------------------
2661 # Traditionell enthalten die TeX-Trennmuster keine Trennstellen deren Abstand
2662 # zur Nachbartrennstelle einen Buchstaben beträgt. Dies ist eine *ästhetische*
2663 # Entscheidung um „Flatterbuchstaben“ zu vermeiden
2665 # Bei einvokalischen Silben im Wortinneren, nimm die zweite:
2667 # >>> from wortliste import keine_einzelvokale
2668 # >>> einzelne = (u'The-a-ter me-ri-di-.o-nal')
2669 # >>> for wort in einzelne.split():
2670 # ... print wort, '->', keine_einzelvokale(wort)
2671 # The-a-ter -> Thea-ter
2672 # me-ri-di-.o-nal -> me-ri-dio-nal
2674 # Allerdings nicht, wenn die erste Trennstelle unterdrückt ist:
2676 # >>> einzelne = (u'La-sal-le-a-.ner Athe-i-.sten')
2677 # >>> for wort in einzelne.split():
2678 # ... print wort, '->', keine_einzelvokale(wort)
2679 # La-sal-le-a-.ner -> La-sal-le-a-.ner
2680 # Athe-i-.sten -> Athe-i-.sten
2684 def keine_einzelvokale(wort
):
2685 """Drop marker after single vowels."""
2686 return re
.sub(u
'-[.]*([aeiouyäöü]-[^.])', u
'\\1', wort
)
2695 # Trennzeichen entfernen::
2697 def join_word(wort
, assert_complete
=False):
2699 # Einfache Trennzeichen:
2701 # == ================================================================
2702 # \= Trennstelle an Wortfugen (Wort=fu-ge)
2703 # \< Trennstelle nach Präfix (Vor<sil-be)
2704 # \> Trennstelle vor Suffix (Freund>schaf-ten)
2705 # \- Nebentrennstelle (ge-hen)
2706 # \. unerwünschte/ungünstige Trennstelle (Ge<schoss=en<.er-gie)
2707 # == ================================================================
2711 for marker
in u
'=<>-.':
2712 wort
= wort
.replace(marker
, u
'')
2714 # Spezielle Trennungen für die traditionelle Rechtschreibung
2715 # (siehe ../../dokumente/README.wortliste)::
2717 if '{' in wort
or '}' in wort
:
2718 wort
= wort
.replace(u
'{ck/kk}', u
'ck')
2719 wort
= wort
.replace(u
'{ck/k', u
'k')
2720 wort
= wort
.replace(u
'k}', u
'k')
2721 # Konsonanthäufungen an Wortfuge: '{xx/xxx}' -> 'xx':
2722 wort
= re
.sub(ur
'\{(.)\1/\1\1\1\}', ur
'\1\1', wort
)
2723 # schon getrennt: ('{xx/xx' -> 'xx' und 'x}' -> 'x'):
2724 wort
= re
.sub(ur
'\{(.)\1/\1\1$', ur
'\1\1', wort
)
2725 wort
= re
.sub(ur
'^(.)\}', ur
'\1', wort
)
2727 # Trennstellen in doppeldeutigen Wörtern::
2729 if '[' in wort
or ']' in wort
:
2730 wort
= re
.sub(ur
'\[(.*)/\1\]', ur
'\1', wort
)
2732 wort
= re
.sub(ur
'\[([^/\[]+)$', ur
'\1', wort
)
2733 wort
= re
.sub(ur
'^([^/\]]+)\]', ur
'\1', wort
)
2735 # Test auf verbliebene komplexe Trennstellen::
2738 for spez
in u
'[{/}]':
2740 raise AssertionError('Spezialtrennung %s, %s' %
2741 (wort
.encode('utf8'), wort
.encode('utf8')))
2748 # Zerlege ein Wort mit Trennzeichen in eine Liste von Silben und eine Liste
2751 # >>> from wortliste import zerlege
2753 # >>> zerlege(u'Haupt=stel-le')
2754 # ([u'Haupt', u'stel', u'le'], [u'=', u'-'])
2755 # >>> zerlege(u'Ge<samt=be<triebs=rats==chef')
2756 # ([u'Ge', u'samt', u'be', u'triebs', u'rats', u'chef'], [u'<', u'=', u'<', u'=', u'=='])
2757 # >>> zerlege(u'an<stands>los')
2758 # ([u'an', u'stands', u'los'], [u'<', u'>'])
2759 # >>> zerlege(u'An<al.pha-bet')
2760 # ([u'An', u'al', u'pha', u'bet'], [u'<', u'.', u'-'])
2765 silben
= re
.split(u
'[-·._<>=]+', wort
)
2766 trennzeichen
= re
.split(u
'[^-·._|<>=]+', wort
)
2767 return silben
, [tz
for tz
in trennzeichen
if tz
]
2772 # Gib einen String mit Trennmarkierung für abweichende Trennungen in
2773 # mehrdeutigen Wörtern zurück:
2775 # >>> from wortliste import alternatives
2776 # >>> alternatives(u'Mas-se', u'Ma-sse')
2778 # >>> alternatives(u'Ma-sse', u'Mas-se')
2783 def alternatives(wort1
, wort2
):
2785 wort1
= [c
for c
in wort1
]
2786 wort2
= [c
for c
in wort2
]
2789 for c1
, c2
in zip(wort1
, wort2
):
2793 for c1
, c2
in zip(wort1
.__reversed__(), wort2
.__reversed__()):
2798 return u
''.join(pre
+ [u
'['] + wort1
[len(pre
):-len(post
)] + [u
'/']
2799 + wort2
[len(pre
):-len(post
)] + [u
']'] + post
)
2804 # Fehler beim Übertragen von Trennstellen mit uebertrage()_::
2806 class TransferError(ValueError):
2807 def __init__(self
, wort1
, wort2
):
2808 msg
= u
'Inkompatibel: %s %s' % (wort1
, wort2
)
2809 ValueError.__init
__(self
, msg
.encode('utf8'))
2811 def __unicode__(self
):
2812 return str(self
).decode('utf8')
2818 # Übertrage die Trennzeichen von `wort1` auf `wort2`:
2820 # >>> from wortliste import uebertrage, TransferError
2822 # >>> uebertrage(u'Haupt=stel-le', u'Haupt·stel·le')
2825 # Auch teilweise Übertragung, von "kategorisiert" nach "unkategorisiert":
2827 # >>> print uebertrage(u'Haupt=stel-le', u'Haupt=stel·le')
2830 # >>> print uebertrage(u'Haupt·stel-le', u'Haupt=stel·le')
2833 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler')
2836 # >>> print uebertrage(u'Freund>schaf·ten', u'Freund-schaf-ten')
2839 # Übertragung doppelter Marker:
2841 # >>> print uebertrage(u'ver<<aus<ga-be', u'ver<aus<ga-be')
2844 # >>> print uebertrage(u'freund>lich>>keit', u'freund>lich>keit')
2847 # >>> print uebertrage(u'Amts==haupt=stel-le', u'Amts=haupt=stel-le')
2848 # Amts==haupt=stel-le
2850 # Kein Überschreiben doppelter Marker:
2851 # >>> print uebertrage(u'ver<aus<ga-be', u'ver<<aus<ga-be')
2854 # >>> print uebertrage(u'Amts=haupt=stel-le', u'Amts==haupt=stel·le')
2855 # Amts==haupt=stel-le
2857 # Erhalt des Markers für ungünstige Stellen:
2858 # >>> print uebertrage(u'An·al.pha·bet', u'An<al.pha-bet')
2861 # Keine Übertragung, wenn die Zahl oder Position der Trennstellen
2862 # unterschiedlich ist oder bei unterschiedlichen Wörtern:
2865 # ... uebertrage(u'Ha-upt=stel-le', u'Haupt=stel·le')
2866 # ... uebertrage(u'Haupt=ste-lle', u'Haupt=stel·le')
2867 # ... uebertrage(u'Waupt=stel-le', u'Haupt=stel·le')
2868 # ... except TransferError:
2871 # Übertragung auch bei unterschiedlicher Schreibung oder Position der
2872 # Trennstellen mit `strict=False` (für Abgleich zwischen Sprachvarianten):
2874 # >>> uebertrage(u'er-ster', u'ers·ter', strict=False)
2876 # >>> uebertrage(u'Fluß=bett', u'Fluss·bett', strict=False)
2878 # >>> uebertrage(u'ab>bei-ßen', u'ab>beis·sen', strict=False)
2880 # >>> print uebertrage(u'Aus<tausch=dien-stes', u'Aus-tausch=diens-tes', False)
2881 # Aus<tausch=diens-tes
2883 # Auch mit `strict=False` muß die Zahl der Trennstellen übereinstimmen
2884 # (Ausnahmen siehe unten):
2887 # ... uebertrage(u'Ha-upt=ste-lle', u'Haupt=stel·le', strict=False)
2888 # ... except TransferError:
2891 # Akzeptiere unterschiedliche Anzahl von Trennungen bei st und ck nach
2894 # >>> uebertrage(u'acht=ecki-ge', u'acht·e{ck/k·k}i·ge', strict=False)
2895 # u'acht=e{ck/k-k}i-ge'
2896 # >>> uebertrage(u'As-to-ria', u'Asto·ria', strict=False)
2898 # >>> uebertrage(u'Asto-ria', u'As·to·ria', strict=False)
2900 # >>> uebertrage(u'So-fa=ecke', u'So·fa=e{ck/k-k}e', strict=False)
2901 # u'So-fa=e{ck/k-k}e'
2902 # >>> uebertrage(u'Drei=ecks=ecke', u'Drei=ecks==e{ck/k-k}e', strict=False)
2903 # u'Drei=ecks==e{ck/k-k}e'
2905 # Mit ``upgrade=False`` werden nur unspezifische Trennstellen überschrieben:
2907 # >>> print uebertrage(u'an=stel-le', u'an<stel·le', upgrade=False)
2910 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler', upgrade=False)
2913 # >>> print uebertrage(u'Aus-stel-ler', u'Aus<stel-ler', upgrade=False)
2916 # >>> print uebertrage(u'vor<an<<stel-le', u'vor-an<stel·le', upgrade=False)
2921 selbstlaute
= u
'aeiouäöüAEIOUÄÖÜ'
2923 def uebertrage(wort1
, wort2
, strict
=True, upgrade
=True):
2925 silben1
, trennzeichen1
= zerlege(wort1
)
2926 silben2
, trennzeichen2
= zerlege(wort2
)
2927 # Prüfe strikte Übereinstimmung:
2928 if silben1
!= silben2
and strict
:
2929 if u
'<' in trennzeichen1
or u
'·' in trennzeichen2
:
2930 raise TransferError(wort1
, wort2
)
2933 # Prüfe ungefähre Übereinstimmung:
2934 if len(trennzeichen1
) != len(trennzeichen2
):
2935 # Selbstlaut + st oder ck?
2936 for s
in selbstlaute
:
2937 if (wort2
.find(s
+u
'{ck/k·k}') != -1 or
2938 wort2
.find(s
+u
'{ck/k-k}') != -1):
2939 wort1
= re
.sub(u
'%sck([%s])'%(s
,selbstlaute
),
2940 ur
'%s-ck\1'%s, wort1
)
2941 silben1
, trennzeichen1
= zerlege(wort1
)
2942 if wort2
.find(s
+u
's·t') != -1:
2943 wort1
= wort1
.replace(s
+u
'st', s
+u
's-t')
2944 silben1
, trennzeichen1
= zerlege(wort1
)
2945 elif wort1
.find(s
+u
's-t') != -1:
2946 wort1
= wort1
.replace(s
+u
's-t', s
+u
'st')
2947 silben1
, trennzeichen1
= zerlege(wort1
)
2948 # print u'retry:', silben1, trennzeichen1
2949 # immer noch ungleiche Zahl an Trennstellen?
2950 if len(trennzeichen1
) != len(trennzeichen2
):
2951 raise TransferError(wort1
, wort2
)
2953 # Baue wort3 aus silben2 und spezifischeren Trennzeichen:
2954 wort3
= silben2
.pop(0)
2955 for t1
,t2
in zip(trennzeichen1
, trennzeichen2
):
2956 if ((t2
== u
'·' and t1
!= u
'.') # unspezifisch
2958 ((t2
in (u
'-', u
'<') and t1
in (u
'<', u
'<<', u
'<=')) # Praefixe
2959 or (t2
in (u
'-', u
'>') and t1
in (u
'>', u
'>>', u
'=>')) # Suffixe
2960 or (t2
in (u
'-', u
'=') and t1
in (u
'=', u
'==', u
'===')) # W-fugen
2964 elif t2
== u
'.' and t1
not in u
'·.':
2968 wort3
+= silben2
.pop(0)
2972 # Übertrag kategorisierter Trennstellen zwischen den Feldern aller Einträge
2975 def sprachabgleich(entry
, vorbildentry
=None):
2978 return # allgemeine Schreibung
2980 mit_affix
= None # < oder >
2981 kategorisiert
= None # kein ·
2982 unkategorisiert
= None # mindestens ein ·
2983 gewichtet
= None # == oder <= oder =>
2984 for field
in entry
[1:]:
2985 if not field
: # -2-, -3-, ...
2987 if u
'{' in field
and u
'[' in field
: # Bi-ber==be[t=t/{tt/tt=t}]uch
2988 continue # zu komplex
2990 unkategorisiert
= field
2991 elif u
'<' in field
or u
'>' in field
:
2994 kategorisiert
= field
2995 if u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
2998 for field
in vorbildentry
[1:]:
2999 if not field
: # -2-, -3-, ...
3001 if u
'{' in field
and u
'[' in field
: # Bi-ber==be[t=t/{tt/tt=t}]uch
3002 continue # zu komplex
3003 if not mit_affix
and u
'<' in field
or u
'>' in field
:
3005 elif not kategorisiert
and unkategorisiert
and u
'·' not in field
:
3006 kategorisiert
= field
3007 if not gewichtet
and u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
3009 # print 've:', mit_affix, kategorisiert, unkategorisiert
3010 if mit_affix
and (kategorisiert
or unkategorisiert
or gewichtet
):
3011 for i
in range(1,len(entry
)):
3012 if not entry
[i
]: # -2-, -3-, ...
3014 if u
'<' not in entry
[i
] or u
'·' in entry
[i
]:
3016 entry
[i
] = uebertrage(mit_affix
, entry
[i
], strict
=False)
3017 except TransferError
, e
:
3018 if not '/' in entry
[i
]:
3019 print u
'Sprachabgleich:', unicode(e
)
3020 # print mit_affix+u':', unicode(entry)
3021 elif kategorisiert
and unkategorisiert
:
3022 for i
in range(1,len(entry
)):
3023 if u
'·' in entry
[i
]:
3025 entry
[i
] = uebertrage(kategorisiert
, entry
[i
], strict
=False)
3026 except TransferError
, e
:
3027 print u
'Sprachabgleich:', unicode(e
)
3028 # print kategorisiert, unicode(entry)
3030 for i
in range(1,len(entry
)):
3031 if u
'=' in entry
[i
] and not (
3032 u
'{' in entry
[i
] and u
'[' in entry
[i
]):
3034 entry
[i
] = uebertrage(gewichtet
, entry
[i
], strict
=False)
3035 except TransferError
, e
:
3036 print u
'Sprachabgleich:', unicode(e
)
3040 # Großschreibung in Kleinschreibung wandeln und umgekehrt
3042 # Diese Version funktioniert auch für Wörter mit Trennzeichen (während
3043 # str.title() nach jedem Trennzeichen wieder groß anfängt)
3045 # >>> from wortliste import toggle_case
3046 # >>> toggle_case(u'Ha-se')
3048 # >>> toggle_case(u'arm')
3050 # >>> toggle_case(u'frei=bier')
3052 # >>> toggle_case(u'L}a-ger')
3055 # Keine Änderung bei Wörtern mit Großbuchstaben im Inneren:
3057 # >>> toggle_case(u'USA')
3059 # >>> toggle_case(u'iRFD')
3062 # >>> toggle_case(u'gri[f-f/{ff/ff')
3064 # >>> toggle_case(u'Gri[f-f/{ff/ff')
3069 def toggle_case(wort
):
3071 key
= join_word(wort
, assert_complete
=True)
3072 except AssertionError:
3077 return wort
[0].upper() + wort
[1:]
3084 # Duden-Sortierung für die Wortliste
3086 # >>> from wortliste import sortkey_duden
3087 # >>> sortkey_duden([u"Abflußröhren"])
3088 # u'abflussrohren a*bflu*szroehren'
3089 # >>> sortkey_duden([u"Abflußrohren"])
3090 # u'abflussrohren a*bflu*szro*hren'
3091 # >>> sortkey_duden([u"Abflussrohren"])
3094 # >>> s = sorted([[u"Abflußröhren"], [u"Abflußrohren"], [u"Abflussrohren"]],
3095 # ... key=sortkey_duden)
3096 # >>> print ', '.join(e[0] for e in s)
3097 # Abflussrohren, Abflußrohren, Abflußröhren
3101 # Ligaturen auflösen und andere "normalisierunde" Ersetzungen für den
3102 # (Haupt-)Sortierschlüssel (Akzente werden über ``unicodedata.normalize``
3111 # "Zweitschlüssel" zur Unterscheidung von Umlauten/SZ und Basisbuchstaben::
3113 umschrift_subkey
= {
3129 # Schlüssel für die alphabetische Sortierung gemäß Duden-Regeln.
3131 # Argument ist ein Eintrag im WordEntry oder ShortEntry Format oder
3132 # ein (Unicode) String:
3134 # >>> print sortkey_duden(weste) # WordEntry
3136 # >>> print sortkey_duden(dst) # ShortEntry
3139 # >>> print sortkey_duden(u'Stra-ße')
3141 # >>> print sortkey_duden([u'Büh-ne'])
3143 # >>> print sortkey_duden(u'Weiß=flog;Weiß=flog;-3-;-4-;-5-')
3144 # weissflog weiszflo*g
3145 # >>> print sortkey_duden(u'-1-;Meß=sen-der;-3-;-4-;-5-')
3146 # messsender meszsender
3147 # >>> print sortkey_duden(u'As-sen # (geogr. und Eigen-) Name\n')
3149 # >>> print sortkey_duden(u'aßen;aßen;-3-;-4-;-5-\n')
3151 # >>> print sortkey_duden(u'äßen\n')
3156 def sortkey_duden(entry
):
3158 # ggf. ungetrenntes Wort extrahieren oder generieren::
3160 if isinstance(entry
, list):
3163 except AttributeError:
3165 if len(entry
) == 1: # ein Muster pro Zeile, siehe z.B. pre-1901
3166 key
= join_word(key
)
3168 match
= re
.search(u
'^[-0-9;]*([^;\s]+)', entry
) # erstes volles Feld
3170 key
= match
.group(1)
3173 key
= join_word(key
)
3174 key
= re
.sub(u
'[0-9;]', ur
'', key
)
3176 # Großschreibung ignorieren:
3178 # Der Duden sortiert Wörter, die sich nur in der Großschreibung unterscheiden
3179 # "klein vor groß" (ASCII sortiert "groß vor klein"). In der
3180 # `Trennmuster-Wortliste` kommen Wörter nur mit der häufiger anzutreffenden
3181 # Großschreibung vor, denn der TeX-Trennalgorithmus ignoriert Großschreibung.
3190 skey
= key
.replace(u
'ß', u
'ss')
3192 # Restliche Akzente weglassen: Wandeln in Darstellung von Buchstaben mit
3193 # Akzent als "Grundzeichen + kombinierender Akzent". Anschließend alle
3194 # nicht-ASCII-Zeichen ignorieren::
3196 skey
= skey
.translate(umschrift_skey
)
3197 skey
= unicodedata
.normalize('NFKD', skey
)
3198 skey
= unicode(skey
.encode('ascii', 'ignore'))
3200 # "Zweitschlüssel" für das eindeutige Einsortieren von Wörtern mit
3201 # gleichem Schlüssel (Masse/Maße, waren/wären, ...):
3203 # * "*" nach aou für die Unterscheidung Grund-/Umlaut
3209 subkey
= key
.translate(umschrift_subkey
)
3210 skey
= u
'%s %s' % (skey
,subkey
)
3212 # Gib den Sortierschlüssel zurück::
3221 # Vergleiche zwei Sequenzen von `WordEntries` (genauer: alle Objekte, die
3222 # sich sinnvoll zu Unicode wandeln lassen).
3226 # >>> from wortliste import udiff
3227 # >>> print udiff([abbeissen, aalbestand], [abbeissen,dresz], 'alt', 'neu')
3231 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
3232 # -Aalbestand;Aal=be<stand # Test
3233 # +Dreß;-2-;Dreß;-4-
3235 # >>> udiff([abbeissen, aalbestand], [abbeissen, aalbestand], 'alt', 'neu')
3240 def udiff(a
, b
, fromfile
='', tofile
='',
3241 fromfiledate
='', tofiledate
='', n
=1, encoding
='utf8'):
3243 a
= [unicode(entry
).rstrip().encode(encoding
) for entry
in a
]
3244 b
= [unicode(entry
).rstrip().encode(encoding
) for entry
in b
]
3246 diff
= '\n'.join(difflib
.unified_diff(a
, b
, fromfile
, tofile
,
3247 fromfiledate
, tofiledate
, n
, lineterm
=''))
3250 return diff
.decode(encoding
)
3256 # Normalisierung und Expansion von Sprachtags nach [BCP47]_
3258 # >>> from wortliste import normalize_language_tag
3259 # >>> normalize_language_tag('de_AT-1901')
3260 # ['de-AT-1901', 'de-AT', 'de-1901', 'de']
3262 # >>> normalize_language_tag('de') # Deutsch, allgemeingültig
3264 # >>> normalize_language_tag('de_1901') # traditionell (Reform 1901)
3266 # >>> normalize_language_tag('de_1996') # reformiert (Reform 1996)
3268 # >>> normalize_language_tag('de_CH') # ohne ß (Schweiz oder versal)
3270 # >>> normalize_language_tag('de-x-versal') # versal
3271 # ['de-x-versal', 'de']
3272 # >>> normalize_language_tag('de-1901-x-versal') # versal
3273 # ['de-1901-x-versal', 'de-1901', 'de-x-versal', 'de']
3274 # >>> normalize_language_tag('de_CH-1996') # Schweiz traditionell (süssauer)
3275 # ['de-CH-1996', 'de-CH', 'de-1996', 'de']
3277 # 'de': 1, # Deutsch, allgemeingültig
3278 # 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
3279 # 'de-1996': 3, # reformierte Reformschreibung (1996)
3280 # 'de-x-versal': 4, # ohne ß (Schweiz oder versal) allgemein
3281 # # 'de-CH': 4, # Alias
3282 # 'de-1901-x-versal': 5, # ohne ß (Schweiz oder versal) "traditionell"
3283 # 'de-1996-x-versal': 6, # ohne ß (Schweiz oder versal) "reformiert"
3284 # # 'de-CH-1996': 6, # Alias
3285 # 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
3290 def normalize_language_tag(tag
):
3291 """Return a list of normalized combinations for a `BCP 47` language tag.
3294 tag
= tag
.replace('_','-')
3295 # split (except singletons, which mark the following tag as non-standard):
3296 tag
= re
.sub(r
'-([a-zA-Z0-9])-', r
'-\1_', tag
)
3298 subtags
= [subtag
.replace('_', '-') for subtag
in tag
.split('-')]
3299 base_tag
= [subtags
.pop(0)]
3300 # find all combinations of subtags
3301 for n
in range(len(subtags
), 0, -1):
3302 # for tags in unique_combinations(subtags, n):
3303 for tags
in itertools
.combinations(subtags
, n
):
3304 taglist
.append('-'.join(base_tag
+list(tags
)))
3313 # Teste Übereinstimmung des ungetrennten Wortes in Feld 1 mit den
3314 # Trennmustern nach Entfernen der Trennmarker. Schreibe Inkonsistenzen auf die
3317 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
3319 def test_keys(wortliste
):
3320 print u
"Teste Schlüssel-Trennmuster-Übereinstimmung:"
3322 for entry
in wortliste
:
3323 if isinstance(entry
, ShortEntry
):
3324 print u
"Wortliste im Kurzformat: überspringe Schlüssel-Test."
3326 # Test der Übereinstimmung ungetrenntes/getrenntes Wort
3329 for wort
in entry
[1:]:
3330 if not wort
: # leere Felder
3332 if key
!= join_word(wort
):
3334 print u
"\nkey '%s' != join_word('%s')" % (key
, wort
),
3335 if key
.lower() == join_word(wort
).lower():
3336 print(u
" Abgleich der Großschreibung mit"
3337 u
"`prepare-patch.py grossabgleich`."),
3342 # Finde Doppeleinträge (teste, ob jeder Schlüssel nur einmal vorkommt).
3343 # Schreibe Inkonsistenzen auf die Standardausgabe.
3345 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
3347 def test_uniqueness(wortliste
):
3351 for entry
in wortliste
:
3355 print "da ", unicode(words
[key
])
3356 print "neu", unicode(entry
)
3358 print u
"%d Doppeleinträge." % doppelte
3360 # Teste die Wandlung einer Zeile im "wortliste"-Format in eine
3361 # ``WordEntry``-Instanz und zurück::
3363 def test_str_entry_str_conversion(wordfile
):
3365 for line
in file(wordfile
.name
):
3366 line
= line
.rstrip().decode(wordfile
.encoding
)
3367 entry
= WordEntry(line
)
3368 if line
== unicode(entry
):
3372 print u
'+', unicode(entry
)
3374 print OK
, u
"Einträge rekonstruiert"
3377 # Teste Vervollständigung und Zusammenfassung von Einträgen::
3379 def test_completion_pruning(entries
):
3381 for entry
in entries
:
3382 new
= copy
.copy(entry
)
3386 patch
= udiff(entries
, reko
, 'wortliste', 'neu')
3390 print u
"alle Einträge rekonstruiert"
3394 # Aufruf von der Kommandozeile
3395 # ============================
3399 if __name__
== '__main__':
3402 # sys.stdout mit UTF8 encoding (wie in Python 3)
3403 sys
.stdout
= codecs
.getwriter('UTF-8')(sys
.stdout
)
3404 # sys.stderr = codecs.getwriter('UTF-8')(sys.stderr)
3406 print u
"Test der Werkzeuge und inneren Konsistenz der Wortliste"
3408 # Ein WordFile Dateiobjekt::
3411 wordfile
= WordFile(sys
.argv
[1], format
='auto')
3413 wordfile
= WordFile('../../../wortliste')
3414 # wordfile = WordFile('../../../wlst', format='auto') # wortliste im Kurzformat
3415 # wordfile = WordFile('neu.todo')
3416 # wordfile = WordFile('neu-kurz.todo', format='f5')
3417 # wordfile = WordFile('korrektur.todo', format='auto')
3419 # Format bestimmen::
3421 print "Datei: '%s'" % wordfile
.name
, u
"Format:", wordfile
.format
3423 # Liste der Datenfelder (die Klasseninstanz als Argument für `list` liefert
3424 # den Iterator über die Felder, `list` macht daraus eine Liste)::
3426 wordlist
= list(wordfile
)
3427 print len(wordlist
), u
"Zeilen"
3429 # Ein Wörterbuch (dict Instanz)::
3431 wordfile
.seek(0) # Pointer zurücksetzen
3432 words
= wordfile
.asdict()
3433 print len(words
), u
"Wörterbucheinträge"
3435 # Test auf Doppeleinträge::
3437 if len(words
) != len(wordlist
):
3438 test_uniqueness(wordlist
)
3441 # Teste Schlüssel-Trennmuster-Übereinstimmung::
3443 if isinstance(wordlist
[0], WordEntry
):
3446 # Teste Eintrags-Konsistenz im Kurzformat::
3448 if isinstance(wordlist
[0], ShortEntry
):
3450 langs
= ("de", "de-1901", "de-CH", "de-1901-x-versal", "de-CH-1901")
3454 for entry
in wordlist
:
3457 word
= entry
.get(lang
)
3458 key
= join_word(word
)
3459 if word
and (key
in words
[lang
]):
3460 oldword
= words
[lang
][key
].get(lang
)
3461 if oldword
and (word
!= oldword
):
3463 print "%-16s" % lang
, oldword
, '!=', word
3464 print " "*18, unicode(words
[lang
][key
].pruned())
3465 print " "*18, unicode(entry
.pruned())
3466 words
[lang
][key
] = entry
3467 print u
"%d Doppeleinträge." % doppelte
3470 # Teste Komplettieren/Zusammenfassen der Einträge::
3472 # test_completion_pruning(wordlist)
3478 # sprache = 'de-1901' # traditionell
3479 sprache
= 'de-1996' # Reformschreibung
3480 # sprache = 'de-x-versal' # ohne ß (Schweiz oder versal) allgemein
3481 # sprache = 'de-1901-x-versal' # ohne ß (Schweiz oder versal) "traditionell"
3482 # sprache = 'de-1996-x-versal' # ohne ß (Schweiz oder versal) "reformiert"
3483 # sprache = 'de-CH-1901' # ohne ß (Schweiz) "traditionell" ("süssauer")
3485 # worte = [entry.get(sprache) for entry in wordlist]
3486 # worte = [wort for wort in worte if wort]
3487 # print len(worte), u"Einträge für Sprachvariante", sprache
3489 # Zeilenrekonstruktion::
3491 # test_str_entry_str_conversion(wordfile)
3497 # .. [BCP47] A. Phillips und M. Davis, (Editoren.),
3498 # `Tags for Identifying Languages`, http://www.rfc-editor.org/rfc/bcp/bcp47.txt
3500 # .. _aktuelle Rechtschreibung:
3502 # .. [Rechtschreibregeln] Rat für deutsche Rechtschreibung,
3503 # `Deutsche Rechtschreibung – Regeln und Wörterverzeichnis`,
3504 # http://www.rechtschreibrat.com/regeln-und-woerterverzeichnis/
3506 # .. _traditionelle Rechtschreibung:
3508 # .. [Duden1991] Wissenschaftlicher Rat der Dudenredaktion (Editoren),
3509 # `Duden: Rechtschreibung der deutschen Sprache`,
3510 # Dudenverlag Mannheim, 1991.
3512 # .. _Wortliste der deutschsprachigen Trennmustermannschaft:
3513 # ../../../dokumente/README.wortliste
3515 # .. _wortliste: ../../../wortliste