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
1829 # """"""""""""""""""""
1831 # (siehe `fremdwortsilben()`_, `verblasst()`_)::
1833 wort
= fremdwortsilben(wort
)
1834 wort
= verblasst(wort
)
1839 # K75: Trenne nie st.
1841 # Ersetze 's-t' mit '-st':
1843 # >>> ableitung1901(u'Diens-te')
1845 # >>> print ableitung1901(u'wuss-te', keep_key=True)
1848 # Aber Trennung von s-theta erlaubt (nach K74):
1850 # >>> print ableitung1901(u'Äs-thet')
1855 wort
= re
.sub(u
'(?<!s)s-t(?!h)', u
'-st', wort
)
1861 # K76: Trenne 'ck' als 'k-k'.
1863 # Ersetze '-ck' mit '{ck/k-k}':
1865 # >>> ableitung1901(u'Ha-cke')
1867 # >>> ableitung1901(u'Acker')
1869 # >>> ableitung1901(u'Got-tes=acker')
1870 # u'Got-tes=a{ck/k-k}er'
1871 # >>> ableitung1901(u'Aal=beck')
1873 # >>> ableitung1901(u'be<spick-te')
1878 wort
= wort
.replace(u
'-ck', u
'{ck/k-k}')
1879 wort
= re
.sub(u
'(?<=[AEIOUYÄÖÜaeiouyäöü])ck(?=[aeiouyäöü])',
1883 # Keine Trennung nach nur einem Buchstaben am Wortanfang:
1885 # >>> ableitung1901(u'Es-te')
1887 # >>> print ableitung1901(u'Nord=os-ten')
1889 # >>> print ableitung1901(u'Po-ly<es-ter')
1892 # Im Wort sind Einvokalsilben erlaubt (aber nicht immer günstig):
1894 # >>> print ableitung1901(u'the-is-tisch')
1899 wort
= re
.sub(u
'((?<=[=<].)|(?<=^.))-', ur
'', wort
)
1904 # Rechtschreibänderungen
1905 # ~~~~~~~~~~~~~~~~~~~~~~
1907 # Diese Regeln ändern die Schreibung des ungetrennten Worts (und somit den
1908 # Schlüssel im Langformat der Wortliste).
1913 # K38: Kein "ss" und "sst" am Silbenende (ungetrenntes "ss" nur in Ausnahmen)
1914 # (Andererseit ist 'ßt' und Schluss-ß auch in de-1996 möglich (langer Vokal).)
1916 # Ersetze ungetrenntes 'ss' mit 'ß':
1918 # >>> print ableitung1901(u'passt')
1920 # >>> print ableitung1901(u'Hass')
1922 # >>> print ableitung1901(u'Fass=brau-se')
1924 # >>> print ableitung1901(u'wuss-te')
1927 # ß steht für inlautendes ss, wenn ein 'e' ausfällt (und der Ausfall nicht
1928 # durch Apostroph angedeutet wird)
1930 # >>> print ableitung1901(u'wäss-rig')
1932 # >>> print ableitung1901(u'an<ge<mess-ner')
1934 # >>> print ableitung1901(u'duss-lig')
1936 # >>> print ableitung1901(u'bissl')
1939 # Keine Wandlung zu "ß":
1940 # getrenntes Doppel-s (s-s)
1942 # >>> print ableitung1901(u'Was-ser')
1945 # Vokal folgt (Fremdwörter):
1946 # >>> print ableitung1901(u'Com-tesse')
1949 # Großbuchstabe folgt
1951 # >>> print ableitung1901(u'WissZeitVG') # Abkürzung
1954 # Drei oder mehr 's' = Lautmalerei → erhalten:
1956 # >>> print ableitung1901(u'pssst')
1961 wort
= re
.sub(u
'(?<=[^s])ss(?=[^aeiouyäöüA-Zs]|$)', ur
'ß', wort
)
1963 # Unterdrückung der Trennstelle nach "…ß=er" und "…ß=en" nicht nötig:
1965 # >>> print ableitung1901(u'hass=er<.füllt')
1970 wort
= re
.sub(ur
'ß(=+)e([rn][<-]+)\.', ur
'ß\1e\2', wort
)
1972 # Dreikonsonantenregel
1973 # """"""""""""""""""""
1975 # K78: Zusammensetzungen, bei denen von drei zusammenstoßenden gleichen
1976 # Konsonanten einer entfällt (K15), schreibt man bei Silbentrennung wieder
1977 # mit allen drei Konsonanten.
1979 # Ersetze 'xx=x' xit '{xx/xx=x}' (für alle Konsonanten vor Selbstlaut)
1981 # >>> print ableitung1901(u'Kipp=pflug')
1983 # >>> print ableitung1901(u'Kipp=punkt')
1985 # >>> print ableitung1901(u'Ab<fall=la-ger')
1986 # Ab<fa{ll/ll=l}a-.ger
1987 # >>> print ableitung1901(u'All<lie-be')
1989 # >>> print ableitung1901(u'hell>licht')
1991 # >>> print ableitung1901(u'Pro<gramm==maß=nah-me')
1992 # Pro<gra{mm/mm==m}aß=nah-me
1996 wort
= re
.sub(ur
'([bfglmnprt])\1([<=>]+)\1(?=[aeiouyäöü])',
1997 ur
'{\1\1/\1\1\2\1}', wort
)
1999 # Unterdrücken der Trennung nach nur einem Buchstaben::
2001 wort
= re
.sub(ur
'(?<=[=>].}[aeiouyäöü])([-<])\.?', ur
'\1.', wort
)
2008 # Ein-Vokal-Silben auch schon 1901 erlaubt:
2010 # >>> print ableitung1901(u'ver<knäu-e-le')
2014 # versalschreibung()
2015 # ------------------
2016 # Ersetze 'ß' mit 'ss', trenne je nach Sprachvarietät `lang`:
2018 # >>> from wortliste import versalschreibung
2019 # >>> print versalschreibung(u'paßt')
2021 # >>> print versalschreibung(u'Dar-ßer')
2023 # >>> print versalschreibung(u'bü-ßen', 'de-CH')
2025 # >>> print versalschreibung(u'bü-ßen', 'de-1996-x-versal')
2027 # >>> print versalschreibung(u'bü-ßen', 'de-CH-1901')
2029 # >>> print versalschreibung(u'äßen', 'de-CH-1901')
2031 # >>> print versalschreibung(u'auf<äßen', 'de-CH-1901')
2033 # >>> print versalschreibung(u'auf<eßt', 'de-CH-1901')
2035 # >>> print versalschreibung(u'Groß=se-gel', 'de-1901-x-versal')
2037 # >>> print versalschreibung(u'Groß=se-gel', 'de-CH-1901')
2039 # >>> print versalschreibung(u'Paß=sy-ste-me', 'de-CH-1901')
2041 # >>> print versalschreibung(u'Pro<zeß=en-de', 'de-CH-1901')
2043 # >>> print versalschreibung(u'Pro<zess=en-.de', 'de-CH-1901')
2045 # >>> print versalschreibung(u'Fluß==sy-stem', 'de-CH-1901')
2047 # >>> print versalschreibung(u'Meß==sen-der', 'de-CH-1901')
2052 def versalschreibung(wort
, lang
='de'):
2054 if not u
'ß' in wort
:
2057 wort
= wort
.replace(u
'ß', u
'ss')
2059 # Trennung von Ersatz-ss in de-CH und de-1996 nach Sprechsilbenregel::
2061 if '1901-x-versal' not in lang
:
2062 # wort = re.sub(u'(?<=[aeiouyäöü])-\.?ss', u's-s', wort)
2063 wort
= re
.sub(u
'-\.?ss(?=[aeiouyäöü])', u
's-s', wort
)
2064 wort
= re
.sub(u
'(?<=^[aeiouyäöü])ss(?=[aeiouyäöü])', u
's-s', wort
)
2065 wort
= re
.sub(u
'(?<=[=<][aeiouyäöü])ss(?=[aeiouyäöü])', u
's-s', wort
)
2067 # Unterdrückung irreführender Trennung::
2069 wort
= re
.sub(u
'ss(=+)(en|er)([<-])\.?', ur
'ss\1\2\3.', wort
)
2071 # Dreikonsonantenregel für Ersatz-ss in de-CH-1901::
2073 if 'CH-1901-x-dreikonsonanten' in lang
:
2074 wort
= re
.sub(u
'ss(=+)s(?=[aeiouyäöü])', ur
'{ss/ss\1s}', wort
)
2075 # Unterdrücken der Trennung nach nur einem Buchstaben und irreführender Trennungen
2076 wort
= re
.sub(ur
'(?<=[=>]s}[aeiouyäöü])([-<])\.?', ur
'\1.', wort
)
2077 # wort = re.sub(ur'(?<===s}[aeiouyäöü])([-<])\.?', ur'\1.', wort) # Reißverschus=sy-.stem
2078 wort
= re
.sub(u
'(?<=[=>]s})(en|er)([<-])\.?', ur
'\1\2.', wort
)
2083 # Kurzformat in Langformat
2084 # ------------------------
2086 # Zusätzlich benötigte Felder werden automatisch erzeugt. Ein Kurzeintrag kann
2087 # mehrere Langeinträge ergeben:
2089 # >>> from wortliste import short2long
2090 # >>> for line in short2long([u"Diens-te", u"Ge<biss"]):
2091 # ... print unicode(line)
2092 # Dienste;-2-;Dien-ste;Diens-te
2093 # Gebiss;-2-;-3-;Ge<biss;Ge<biss
2094 # Gebiß;-2-;Ge<biß;-4-
2096 # >>> for line in short2long([u"Ge<schoss", u"Ge<schoß # österr."]):
2097 # ... print unicode(line)
2098 # Geschoss;-2-;-3-;Ge<schoss;Ge<schoss
2099 # Geschoß;Ge<schoß # österr.
2105 def short2long(lines
, sort
=True, prune
=True):
2106 """Convert sequence of lines in ShortEntry format to WordEntry instances.
2109 # Sammeln in Liste und Dictionary:
2110 words
= {} # zum Zusammenfassen
2114 shortentry
= ShortEntry(line
)
2115 shortentry
.complete()
2116 for entry
in shortentry
.wordentries(prune
=False):
2117 key
= entry
.key().lower()
2118 try: # Eintrag mit gleichem Schlüssel vorhanden?
2119 altentry
= words
[key
]
2120 except KeyError: # nein -> neuer Eintrag
2122 entries
.append(entry
)
2125 if entry
[3]: # de-1996 non-empty
2126 entry
.merge(altentry
, prune
=False, allow_alternatives
=True)
2127 # Alternativen Eintrag "in-place" ersetzen:
2128 for i
, word
in enumerate(entry
):
2130 altentry
.comment
= entry
.comment
2132 altentry
.merge(entry
, prune
=False, allow_alternatives
=True)
2133 except AssertionError as e
:
2134 sys
.stderr
.write(unicode(e
).encode('utf8')+'\n')
2135 entries
.append(entry
)
2136 except IndexError: # Leerer Eintrag (Kommentar)
2137 entries
.append(entry
)
2141 for entry
in entries
:
2145 entries
.sort(key
=sortkey_duden
)
2151 # Kommentare bleiben erhalten:
2153 # >>> for line in short2long([u'Aal=an-geln', u'# toller Kommentar'],
2155 # ... print unicode(line)
2156 # Aalangeln;Aal=an-geln
2157 # # toller Kommentar
2159 # Beim Sortieren werden Kommenare an den Beginn geschrieben.
2160 # >>> for line in short2long([u'# erster Kommentar',
2161 # ... u'Aal=an-geln',
2162 # ... u'# zweiter Kommentar']):
2163 # ... print unicode(line)
2164 # # erster Kommentar
2165 # # zweiter Kommentar
2166 # Aalangeln;Aal=an-geln
2169 # Langformat in Kurzformat
2170 # ------------------------
2172 # Optionale Felder werden weggelassen, wenn sie mit dem automatisch erzeugten
2173 # Inhalt übereinstimmen:
2175 # >>> from wortliste import long2short
2176 # >>> for entry in long2short([u"Dienste;-2-;Dien-ste;Diens-te"]):
2177 # ... print unicode(entry)
2180 # Zusammengehörige Einträge werden zusammengefasst:
2182 # >>> for line in long2short([u"Großanlass;-2-;-3-;Groß=an<lass",
2183 # ... u"Großanlaß;-2-;Groß=an<laß;-4-",
2184 # ... u"Grossanlass;-2-;-3-;-4-;Gross=an<lass"]):
2185 # ... print unicode(line)
2191 # Bei einigen Wörtern ist die ß-ss-Beziehung nicht eindeutig:
2193 # ======= ======== =========
2194 # de1901 de-1996 de-CH
2195 # ======= ======== =========
2198 # Geschoß Geschoß Geschoss
2199 # Geschoß Geschoss Geschoss
2200 # ======= ======== =========
2202 # Daher kann es vorkommen, dass ein Langform-Eintrag Beiträge von
2203 # verschiedenen Kurzformen erhält. So erzeugen, z.B. sowohl
2204 # „Masse“ als auch „Maße“ einen Langeintrag mit Schlüssel „Masse“:
2206 # >>> for entry in short2long([u'Mas-se']): print unicode(entry)
2208 # >>> for entry in short2long([u'Ma-ße']): print unicode(entry)
2209 # Masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se
2212 # In der Langform werden Alternativen in ein Feld geschrieben:
2214 # >>> for entry in short2long([u'Mas-se', u'Ma-ße']):
2215 # ... print unicode(entry)
2216 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[s-/-s]se;Mas-se;Mas-se
2219 # Sind die Alternativen bereits in der Quelle, bleiben sie in der Kurzform
2222 # >>> ml = [u'Masse;-2-;Mas-se;Mas-se;-5-;Ma[-s/s-]se;Mas-se;Mas-se',
2223 # ... u'Maße;Ma-ße']
2224 # >>> for entry in long2short(ml):
2225 # ... print unicode(entry)
2226 # Mas-se;Mas-se;Mas-se;Ma[-s/s-]se
2227 # Ma-ße;Ma-ße;Mas-se;Ma[-s/s-]se
2229 # Zurück in die Langform:
2231 # >>> for entry in short2long([u'Mas-se;Mas-se;Mas-se;Ma[-s/s-]se',
2232 # ... u'Ma-ße;Ma-ße;-3-;Ma[-s/s-]se']):
2233 # ... print unicode(entry)
2234 # Masse;-2-;Mas-se;Mas-se;-5-;Ma[-s/s-]se;Mas-se;Mas-se
2241 def long2short(lines
, prune
=True, drop_sz
=False):
2242 """Convert sequence of 8-column lines to ShortEntry instances."""
2243 words
= {} # Einträge mit `de`
2244 words_x
= {} # Einträge ohne `de`
2245 words_merged
= set() # Einträge die vollständig in andere einsortiert wurden
2246 entries
= [] # Rückgabewert: Liste der Kurzeinträge
2248 # Zeilen Einlesen, Wandeln und Sammeln::
2251 longentry
= WordEntry(line
)
2252 entry
= ShortEntry(longentry
, prune
=False)
2253 key
= entry
.key().lower() # Schlüssel ohne Großschreibung
2255 if not entry
: # reiner Kommentar oder leerer Eintrag
2257 entries
.append(entry
)
2260 # Einträge mit leerem ersten Feld werden später einsortiert:
2261 if prune
and not entry
[0]:
2262 words_x
[key
] = entry
2265 # Eintrag in `dictionary` und Liste:
2267 entries
.append(entry
)
2269 # Straffen (Weglassen von Feldern/Einträgen wenn möglich),
2270 # es sei denn, der Aufruf erfolgte mit `prune=False`::
2275 for entry
in entries
:
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()
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):
2295 # Anhängen aller Einträge mit leerem `de`-Feld, die nicht in
2296 # einen zugehörigen Eintrag einsortiert wurden an die Liste::
2298 for key
, entry
in words_x
.iteritems():
2299 # print key, unicode(entry)
2300 if key
in words_merged
:
2303 if len(entry
) > 2 and not entry
[3]: # de-1901-x-versal
2304 key
= join_word(entry
.getitem(3, substitute
=True)).lower()
2306 co_entry
= words_x
[key
]
2307 entry
.merge(co_entry
, prune
=False, start
=3)
2308 words_merged
.add(key
)
2309 except (KeyError, AssertionError):
2311 co_entry
= words
[key
]
2312 entry
.merge(co_entry
, prune
=False, start
=3)
2313 # print key, unicode(co_entry)
2314 except (KeyError, AssertionError):
2317 entries
.append(entry
)
2319 for entry
in entries
:
2320 entry
.prune(drop_sz
)
2326 # Separate Einträge ß-Schreibungen zusammenfassen:
2328 # Ein von 2 ß wird zu ss in de-1996:
2330 # >>> for entry in long2short([
2331 # ... u'Passstrasse;-2-;-3-;-4-;-5-;Pass=stra-sse;Pass=stras-se;Pass=stras-se',
2332 # ... u'Passstraße;-2-;-3-;Pass=stra-ße',
2333 # ... u'Paßstraße;-2-;Paß=stra-ße;-4-']):
2334 # ... print unicode(entry)
2339 # >>> for line in long2short([
2340 # ... u"Grossserie;-2-;-3-;-4-;-5-;Gross=se-rie;Gross=se-rie",
2341 # ... u"Großserie;Groß=se-rie"]):
2342 # ... print unicode(line)
2345 # kein ß in de-1996:
2347 # >>> for line in long2short([
2348 # ... u"Basssaite;-2-;-3-;Bass=sai-te;-5-;Bass=sai-te;Bass=sai-te",
2349 # ... u"Baßsaite;-2-;Baß=sai-te;-4-"]):
2350 # ... print unicode(line)
2353 # Zusätzliche Variante (Fremdwort vs. Lehnwort) in de-1901:
2355 # >>> for entry in short2long([u'Boss;Boss # en.']): print unicode(entry)
2357 # >>> for entry in long2short([u'Boss;Boss # en.']): print unicode(entry)
2359 # >>> for entry in long2short([u'Boss;Boss # en.', u'Boß;-2-;Boß;-3- # < en.']):
2360 # ... print unicode(entry)
2364 # Alternativschreibung in de-1996 (Geschoß, Löß):
2366 # >>> for entry in long2short([u'Geschoss;-2-;-3-;Ge<schoss;Ge<schoss',
2367 # ... u'Geschoß;Ge<schoß # österr. auch de-1996']):
2368 # ... print unicode(entry)
2370 # Ge<schoß # österr. auch de-1996
2372 # Eigennamen auf -ss:
2374 # >>> for entry in long2short([u'Vossstrasse;-2-;-3-;-4-;-5-;Voss=stra-sse;Voss=stras-se;Voss=stras-se',
2375 # ... u'Vossstraße;Voss=stra-ße',
2376 # ... u'Voßstraße;Voß=stra-ße']):
2377 # ... print unicode(entry)
2378 # Voss=stra-ße;Voss=stra-ße
2383 # >>> long2short(['# toller Kommentar'], prune=False)
2384 # [ShortEntry(u'# toller Kommentar')]
2385 # >>> long2short(['# toller Kommentar'], prune=True)
2386 # [ShortEntry(u'# toller Kommentar')]
2392 # Funktionen, die einen Trennstil (oder einen Aspekt eines Trennstils)
2393 # implementieren (siehe auch dokumentation/Trennstile.txt).
2398 # Entferne Alternativtrennungen bei einfachen und suffigierten Fremdwörtern:
2400 # Regelwerk (1996) § 112:
2401 # In Fremdwörtern können die Verbindungen aus Buchstaben für einen
2402 # Konsonanten + l, n oder r entweder entsprechend § 110 getrennt werden,
2403 # oder sie kommen ungetrennt auf die neue Zeile.
2405 # >>> from wortliste import fremdwortsilben
2407 # >>> fremdwoerter = (u'no-b-le Zy-k-lus Ma-g-net Fe-b-ru-ar '
2408 # ... u'Hy-d-rant Ar-th-ri-tis ger<i-a-t-ri-sche')
2409 # >>> for wort in fremdwoerter.split():
2410 # ... print wort, '->', fremdwortsilben(wort)
2412 # Zy-k-lus -> Zy-klus
2413 # Ma-g-net -> Ma-gnet
2414 # Fe-b-ru-ar -> Fe-bru-ar
2415 # Hy-d-rant -> Hy-drant
2416 # Ar-th-ri-tis -> Ar-thri-tis
2417 # ger<i-a-t-ri-sche -> ger<i-a-tri-sche
2419 # >>> for wort in fremdwoerter.split():
2420 # ... print wort, '->', fremdwortsilben(wort, 'modern')
2422 # Zy-k-lus -> Zyk-lus
2423 # Ma-g-net -> Mag-net
2424 # Fe-b-ru-ar -> Feb-ru-ar
2425 # Hy-d-rant -> Hyd-rant
2426 # Ar-th-ri-tis -> Arth-ri-tis
2427 # ger<i-a-t-ri-sche -> ger<i-at-ri-sche
2431 def fremdwortsilben(wort
, style
='etymologisch'):
2432 """Select in-word hyphenation of foreign words."""
2433 if style
== "modern": # Sprechsilbenregel
2434 return re
.sub(u
'-([bcdfgkptv]|th)-(?=[lrn])', u
'\\1-', wort
) # §112
2435 else: # morphematisch
2436 return re
.sub(u
'-([bcdfgkptv]|th)-(?=[lrn])', u
'-\\1', wort
) # K86, K87
2437 # Versuch: auch Alternativtrennung nach führendem Vokal:
2438 # Ap-ri-kose -> Apri-kose aber auch Ad-ler -> Adler (!)
2439 # return re.sub(u'(-|^[AEIOUÄÖÜaeiouäöü])([bcdfgkptv]|th|st)-(?=[lrn])',
2444 # K86 Untrennbar sind in Fremdwörtern die Verbindungen von Verschluß- und
2445 # Reibelauten mit l und r, ...
2447 # >>> fremdwoerter = (u'Pu-b-li-kum flexi-b-ler Zy-k-lone Qua-d-rat '
2448 # ... u'Spek-t-rum manö-v-rieren')
2449 # >>> for wort in fremdwoerter.split():
2450 # ... print wort, '->', fremdwortsilben(wort)
2451 # Pu-b-li-kum -> Pu-bli-kum
2452 # flexi-b-ler -> flexi-bler
2453 # Zy-k-lone -> Zy-klone
2454 # Qua-d-rat -> Qua-drat
2455 # Spek-t-rum -> Spek-trum
2456 # manö-v-rieren -> manö-vrieren
2458 # die Lautfolge st+r bleibt ungetrennt, wenn keine Wortfuge vorliegt.
2460 # >>> fremdwoerter = u'Di-st-rikt Magi-st-rat laku-st-risch ab-st-rakt'
2461 # >>> for wort in fremdwoerter.split():
2462 # ... print wort, '->', fremdwortsilben(wort)
2463 # Di-st-rikt -> Di-strikt
2464 # Magi-st-rat -> Magi-strat
2465 # laku-st-risch -> laku-strisch
2466 # ab-st-rakt -> ab-strakt
2468 # K 87 Untrennbar ist die Konsonantenverbindung "gn".
2470 # >>> fremdwoerter = u'Ma-g-net Pro-g-nose Si-g-net'
2471 # >>> for wort in fremdwoerter.split():
2472 # ... print wort, '->', fremdwortsilben(wort)
2473 # Ma-g-net -> Ma-gnet
2474 # Pro-g-nose -> Pro-gnose
2475 # Si-g-net -> Si-gnet
2477 # Keine Übergeneralisierung:
2479 # >>> woerter = u'Seg-ler bast-le Ad-ler'
2480 # >>> for wort in woerter.split():
2481 # ... print wort, '->', fremdwortsilben(wort)
2482 # Seg-ler -> Seg-ler
2483 # bast-le -> bast-le
2486 # wegen Übergeneralisierung nicht möglich:
2487 # Ap-ri-kose -> Apri-kose
2488 # ig-no-rie-ren -> igno-rie-ren
2494 # Entferne Alternativtrennungen nach §113 (verblasste Etymologie).
2496 # Regelwerk (1996) §113:
2497 # Wörter, die sprachhistorisch oder von der Herkunftssprache her gesehen
2498 # Zusammensetzungen oder Präfigierungen sind, aber nicht mehr als solche
2499 # empfunden oder erkannt werden, kann man entweder nach § 108 oder nach §
2500 # 109 bis § 112 trennen.
2502 # >>> from wortliste import verblasst
2503 # >>> blasse = (u'hi-n<auf he-r<an da-r<um Chry-s<an-the-me Hek-t<ar '
2504 # ... u'He-li-ko<p-ter in-te-r>es-sant Li-n<oleum Pä-d<a-go-gik')
2505 # >>> for wort in blasse.split():
2506 # ... print wort, '->', verblasst(wort)
2507 # hi-n<auf -> hin<auf
2510 # Chry-s<an-the-me -> Chrys<an-the-me
2511 # Hek-t<ar -> Hekt<ar
2512 # He-li-ko<p-ter -> He-li-ko<pter
2513 # in-te-r>es-sant -> in-ter>es-sant
2514 # Li-n<oleum -> Lin<oleum
2515 # Pä-d<a-go-gik -> Päd<ago-gik
2517 # >>> for wort in blasse.split():
2518 # ... print wort, '->', verblasst(wort, 'modern')
2519 # hi-n<auf -> hi-nauf
2522 # Chry-s<an-the-me -> Chry-san-the-me
2523 # Hek-t<ar -> Hek-tar
2524 # He-li-ko<p-ter -> He-li-kop-ter
2525 # in-te-r>es-sant -> in-te-res-sant
2526 # Li-n<oleum -> Li-noleum
2527 # Pä-d<a-go-gik -> Pä-da-go-gik
2529 # Ersetze, wenn zwischen Haupttrennstelle und Nebentrennstelle nur ein
2531 # (Die Haupttrennstelle kann vor oder nach der Nebentrennstelle liegen.)
2534 def verblasst(wort
, style
='morphematisch'):
2535 """Select hyphenation of foreign words with obscure etymology."""
2536 if style
== "modern": # Sprechsilbenregel
2537 wort
= re
.sub(u
'[<>=]+[.]*(.[-.]+)', u
'\\1', wort
)
2538 wort
= re
.sub(u
'([-.]+.)[<>=]+[.]*', u
'\\1', wort
)
2539 else: # morphematisch
2540 wort
= re
.sub(u
'([<>=]+[.]*.)[-.]+', u
'\\1', wort
)
2541 wort
= re
.sub(u
'[-.]+(.[<>=]+)', u
'\\1', wort
)
2548 # K44 „ſ“ (langes s) steht in Fremdwörtern...
2550 # >>> blasse = (u'tran<s-pirieren tran<s-zendent ab<s-tinent '
2551 # ... u'Ab<s-zess Pro-s<odie')
2552 # >>> for wort in blasse.split():
2553 # ... print wort, '->', verblasst(wort)
2554 # tran<s-pirieren -> tran<spirieren
2555 # tran<s-zendent -> tran<szendent
2556 # ab<s-tinent -> ab<stinent
2557 # Ab<s-zess -> Ab<szess
2558 # Pro-s<odie -> Pros<odie
2560 # Trennstellen können als ungünstig markiert sein:
2562 # >>> blasse = (u'Bür-ger=in<.i-ti-a-ti-ve Pä-..d<e-..rast')
2563 # >>> for wort in blasse.split():
2564 # ... print wort, '->', verblasst(wort)
2565 # Bür-ger=in<.i-ti-a-ti-ve -> Bür-ger=in<.iti-a-ti-ve
2566 # Pä-..d<e-..rast -> Päd<erast
2567 # >>> for wort in blasse.split():
2568 # ... print wort, '->', verblasst(wort, 'modern')
2569 # Bür-ger=in<.i-ti-a-ti-ve -> Bür-ger=ini-ti-a-ti-ve
2570 # Pä-..d<e-..rast -> Pä-..de-..rast
2576 # Füge Trennmöglichkeiten am Wortanfang und -ende zu, die nach K79 (bzw. §107
2577 # E2 des Regelwerkes) verboten sind aber in Notentexten gebraucht werden.
2579 # >>> from wortliste import scoretext
2580 # >>> scoretext(u'Abend')
2582 # >>> scoretext(u'Ra-dio')
2585 # Das gleiche gilt für Trennmöglichkeiten am Anfang/Ende von Teilwörtern:
2587 # >>> scoretext(u'Eis=ano-ma-lie')
2588 # u'Eis=a.no-ma-lie'
2589 # >>> scoretext(u'Ra-dio<phon')
2594 # >>> scoretext(u'Ai-chin-ger'), scoretext(u'Ai-da')
2595 # (u'Ai-chin-ger', u'A.i-da')
2596 # >>> scoretext(u'Ma-rie'), scoretext(u'Li-nie')
2597 # (u'Ma-rie', u'Li-ni.e')
2598 # >>> scoretext(u'Ta-too'), scoretext(u'Zoo<lo-gie')
2599 # (u'Ta-too', u'Zo.o<lo-gie')
2600 # >>> scoretext(u'A-pnoe'), scoretext(u'O-boe')
2601 # (u'A-pnoe', u'O-bo.e')
2602 # >>> scoretext(u'Plaque'), scoretext(u'treue')
2603 # (u'Plaque', u'treu.e')
2604 # >>> scoretext(u'Fon-due=pfan-ne'), scoretext(u'Aue')
2605 # (u'Fon-due=pfan-ne', u'Au.e')
2606 # >>> scoretext(u'Ge-nie'), scoretext(u'Iphi-ge-nie')
2607 # (u'Ge-nie', u'I.phi-ge-nie')
2608 # >>> scoretext(u'Ago-nie'), scoretext(u'Be-go-nie')
2609 # (u'A.go-nie', u'Be-go-ni.e')
2610 # >>> scoretext(u'Kom-pa-nie'), scoretext(u'Kas-ta-nie'), scoretext(u'Ge-ra-nie')
2611 # (u'Kom-pa-nie', u'Kas-ta-ni.e', u'Ge-ra-ni.e')
2613 # ungelöst: Knie / Kni.e # pl.
2616 def scoretext(word
):
2617 """Mark up leading one-letter syllables."""
2618 # Führender Vokal, gefolgt von Silbenanfang
2619 # (optionaler Konsonant (auch ch/ck/ph/rh/sch/sh/th) + Vokal)
2620 for match
in re
.finditer(u
'(^|[<=])([aeiouäöü])((.|ch|ck|ph|sch|th)?[aeiouäöü])',
2621 word
, flags
=re
.IGNORECASE
):
2622 # print match.groups()
2623 # Ausnahmen: Doppellaute, Diphtonge (außer A-i-da), Umlaute
2624 if (re
.search(u
'(aa|ae|ai|au|äu|ei|eu|oe|oo|ou|ue)',
2625 match
.group(0), flags
=re
.IGNORECASE
)
2626 and word
!= u
'Ai-da' or word
== u
'io-ta' ):
2628 word
= ''.join((word
[:match
.start()], match
.expand(u
'\\1\\2.\\3'),
2629 scoretext(word
[match
.end():])))
2631 # zwei Vokale am Wortende
2632 for match
in re
.finditer(u
'([aeiouäöü])([aeiouäöü])([<>=]|$)', word
):
2633 # if re.search(u'(oo)', match.group(0)):
2634 # if 'ie' in match.group(0) and re.search(u'([a]-nie)', word, flags=re.IGNORECASE):
2635 # sys.stderr.write(word+' '+match.group(0)+'\n')
2636 # Ausnahmen: Doppellaute, Diphtonge, Umlaute (außer "Oboe")
2637 if (re
.search(u
'(aa|ae|ai|au|äu|ee|ei|eu|oe|oi|ou|ui)', match
.group(0))
2638 and not word
[:match
.end()].endswith(u
'waii') # ! Hawaii
2639 and not word
[:match
.end()].endswith(u
'boe')): # ! Oboe
2641 # Ausnahmen mit Ausnahmen:
2642 # …oo außer zoo<, ...
2643 if 'oo' in match
.group(0) and match
.group(0) != 'oo<':
2645 # …ie außer "-(l)inie", Iphigenie, Kastanie, Geranie, Begonie
2646 if ('ie' in match
.group(0)
2647 and not word
[:match
.end()].endswith(u
'i-nie') # Linie,
2648 and not word
[:match
.end()].endswith(u
'ta-nie') # Kastanie,
2649 and not word
[:match
.end()].endswith(u
'ra-nie') # Geranie,
2650 and not word
[:match
.end()].endswith(u
'e-go-nie') # Begonie != Agonie
2651 and not word
== u
'Iphi-ge-nie'):
2653 # …ue (Plaque, Re-vue, vogue) außer "-tue, -aue, ... -äue"
2654 if 'ue' in match
.group(0) and re
.search(u
'[^aeät]ue([<>=]|$)',
2655 word
[:match
.end()], flags
=re
.IGNORECASE
):
2658 word
= ''.join((word
[:match
.start()], match
.expand(u
'\\1.\\2\\3'),
2659 scoretext(word
[match
.end():])))
2664 # keine_einzelvokale()
2665 # --------------------
2667 # Traditionell enthalten die TeX-Trennmuster keine Trennstellen deren Abstand
2668 # zur Nachbartrennstelle einen Buchstaben beträgt. Dies ist eine *ästhetische*
2669 # Entscheidung um „Flatterbuchstaben“ zu vermeiden
2671 # Bei einvokalischen Silben im Wortinneren, nimm die zweite:
2673 # >>> from wortliste import keine_einzelvokale
2674 # >>> einzelne = (u'The-a-ter me-ri-di-.o-nal')
2675 # >>> for wort in einzelne.split():
2676 # ... print wort, '->', keine_einzelvokale(wort)
2677 # The-a-ter -> Thea-ter
2678 # me-ri-di-.o-nal -> me-ri-dio-nal
2680 # Allerdings nicht, wenn die erste Trennstelle unterdrückt ist:
2682 # >>> einzelne = (u'La-sal-le-a-.ner Athe-i-.sten')
2683 # >>> for wort in einzelne.split():
2684 # ... print wort, '->', keine_einzelvokale(wort)
2685 # La-sal-le-a-.ner -> La-sal-le-a-.ner
2686 # Athe-i-.sten -> Athe-i-.sten
2690 def keine_einzelvokale(wort
):
2691 """Drop hyphenation marker after single vowels."""
2692 return re
.sub(u
'-[.]*([aeiouyäöü])(?=-[^.])', u
'\\1', wort
)
2698 # Entferne die explizit als ungünstig gekennzeichneten Trennstellen:
2700 # >>> from wortliste import unguenstig
2701 # >>> unguenstig(u'Text=il<..lu-stra-ti-.on')
2702 # u'Text=illu-stra-tion'
2703 # >>> unguenstig(u'Text=il<..lu-stra-ti-.on', level=2)
2704 # u'Text=illu-stra-ti-on'
2708 def unguenstig(wort
, level
=1):
2709 """Drop bad hyphenations."""
2710 marker
= r
'\.' * level
2711 wort
= re
.sub(u
'[-=<>]+%s+'%marker
, u
'', wort
)
2712 return wort
.replace(u
'.', u
'')
2720 # Trennzeichen entfernen::
2722 def join_word(wort
, assert_complete
=False):
2724 # Einfache Trennzeichen:
2726 # == ================================================================
2727 # \= Trennstelle an Wortfugen (Wort=fu-ge)
2728 # \< Trennstelle nach Präfix (Vor<sil-be)
2729 # \> Trennstelle vor Suffix (Freund>schaf-ten)
2730 # \- Nebentrennstelle (ge-hen)
2731 # \. unerwünschte/ungünstige Trennstelle (Ge<schoss=en<.er-gie)
2732 # == ================================================================
2736 for marker
in u
'=<>-.':
2737 wort
= wort
.replace(marker
, u
'')
2739 # Spezielle Trennungen für die traditionelle Rechtschreibung
2740 # (siehe ../../dokumente/README.wortliste)::
2742 if '{' in wort
or '}' in wort
:
2743 wort
= wort
.replace(u
'{ck/kk}', u
'ck')
2744 wort
= wort
.replace(u
'{ck/k', u
'k')
2745 wort
= wort
.replace(u
'k}', u
'k')
2746 # Konsonanthäufungen an Wortfuge: '{xx/xxx}' -> 'xx':
2747 wort
= re
.sub(ur
'\{(.)\1/\1\1\1\}', ur
'\1\1', wort
)
2748 # schon getrennt: ('{xx/xx' -> 'xx' und 'x}' -> 'x'):
2749 wort
= re
.sub(ur
'\{(.)\1/\1\1$', ur
'\1\1', wort
)
2750 wort
= re
.sub(ur
'^(.)\}', ur
'\1', wort
)
2752 # Trennstellen in doppeldeutigen Wörtern::
2754 if '[' in wort
or ']' in wort
:
2755 wort
= re
.sub(ur
'\[(.*)/\1\]', ur
'\1', wort
)
2757 wort
= re
.sub(ur
'\[([^/\[]+)$', ur
'\1', wort
)
2758 wort
= re
.sub(ur
'^([^/\]]+)\]', ur
'\1', wort
)
2760 # Test auf verbliebene komplexe Trennstellen::
2763 for spez
in u
'[{/}]':
2765 raise AssertionError('Spezialtrennung %s, %s' %
2766 (wort
.encode('utf8'), wort
.encode('utf8')))
2773 # Zerlege ein Wort mit Trennzeichen in eine Liste von Silben und eine Liste
2776 # >>> from wortliste import zerlege
2778 # >>> zerlege(u'Haupt=stel-le')
2779 # ([u'Haupt', u'stel', u'le'], [u'=', u'-'])
2780 # >>> zerlege(u'Ge<samt=be<triebs=rats==chef')
2781 # ([u'Ge', u'samt', u'be', u'triebs', u'rats', u'chef'], [u'<', u'=', u'<', u'=', u'=='])
2782 # >>> zerlege(u'an<stands>los')
2783 # ([u'an', u'stands', u'los'], [u'<', u'>'])
2784 # >>> zerlege(u'An<al.pha-bet')
2785 # ([u'An', u'al', u'pha', u'bet'], [u'<', u'.', u'-'])
2790 silben
= re
.split(u
'[-·._<>=]+', wort
)
2791 trennzeichen
= re
.split(u
'[^-·._|<>=]+', wort
)
2792 return silben
, [tz
for tz
in trennzeichen
if tz
]
2797 # Gib einen String mit Trennmarkierung für abweichende Trennungen in
2798 # mehrdeutigen Wörtern zurück:
2800 # >>> from wortliste import alternatives
2801 # >>> alternatives(u'Mas-se', u'Ma-sse')
2803 # >>> alternatives(u'Ma-sse', u'Mas-se')
2808 def alternatives(wort1
, wort2
):
2810 wort1
= [c
for c
in wort1
]
2811 wort2
= [c
for c
in wort2
]
2814 for c1
, c2
in zip(wort1
, wort2
):
2818 for c1
, c2
in zip(wort1
.__reversed__(), wort2
.__reversed__()):
2823 return u
''.join(pre
+ [u
'['] + wort1
[len(pre
):-len(post
)] + [u
'/']
2824 + wort2
[len(pre
):-len(post
)] + [u
']'] + post
)
2829 # Fehler beim Übertragen von Trennstellen mit uebertrage()_::
2831 class TransferError(ValueError):
2832 def __init__(self
, wort1
, wort2
):
2833 msg
= u
'Inkompatibel: %s %s' % (wort1
, wort2
)
2834 ValueError.__init
__(self
, msg
.encode('utf8'))
2836 def __unicode__(self
):
2837 return str(self
).decode('utf8')
2843 # Übertrage die Trennzeichen von `wort1` auf `wort2`:
2845 # >>> from wortliste import uebertrage, TransferError
2847 # >>> uebertrage(u'Haupt=stel-le', u'Haupt·stel·le')
2850 # Auch teilweise Übertragung, von "kategorisiert" nach "unkategorisiert":
2852 # >>> print uebertrage(u'Haupt=stel-le', u'Haupt=stel·le')
2855 # >>> print uebertrage(u'Haupt·stel-le', u'Haupt=stel·le')
2858 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler')
2861 # >>> print uebertrage(u'Freund>schaf·ten', u'Freund-schaf-ten')
2864 # Übertragung doppelter Marker:
2866 # >>> print uebertrage(u'ver<<aus<ga-be', u'ver<aus<ga-be')
2869 # >>> print uebertrage(u'freund>lich>>keit', u'freund>lich>keit')
2872 # >>> print uebertrage(u'Amts==haupt=stel-le', u'Amts=haupt=stel-le')
2873 # Amts==haupt=stel-le
2875 # Kein Überschreiben doppelter Marker:
2876 # >>> print uebertrage(u'ver<aus<ga-be', u'ver<<aus<ga-be')
2879 # >>> print uebertrage(u'Amts=haupt=stel-le', u'Amts==haupt=stel·le')
2880 # Amts==haupt=stel-le
2882 # Erhalt des Markers für ungünstige Stellen:
2883 # >>> print uebertrage(u'An·al.pha·bet', u'An<al.pha-bet')
2886 # Keine Übertragung, wenn die Zahl oder Position der Trennstellen
2887 # unterschiedlich ist oder bei unterschiedlichen Wörtern:
2890 # ... uebertrage(u'Ha-upt=stel-le', u'Haupt=stel·le')
2891 # ... uebertrage(u'Haupt=ste-lle', u'Haupt=stel·le')
2892 # ... uebertrage(u'Waupt=stel-le', u'Haupt=stel·le')
2893 # ... except TransferError:
2896 # Übertragung auch bei unterschiedlicher Schreibung oder Position der
2897 # Trennstellen mit `strict=False` (für Abgleich zwischen Sprachvarianten):
2899 # >>> uebertrage(u'er-ster', u'ers·ter', strict=False)
2901 # >>> uebertrage(u'Fluß=bett', u'Fluss·bett', strict=False)
2903 # >>> uebertrage(u'ab>bei-ßen', u'ab>beis·sen', strict=False)
2905 # >>> print uebertrage(u'Aus<tausch=dien-stes', u'Aus-tausch=diens-tes', False)
2906 # Aus<tausch=diens-tes
2908 # Auch mit `strict=False` muß die Zahl der Trennstellen übereinstimmen
2909 # (Ausnahmen siehe unten):
2912 # ... uebertrage(u'Ha-upt=ste-lle', u'Haupt=stel·le', strict=False)
2913 # ... except TransferError:
2916 # Akzeptiere unterschiedliche Anzahl von Trennungen bei st und ck nach
2919 # >>> uebertrage(u'acht=ecki-ge', u'acht·e{ck/k·k}i·ge', strict=False)
2920 # u'acht=e{ck/k-k}i-ge'
2921 # >>> uebertrage(u'As-to-ria', u'Asto·ria', strict=False)
2923 # >>> uebertrage(u'Asto-ria', u'As·to·ria', strict=False)
2925 # >>> uebertrage(u'So-fa=ecke', u'So·fa=e{ck/k-k}e', strict=False)
2926 # u'So-fa=e{ck/k-k}e'
2927 # >>> uebertrage(u'Drei=ecks=ecke', u'Drei=ecks==e{ck/k-k}e', strict=False)
2928 # u'Drei=ecks==e{ck/k-k}e'
2930 # Mit ``upgrade=False`` werden nur unspezifische Trennstellen überschrieben:
2932 # >>> print uebertrage(u'an=stel-le', u'an<stel·le', upgrade=False)
2935 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler', upgrade=False)
2938 # >>> print uebertrage(u'Aus-stel-ler', u'Aus<stel-ler', upgrade=False)
2941 # >>> print uebertrage(u'vor<an<<stel-le', u'vor-an<stel·le', upgrade=False)
2946 selbstlaute
= u
'aeiouäöüAEIOUÄÖÜ'
2948 def uebertrage(wort1
, wort2
, strict
=True, upgrade
=True):
2950 silben1
, trennzeichen1
= zerlege(wort1
)
2951 silben2
, trennzeichen2
= zerlege(wort2
)
2952 # Prüfe strikte Übereinstimmung:
2953 if silben1
!= silben2
and strict
:
2954 if u
'<' in trennzeichen1
or u
'·' in trennzeichen2
:
2955 raise TransferError(wort1
, wort2
)
2958 # Prüfe ungefähre Übereinstimmung:
2959 if len(trennzeichen1
) != len(trennzeichen2
):
2960 # Selbstlaut + st oder ck?
2961 for s
in selbstlaute
:
2962 if (wort2
.find(s
+u
'{ck/k·k}') != -1 or
2963 wort2
.find(s
+u
'{ck/k-k}') != -1):
2964 wort1
= re
.sub(u
'%sck([%s])'%(s
,selbstlaute
),
2965 ur
'%s-ck\1'%s, wort1
)
2966 silben1
, trennzeichen1
= zerlege(wort1
)
2967 if wort2
.find(s
+u
's·t') != -1:
2968 wort1
= wort1
.replace(s
+u
'st', s
+u
's-t')
2969 silben1
, trennzeichen1
= zerlege(wort1
)
2970 elif wort1
.find(s
+u
's-t') != -1:
2971 wort1
= wort1
.replace(s
+u
's-t', s
+u
'st')
2972 silben1
, trennzeichen1
= zerlege(wort1
)
2973 # print u'retry:', silben1, trennzeichen1
2974 # immer noch ungleiche Zahl an Trennstellen?
2975 if len(trennzeichen1
) != len(trennzeichen2
):
2976 raise TransferError(wort1
, wort2
)
2978 # Baue wort3 aus silben2 und spezifischeren Trennzeichen:
2979 wort3
= silben2
.pop(0)
2980 for t1
,t2
in zip(trennzeichen1
, trennzeichen2
):
2981 if ((t2
== u
'·' and t1
!= u
'.') # unspezifisch
2983 ((t2
in (u
'-', u
'<') and t1
in (u
'<', u
'<<', u
'<=')) # Praefixe
2984 or (t2
in (u
'-', u
'>') and t1
in (u
'>', u
'>>', u
'=>')) # Suffixe
2985 or (t2
in (u
'-', u
'=') and t1
in (u
'=', u
'==', u
'===')) # W-fugen
2989 elif t2
== u
'.' and t1
not in u
'·.':
2993 wort3
+= silben2
.pop(0)
2997 # Übertrag kategorisierter Trennstellen zwischen den Feldern aller Einträge
3000 def sprachabgleich(entry
, vorbildentry
=None):
3003 return # allgemeine Schreibung
3005 mit_affix
= None # < oder >
3006 kategorisiert
= None # kein ·
3007 unkategorisiert
= None # mindestens ein ·
3008 gewichtet
= None # == oder <= oder =>
3009 for field
in entry
[1:]:
3010 if not field
: # -2-, -3-, ...
3012 if u
'{' in field
and u
'[' in field
: # Bi-ber==be[t=t/{tt/tt=t}]uch
3013 continue # zu komplex
3015 unkategorisiert
= field
3016 elif u
'<' in field
or u
'>' in field
:
3019 kategorisiert
= field
3020 if u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
3023 for field
in vorbildentry
[1:]:
3024 if not field
: # -2-, -3-, ...
3026 if u
'{' in field
and u
'[' in field
: # Bi-ber==be[t=t/{tt/tt=t}]uch
3027 continue # zu komplex
3028 if not mit_affix
and u
'<' in field
or u
'>' in field
:
3030 elif not kategorisiert
and unkategorisiert
and u
'·' not in field
:
3031 kategorisiert
= field
3032 if not gewichtet
and u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
3034 # print 've:', mit_affix, kategorisiert, unkategorisiert
3035 if mit_affix
and (kategorisiert
or unkategorisiert
or gewichtet
):
3036 for i
in range(1,len(entry
)):
3037 if not entry
[i
]: # -2-, -3-, ...
3039 if u
'<' not in entry
[i
] or u
'·' in entry
[i
]:
3041 entry
[i
] = uebertrage(mit_affix
, entry
[i
], strict
=False)
3042 except TransferError
, e
:
3043 if not '/' in entry
[i
]:
3044 print u
'Sprachabgleich:', unicode(e
)
3045 # print mit_affix+u':', unicode(entry)
3046 elif kategorisiert
and unkategorisiert
:
3047 for i
in range(1,len(entry
)):
3048 if u
'·' in entry
[i
]:
3050 entry
[i
] = uebertrage(kategorisiert
, entry
[i
], strict
=False)
3051 except TransferError
, e
:
3052 print u
'Sprachabgleich:', unicode(e
)
3053 # print kategorisiert, unicode(entry)
3055 for i
in range(1,len(entry
)):
3056 if u
'=' in entry
[i
] and not (
3057 u
'{' in entry
[i
] and u
'[' in entry
[i
]):
3059 entry
[i
] = uebertrage(gewichtet
, entry
[i
], strict
=False)
3060 except TransferError
, e
:
3061 print u
'Sprachabgleich:', unicode(e
)
3065 # Großschreibung in Kleinschreibung wandeln und umgekehrt
3067 # Diese Version funktioniert auch für Wörter mit Trennzeichen (während
3068 # str.title() nach jedem Trennzeichen wieder groß anfängt)
3070 # >>> from wortliste import toggle_case
3071 # >>> toggle_case(u'Ha-se')
3073 # >>> toggle_case(u'arm')
3075 # >>> toggle_case(u'frei=bier')
3077 # >>> toggle_case(u'L}a-ger')
3080 # Keine Änderung bei Wörtern mit Großbuchstaben im Inneren:
3082 # >>> toggle_case(u'USA')
3084 # >>> toggle_case(u'iRFD')
3087 # >>> toggle_case(u'gri[f-f/{ff/ff')
3089 # >>> toggle_case(u'Gri[f-f/{ff/ff')
3094 def toggle_case(wort
):
3096 key
= join_word(wort
, assert_complete
=True)
3097 except AssertionError:
3102 return wort
[0].upper() + wort
[1:]
3109 # Duden-Sortierung für die Wortliste
3111 # >>> from wortliste import sortkey_duden
3112 # >>> sortkey_duden([u"Abflußröhren"])
3113 # u'abflussrohren a*bflu*szroehren'
3114 # >>> sortkey_duden([u"Abflußrohren"])
3115 # u'abflussrohren a*bflu*szro*hren'
3116 # >>> sortkey_duden([u"Abflussrohren"])
3119 # >>> s = sorted([[u"Abflußröhren"], [u"Abflußrohren"], [u"Abflussrohren"]],
3120 # ... key=sortkey_duden)
3121 # >>> print ', '.join(e[0] for e in s)
3122 # Abflussrohren, Abflußrohren, Abflußröhren
3126 # Ligaturen auflösen und andere "normalisierunde" Ersetzungen für den
3127 # (Haupt-)Sortierschlüssel (Akzente werden über ``unicodedata.normalize``
3136 # "Zweitschlüssel" zur Unterscheidung von Umlauten/SZ und Basisbuchstaben::
3138 umschrift_subkey
= {
3154 # Schlüssel für die alphabetische Sortierung gemäß Duden-Regeln.
3156 # Argument ist ein Eintrag im WordEntry oder ShortEntry Format oder
3157 # ein (Unicode) String:
3159 # >>> print sortkey_duden(weste) # WordEntry
3161 # >>> print sortkey_duden(dst) # ShortEntry
3164 # >>> print sortkey_duden(u'Stra-ße')
3166 # >>> print sortkey_duden([u'Büh-ne'])
3168 # >>> print sortkey_duden(u'Weiß=flog;Weiß=flog;-3-;-4-;-5-')
3169 # weissflog weiszflo*g
3170 # >>> print sortkey_duden(u'-1-;Meß=sen-der;-3-;-4-;-5-')
3171 # messsender meszsender
3172 # >>> print sortkey_duden(u'As-sen # (geogr. und Eigen-) Name\n')
3174 # >>> print sortkey_duden(u'aßen;aßen;-3-;-4-;-5-\n')
3176 # >>> print sortkey_duden(u'äßen\n')
3181 def sortkey_duden(entry
):
3183 # ggf. ungetrenntes Wort extrahieren oder generieren::
3185 if isinstance(entry
, list):
3188 except AttributeError:
3190 if len(entry
) == 1: # ein Muster pro Zeile, siehe z.B. pre-1901
3191 key
= join_word(key
)
3193 match
= re
.search(u
'^[-0-9;]*([^;\s]+)', entry
) # erstes volles Feld
3195 key
= match
.group(1)
3198 key
= join_word(key
)
3199 key
= re
.sub(u
'[0-9;]', ur
'', key
)
3201 # Großschreibung ignorieren:
3203 # Der Duden sortiert Wörter, die sich nur in der Großschreibung unterscheiden
3204 # "klein vor groß" (ASCII sortiert "groß vor klein"). In der
3205 # `Trennmuster-Wortliste` kommen Wörter nur mit der häufiger anzutreffenden
3206 # Großschreibung vor, denn der TeX-Trennalgorithmus ignoriert Großschreibung.
3215 skey
= key
.replace(u
'ß', u
'ss')
3217 # Restliche Akzente weglassen: Wandeln in Darstellung von Buchstaben mit
3218 # Akzent als "Grundzeichen + kombinierender Akzent". Anschließend alle
3219 # nicht-ASCII-Zeichen ignorieren::
3221 skey
= skey
.translate(umschrift_skey
)
3222 skey
= unicodedata
.normalize('NFKD', skey
)
3223 skey
= unicode(skey
.encode('ascii', 'ignore'))
3225 # "Zweitschlüssel" für das eindeutige Einsortieren von Wörtern mit
3226 # gleichem Schlüssel (Masse/Maße, waren/wären, ...):
3228 # * "*" nach aou für die Unterscheidung Grund-/Umlaut
3234 subkey
= key
.translate(umschrift_subkey
)
3235 skey
= u
'%s %s' % (skey
,subkey
)
3237 # Gib den Sortierschlüssel zurück::
3246 # Vergleiche zwei Sequenzen von `WordEntries` (genauer: alle Objekte, die
3247 # sich sinnvoll zu Unicode wandeln lassen).
3251 # >>> from wortliste import udiff
3252 # >>> print udiff([abbeissen, aalbestand], [abbeissen,dresz], 'alt', 'neu')
3256 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
3257 # -Aalbestand;Aal=be<stand # Test
3258 # +Dreß;-2-;Dreß;-4-
3260 # >>> udiff([abbeissen, aalbestand], [abbeissen, aalbestand], 'alt', 'neu')
3265 def udiff(a
, b
, fromfile
='', tofile
='',
3266 fromfiledate
='', tofiledate
='', n
=1, encoding
='utf8'):
3268 a
= [unicode(entry
).rstrip().encode(encoding
) for entry
in a
]
3269 b
= [unicode(entry
).rstrip().encode(encoding
) for entry
in b
]
3271 diff
= '\n'.join(difflib
.unified_diff(a
, b
, fromfile
, tofile
,
3272 fromfiledate
, tofiledate
, n
, lineterm
=''))
3275 return diff
.decode(encoding
)
3281 # Normalisierung und Expansion von Sprachtags nach [BCP47]_
3283 # >>> from wortliste import normalize_language_tag
3284 # >>> normalize_language_tag('de_AT-1901')
3285 # ['de-AT-1901', 'de-AT', 'de-1901', 'de']
3287 # >>> normalize_language_tag('de') # Deutsch, allgemeingültig
3289 # >>> normalize_language_tag('de_1901') # traditionell (Reform 1901)
3291 # >>> normalize_language_tag('de_1996') # reformiert (Reform 1996)
3293 # >>> normalize_language_tag('de_CH') # ohne ß (Schweiz oder versal)
3295 # >>> normalize_language_tag('de-x-versal') # versal
3296 # ['de-x-versal', 'de']
3297 # >>> normalize_language_tag('de-1901-x-versal') # versal
3298 # ['de-1901-x-versal', 'de-1901', 'de-x-versal', 'de']
3299 # >>> normalize_language_tag('de_CH-1996') # Schweiz traditionell (süssauer)
3300 # ['de-CH-1996', 'de-CH', 'de-1996', 'de']
3302 # 'de': 1, # Deutsch, allgemeingültig
3303 # 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
3304 # 'de-1996': 3, # reformierte Reformschreibung (1996)
3305 # 'de-x-versal': 4, # ohne ß (Schweiz oder versal) allgemein
3306 # # 'de-CH': 4, # Alias
3307 # 'de-1901-x-versal': 5, # ohne ß (Schweiz oder versal) "traditionell"
3308 # 'de-1996-x-versal': 6, # ohne ß (Schweiz oder versal) "reformiert"
3309 # # 'de-CH-1996': 6, # Alias
3310 # 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
3315 def normalize_language_tag(tag
):
3316 """Return a list of normalized combinations for a `BCP 47` language tag.
3319 tag
= tag
.replace('_','-')
3320 # split (except singletons, which mark the following tag as non-standard):
3321 tag
= re
.sub(r
'-([a-zA-Z0-9])-', r
'-\1_', tag
)
3323 subtags
= [subtag
.replace('_', '-') for subtag
in tag
.split('-')]
3324 base_tag
= [subtags
.pop(0)]
3325 # find all combinations of subtags
3326 for n
in range(len(subtags
), 0, -1):
3327 # for tags in unique_combinations(subtags, n):
3328 for tags
in itertools
.combinations(subtags
, n
):
3329 taglist
.append('-'.join(base_tag
+list(tags
)))
3338 # Teste Übereinstimmung des ungetrennten Wortes in Feld 1 mit den
3339 # Trennmustern nach Entfernen der Trennmarker. Schreibe Inkonsistenzen auf die
3342 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
3344 def test_keys(wortliste
):
3345 print u
"Teste Schlüssel-Trennmuster-Übereinstimmung:"
3347 for entry
in wortliste
:
3348 if isinstance(entry
, ShortEntry
):
3349 print u
"Wortliste im Kurzformat: überspringe Schlüssel-Test."
3351 # Test der Übereinstimmung ungetrenntes/getrenntes Wort
3354 for wort
in entry
[1:]:
3355 if not wort
: # leere Felder
3357 if key
!= join_word(wort
):
3359 print u
"\nkey '%s' != join_word('%s')" % (key
, wort
),
3360 if key
.lower() == join_word(wort
).lower():
3361 print(u
" Abgleich der Großschreibung mit"
3362 u
"`prepare-patch.py grossabgleich`."),
3367 # Finde Doppeleinträge (teste, ob jeder Schlüssel nur einmal vorkommt).
3368 # Schreibe Inkonsistenzen auf die Standardausgabe.
3370 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
3372 def test_uniqueness(wortliste
):
3376 for entry
in wortliste
:
3380 print "da ", unicode(words
[key
])
3381 print "neu", unicode(entry
)
3383 print u
"%d Doppeleinträge." % doppelte
3385 # Teste die Wandlung einer Zeile im "wortliste"-Format in eine
3386 # ``WordEntry``-Instanz und zurück::
3388 def test_str_entry_str_conversion(wordfile
):
3390 for line
in file(wordfile
.name
):
3391 line
= line
.rstrip().decode(wordfile
.encoding
)
3392 entry
= WordEntry(line
)
3393 if line
== unicode(entry
):
3397 print u
'+', unicode(entry
)
3399 print OK
, u
"Einträge rekonstruiert"
3402 # Teste Vervollständigung und Zusammenfassung von Einträgen::
3404 def test_completion_pruning(entries
):
3406 for entry
in entries
:
3407 new
= copy
.copy(entry
)
3411 patch
= udiff(entries
, reko
, 'wortliste', 'neu')
3415 print u
"alle Einträge rekonstruiert"
3419 # Aufruf von der Kommandozeile
3420 # ============================
3424 if __name__
== '__main__':
3427 # sys.stdout mit UTF8 encoding (wie in Python 3)
3428 sys
.stdout
= codecs
.getwriter('UTF-8')(sys
.stdout
)
3429 # sys.stderr = codecs.getwriter('UTF-8')(sys.stderr)
3431 print u
"Test der Werkzeuge und inneren Konsistenz der Wortliste"
3433 # Ein WordFile Dateiobjekt::
3436 wordfile
= WordFile(sys
.argv
[1], format
='auto')
3438 wordfile
= WordFile('../../../wortliste')
3439 # wordfile = WordFile('../../../wlst', format='auto') # wortliste im Kurzformat
3440 # wordfile = WordFile('neu.todo')
3441 # wordfile = WordFile('neu-kurz.todo', format='f5')
3442 # wordfile = WordFile('korrektur.todo', format='auto')
3444 # Format bestimmen::
3446 print "Datei: '%s'" % wordfile
.name
, u
"Format:", wordfile
.format
3448 # Liste der Datenfelder (die Klasseninstanz als Argument für `list` liefert
3449 # den Iterator über die Felder, `list` macht daraus eine Liste)::
3451 wordlist
= list(wordfile
)
3452 print len(wordlist
), u
"Zeilen"
3454 # Ein Wörterbuch (dict Instanz)::
3456 wordfile
.seek(0) # Pointer zurücksetzen
3457 words
= wordfile
.asdict()
3458 print len(words
), u
"Wörterbucheinträge"
3460 # Test auf Doppeleinträge::
3462 if len(words
) != len(wordlist
):
3463 test_uniqueness(wordlist
)
3466 # Teste Schlüssel-Trennmuster-Übereinstimmung::
3468 if isinstance(wordlist
[0], WordEntry
):
3471 # Teste Eintrags-Konsistenz im Kurzformat::
3473 if isinstance(wordlist
[0], ShortEntry
):
3475 langs
= ("de", "de-1901", "de-CH", "de-1901-x-versal", "de-CH-1901")
3479 for entry
in wordlist
:
3482 word
= entry
.get(lang
)
3483 key
= join_word(word
)
3484 if word
and (key
in words
[lang
]):
3485 oldword
= words
[lang
][key
].get(lang
)
3486 if oldword
and (word
!= oldword
):
3488 print "%-16s" % lang
, oldword
, '!=', word
3489 print " "*18, unicode(words
[lang
][key
].pruned())
3490 print " "*18, unicode(entry
.pruned())
3491 words
[lang
][key
] = entry
3492 print u
"%d Doppeleinträge." % doppelte
3495 # Teste Komplettieren/Zusammenfassen der Einträge::
3497 # test_completion_pruning(wordlist)
3503 # sprache = 'de-1901' # traditionell
3504 sprache
= 'de-1996' # Reformschreibung
3505 # sprache = 'de-x-versal' # ohne ß (Schweiz oder versal) allgemein
3506 # sprache = 'de-1901-x-versal' # ohne ß (Schweiz oder versal) "traditionell"
3507 # sprache = 'de-1996-x-versal' # ohne ß (Schweiz oder versal) "reformiert"
3508 # sprache = 'de-CH-1901' # ohne ß (Schweiz) "traditionell" ("süssauer")
3510 # worte = [entry.get(sprache) for entry in wordlist]
3511 # worte = [wort for wort in worte if wort]
3512 # print len(worte), u"Einträge für Sprachvariante", sprache
3514 # Zeilenrekonstruktion::
3516 # test_str_entry_str_conversion(wordfile)
3522 # .. [BCP47] A. Phillips und M. Davis, (Editoren.),
3523 # `Tags for Identifying Languages`, http://www.rfc-editor.org/rfc/bcp/bcp47.txt
3525 # .. _aktuelle Rechtschreibung:
3527 # .. [Rechtschreibregeln] Rat für deutsche Rechtschreibung,
3528 # `Deutsche Rechtschreibung – Regeln und Wörterverzeichnis`,
3529 # http://www.rechtschreibrat.com/regeln-und-woerterverzeichnis/
3531 # .. _traditionelle Rechtschreibung:
3533 # .. [Duden1991] Wissenschaftlicher Rat der Dudenredaktion (Editoren),
3534 # `Duden: Rechtschreibung der deutschen Sprache`,
3535 # Dudenverlag Mannheim, 1991.
3537 # .. _Wortliste der deutschsprachigen Trennmustermannschaft:
3538 # ../../../dokumente/README.wortliste
3540 # .. _wortliste: ../../../wortliste