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)
15 """Hilfsmittel für die Arbeit mit der `Wortliste`"""
19 # Die hier versammelten Funktionen und Klassen dienen der Arbeit an und
20 # mit der freien `Wortliste der deutschsprachigen Trennmustermannschaft`_
21 # ("Lembergsche Liste")
37 # Klasse zum Lesen und Schreiben der `Wortliste`::
46 # Das Argument ``format`` ist "f8", "f5", oder "auto":
48 # :f8: das originale, maximal 8-spaltige Wortlisten-Format,
49 # :f5: das neue, maximal 5-spaltige Wortlisten-Format,
50 # :auto: bestimme das Format automatisch.
54 def __init__(self
, name
, mode
='r', encoding
='utf8', format
='f8'):
56 self
.encoding
= encoding
57 file.__init
__(self
, name
, mode
)
58 # Dateiformat bestimmen und die Eintrags-Klasse setzen:
59 self
.set_entry_class(format
)
61 # Klasse zum Verarbeiten der Zeilen setzen.
62 # Mit self.format == "auto", werden die ersten Zeilen der Datei untersucht
63 # und der Pointer zurückgesetzt.
67 def set_entry_class(self
, format
, search_limit
=1000):
69 assert(format
in ('f8', 'f5', 'auto'))
70 self
.format
= format
# default
71 self
.entry_class
= WordEntry
# default
74 # Format anhand des Dateiinhalts bestimmen:
77 # nur ein Feld oder erstes Feld leer oder mit Trennzeichen:
78 if len(e
) == 1 or e
and re
.search(u
'[-=<>]', e
[0]):
81 # mehr als 5 Felder: 8-Felder-Format (Langform)
88 if self
.format
== 'f5':
89 self
.entry_class
= ShortEntry
91 self
.seek(0) # Dateizeiger zurücksetzen
97 # Die spezielle Funktion `__iter__` wird aufgerufen wenn über eine
98 # Klasseninstanz iteriert wird.
99 # Sie liefert einen Iterator über die "geparsten" Zeilen (Datenfelder)
103 entry_class
= self
.entry_class
105 line
= self
.next().rstrip().decode(self
.encoding
)
106 yield entry_class(line
)
112 # Lies Datei und trage die Zeilen mit ungetrenntem Wort
113 # als `key` und den Datenfeldern als `value` in ein `dictionary`
114 # (assoziatives Array) ein::
119 words
[entry
[0]] = entry
125 # Schreibe eine Liste von `unicode` Strings (Zeilen ohne Zeilenendezeichen)
126 # in die Datei `destination`::
128 def writelines(self
, lines
, destination
, encoding
=None):
129 outfile
= codecs
.open(destination
, 'w',
130 encoding
=(encoding
or self
.encoding
))
131 # outfile.write(u'\n'.join(lines))
132 # outfile.write(u'\n')
133 outfile
.writelines(lines
)
138 # Schreibe eine Liste von Einträgen (WordEntry/ShortEntry Objekten) in
139 # die Datei `destination`::
141 def write_entries(self
, wortliste
, destination
, encoding
=None):
142 lines
= (unicode(entry
) for entry
in wortliste
)
143 self
.writelines(lines
, destination
, encoding
)
149 # Klasse für Einträge (Zeilen) der Wortliste
153 # >>> from wortliste import WordEntry, ShortEntry
155 # >>> aalbestand = WordEntry(u'Aalbestand;Aal=be<stand # Test')
156 # >>> print aalbestand
157 # Aalbestand;Aal=be<stand # Test
161 class WordEntry(list):
166 # Kommentare (aktualisiert, wenn Kommentar vorhanden)::
174 # 2. Wort mit Trennungen, falls für alle Varianten identisch,
176 # 3. falls Feld 2 leer, Trennung nach traditioneller Rechtschreibung
177 # 4. falls Feld 2 leer, Trennung nach reformierter Rechtschreibung (2006)
178 # 5. falls Feld 2 leer, Trennung für Wortform, die entweder in
179 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird
180 # und für traditionelle und reformierte Rechtschreibung identisch ist
181 # 6. falls Feld 5 leer, Trennung für Wortform, die entweder in
182 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
183 # traditionelle Rechtschreibung
184 # 7. falls Feld 5 leer, Trennung für Wortform, die entweder in
185 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
186 # reformierte Rechtschreibung (2006)
187 # 8. falls Feld 5 leer, Trennung nach (deutsch)schweizerischer
188 # Rechtschreibung; insbesondere Wörter mit "sss" gefolgt von
189 # einem Vokal, die wie andere Dreifachkonsonanten gehandhabt wurden
190 # (also anders, als der Duden früher vorgeschrieben hat), z.B.
193 # Rechtschreibvarianten (Tags nach [BCP47]_) (Die Zählung der Indizes beginn
197 'key': 0, # Schlüssel
198 'de': 1, # Deutsch, allgemeingültig
199 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
200 'de-1996': 3, # reformierte Reformschreibung (1996)
201 'de-x-GROSS': 4, # ohne ß (Schweiz oder versal) allgemein
202 'de-x-versal': 4, # Alias
203 # 'de-CH': 4, # Alias
204 'de-1901-x-GROSS': 5, # ohne ß (Schweiz oder versal) "traditionell"
205 'de-1901-x-versal': 5, # ohne ß (Schweiz oder versal) "traditionell"
206 'de-1996-x-GROSS': 6, # ohne ß (Schweiz oder versal) "reformiert"
207 'de-1996-x-versal': 6, # ohne ß (Schweiz oder versal) "reformiert"
208 # 'de-CH-1996': 6, # Alias
209 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
212 feldnamen
= ('key', 'de', 'de-1901', 'de-1996', 'de-x-versal',
213 'de-1901-x-versal', 'de-1996-x-versal', 'de-CH-1901')
218 # Bei gleicher Trennung werden Felder weggelassen. Bei der Auswahl eines
219 # Wortes in einer Rechtschreibvariante werden Ersatzfelder gewählt, falls das
220 # "Originalfeld" fehlt. Die folgenden Tupel listen die Ersatzfelder für das
221 # ensprechende Originalfeld.
223 # Bsp: Ersatz-Felder für 'de-CH-1901':
225 # >>> for i in WordEntry.ersatzfelder[WordEntry.feldindizes['de-CH-1901']]:
226 # ... print WordEntry.feldnamen[i]
234 # Ersatzindizes Index Nr Name
235 ersatzfelder
= (tuple(), # 0 1 key
237 (1,), # 2 3 'de-1901'
238 (1,), # 3 4 'de-1996'
239 (1,), # 4 5 'de-x-versal'
240 (4, 2, 1), # 5 6 'de-1901-x-versal'
241 (4, 3, 1), # 6 7 'de-1996-x-versal'
242 (5, 4, 2, 1), # 7 8 'de-CH-1901'
252 def __init__(self
, line
, delimiter
=u
';'):
254 self
.delimiter
= delimiter
256 # eventuell vorhandenen Kommentar abtrennen und speichern::
259 line
, comment
= line
.split(u
'#', 1)
260 self
.comment
= comment
.lstrip()
262 # print (line, self.comment)
264 if not line
: # kein Inhalt
267 # Zerlegen in Datenfelder, in Liste eintragen::
269 for field
in line
.split(delimiter
):
270 if field
.startswith(u
'-'):
276 # Rückverwandlung in String
277 # -----------------------------------
279 # Erzeugen eines Eintrag-Strings (Zeile) aus der Liste der Datenfelder und
282 # >>> unicode(aalbestand)
283 # u'Aalbestand;Aal=be<stand # Test'
284 # >>> leerkommentar = WordEntry(u'# Testkommentar')
287 # >>> unicode(leerkommentar)
292 def __unicode__(self
):
293 # Umnummerieren leerer Felder:
294 fields
= [field
or u
'-%d-' % (i
+1)
295 for (i
, field
) in enumerate(self
)]
296 line
= u
';'.join(fields
)
298 line
= u
' # '.join((line
, self
.comment
)).lstrip()
302 return unicode(self
).encode('utf8')
307 # Index des zur Sprachvariante gehörenden Datenfeldes:
309 # >>> aalbestand.lang_index('de')
311 # >>> aalbestand.lang_index('de-1901')
313 # >>> aalbestand.lang_index('de-1996')
315 # >>> aalbestand.lang_index('de-x-versal')
317 # >>> aalbestand.lang_index('de-1901-x-versal')
319 # >>> aalbestand.lang_index('de-1996-x-versal')
321 # >>> abbeissen = WordEntry(
322 # ... u'abbeissen;-2-;-3-;-4-;-5-;ab<bei-ssen;ab<beis-sen;ab<beis-sen')
323 # >>> print abbeissen.lang_index('de')
325 # >>> print abbeissen.lang_index('de-x-versal')
327 # >>> abbeissen.lang_index('de-1901-x-versal')
329 # >>> abbeissen.lang_index('de-1996-x-versal')
331 # >>> abbeissen.lang_index('de-CH-1901')
333 # >>> urlaubstipp = WordEntry(u'Urlaubstipp;-2-;-3-;Ur<laubs=tipp')
334 # >>> print urlaubstipp.lang_index('de')
336 # >>> print urlaubstipp.lang_index('de-1901')
338 # >>> print urlaubstipp.lang_index('de-1996')
340 # >>> print urlaubstipp.lang_index('de-x-versal')
342 # >>> print urlaubstipp.lang_index('de-1901-x-versal')
347 def lang_index(self
, lang
):
349 assert lang
in self
.feldindizes
, \
350 'Sprachvariante "%s" nicht in %s' % (lang
,
351 self
.feldindizes
.keys())
353 # Einfacher Fall: eine allgemeine Schreibweise::
356 if u
'ß' in self
[0] and lang
in ('de-CH-1901', 'de-1901-x-versal',
357 'de-CH-1996', 'de-1996-x-versal'):
362 # Spezielle Schreibung::
365 i
= self
.feldindizes
[lang
]
368 if lang
== 'de-CH-1901':
369 return self
.lang_index('de-1901-x-versal')
370 # Allgemeine Schweiz/versal Schreibung:
371 if i
> 4 and len(self
) == 5:
373 # versal == normal (kein sz):
374 if len(self
) == 4 and u
'ß' not in self
[0]:
375 if lang
in ('de-CH-1901', 'de-1901-x-versal'):
376 return self
.lang_index('de-1901')
377 elif lang
in ('de-CH-1996', 'de-1996-x-versal'):
378 return self
.lang_index('de-1996')
379 return None # Feld nicht vorhanden
381 if not feld
: # leeres Feld
389 # Bei Einträgen im 8-Feld-Format ist der Schlüssel gleich dem Inhalt des
390 # ersten Feldes. Das `lang` Argument wird ignoriert, da der Schlüssel (im
391 # Gegensatz zum 5-Feld-Format) stets eindeutig ist::
393 def key(self
, lang
=None):
394 """Gib den Schlüssel (ungetrenntes Wort) zurück."""
397 except IndexError: # reiner Kommentar oder leerer Eintrag
403 def getitem(self
, i
):
404 """Return item ``i`` or a subsititute"""
407 except IndexError: # Feld i nicht vorhanden
409 # Ersatzregeln anwenden
410 for _i
in self
.ersatzfelder
[i
]:
413 except IndexError: # Feld i nicht vorhanden
414 continue # nächsten `tag` versuchen
415 # Spezialfall: in Versalschreibung ungültige Ersatz-Wörter
416 if i
>= 4 and _i
<4 and u
'ß' in word
:
422 # getitem(): Gib Feld ``i`` oder Ersatzfeld zurück.
424 def getitem(self
, i
):
425 """Return item ``i`` or a subsititute"""
428 except IndexError: # Feld i nicht vorhanden
430 # Ersatzregeln anwenden
431 for _i
in self
.ersatzfelder
[i
]:
434 except IndexError: # Feld i nicht vorhanden
435 continue # nächsten `tag` versuchen
436 # Spezialfall: in Versalschreibung ungültige Ersatz-Wörter
437 if i
>= 4 and _i
<4 and u
'ß' in word
:
443 # Trennmuster für eine Sprachvariante ausgeben
445 # >>> aalbestand.get('de')
447 # >>> aalbestand.get('de-1901')
449 # >>> aalbestand.get('de-1996')
451 # >>> aalbestand.get('de-x-versal')
453 # >>> aalbestand.get('de-1901-x-versal')
455 # >>> aalbestand.get('de-1996-x-versal')
457 # >>> aalbestand.get('de-CH-1901')
460 # >>> weste = WordEntry(u'Weste;-2-;We-ste;Wes-te')
461 # >>> weste.get('de')
463 # >>> weste.get('de-1901')
465 # >>> weste.get('de-1996')
468 # >>> abbeissen.get('de')
470 # >>> abbeissen.get('de-x-versal')
472 # >>> abbeissen.get('de,de-x-versal')
474 # >>> abbeissen.get('de-1901-x-versal')
476 # >>> abbeissen.get('de,de-1901,de-1901-x-versal')
478 # >>> abbeissen.get('de-CH-1901')
484 for lang
in tags
.split(','):
485 word
= self
.getitem(self
.feldindizes
[lang
])
491 # Trennmuster für Sprachvariante setzen
493 # >>> abbeissen.set('test', 'de-1901-x-versal')
494 # >>> print abbeissen
495 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
497 # >>> abbeissen.set('test', 'de-1901')
498 # Traceback (most recent call last):
500 # IndexError: kann kein leeres Feld setzen
502 # >>> abbeissen.set('test', 'de-1901,de-1901-x-versal')
503 # >>> print abbeissen
504 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
508 def set(self
, word
, tags
):
509 for lang
in tags
.split(','):
510 i
= self
.lang_index(lang
)
517 raise IndexError, "kann kein leeres Feld setzen"
519 # setitem(): Feld ``i`` setzen.
521 # >>> entry = WordEntry(u'test;test')
522 # >>> entry.setitem(4, u's-x')
524 # test;test;-3-;-4-;s-x
525 # >>> entry.setitem(3, u'sz')
527 # test;test;-3-;sz;s-x
531 def setitem(self
, i
, word
):
532 while len(self
) < i
+1:
536 # complete(): Alle Felder setzen.
538 # >>> print str(aalbestand), len(aalbestand)
539 # Aalbestand;Aal=be<stand # Test 2
540 # >>> print aalbestand.completed()
541 # Aalbestand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand # Test
543 # >>> auffrass = WordEntry(u'auffrass;-2-;-3-;-4-;auf-frass')
544 # >>> print auffrass.completed()
545 # auffrass;-2-;-3-;-4-;auf-frass;auf-frass;auf-frass;auf-frass
547 # >>> fresssack= WordEntry(u'Fresssack;-2-;-3-;Fress=sack;Fress=sack')
548 # >>> print fresssack.completed()
549 # Fresssack;-2-;-3-;Fress=sack;Fress=sack;Fress=sack;Fress=sack;Fress=sack
551 # >>> line = u'Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-'
552 # >>> print WordEntry(line).completed()
553 # Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-;Fluss=schi{ff/ff=f}ahrt
555 # >>> line = u'Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße'
556 # >>> ackerstrasse = WordEntry(line).completed()
558 # [u'Ackerstra\xdfe', u'', u'A{ck/k-k}er=stra-\xdfe', u'Acker=stra-\xdfe', u'', u'', u'', u'']
563 for i
in range(len(self
),8):
564 self
.append(self
.getitem(i
))
567 neu
= copy
.copy(self
)
572 # Felder für Sprachvarianten zusammenfassen, wenn gleich
574 # >>> aalbestand.prune()
575 # >>> print aalbestand
576 # Aalbestand;Aal=be<stand # Test
577 # >>> auffrass.prune()
579 # auffrass;-2-;-3-;-4-;auf-frass
580 # >>> entry = WordEntry(u'distanziert;-2-;di-stan-ziert;di-stan-ziert')
583 # distanziert;di-stan-ziert
584 # >>> entry = WordEntry(u'Gauss;-2-;Gauss;Gauss;Gauss')
588 # >>> fresssack.prune()
589 # >>> print fresssack
590 # Fresssack;-2-;-3-;Fress=sack;Fress=sack
592 # >>> masse = WordEntry(u'Masse;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se;Mas-se')
597 # Aber nicht, wenn die Trennstellen sich unterscheiden:
599 # >>> abenddienste = WordEntry(
600 # ... u'Abenddienste;-2-;Abend=dien-ste;Abend=diens-te')
601 # >>> abenddienste.prune()
602 # >>> print abenddienste
603 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
604 # >>> ackerstrasse.prune()
605 # >>> print unicode(ackerstrasse)
606 # Ackerstraße;-2-;A{ck/k-k}er=stra-ße;Acker=stra-ße
607 # >>> entry = WordEntry(
608 # ... u'Schlammmasse;-2-;-3-;Schlamm=mas-se;-5-;-6-;Schlamm=mas-se;-8-')
611 # Schlammmasse;-2-;-3-;Schlamm=mas-se
612 # >>> entry = WordEntry(
613 # ... u'Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-')
616 # Flussschiffahrt;-2-;-3-;-4-;-5-;Fluss=schi{ff/ff=f}ahrt;-7-
622 if self
[7] == self
[5]: # de-CH-1901 gleich versal-1901
625 if self
[6] == self
[5]:
626 self
[4] = self
[5] # umschreiben auf versal-allgemein
629 elif (not(self
[4] or self
[5] or self
[6])
630 or self
[5] == self
[2] and self
[6] == self
[3]):
631 # alle leer oder ohne ß-Ersetzung
638 elif self
[1] == self
[4]: # de-x-versal == de
642 elif self
[2] == self
[3] == self
[4]:
643 self
[1] = self
[2] # Umschreiben auf de (allgemein)
648 if self
[3] == self
[2]: # de-1996 == de-1901
649 self
[1] = self
[2] # Umschreiben auf de (allgemein)
659 # Einträge zusammenfassen
661 # >>> entry = WordEntry(u'Abenddress;Abend=dress')
662 # >>> entry.merge(WordEntry(u'Abenddress;-2-;-3-;-4-;Abend=dress'))
663 # >>> print unicode(entry)
664 # Abenddress;Abend=dress
665 # >>> entry = WordEntry(u'Gauss;Gauss')
666 # >>> entry.merge(WordEntry(u'Gauss;-2-;-3-;-4-;Gauss'))
667 # >>> print unicode(entry)
669 # >>> masse.merge(WordEntry(u'Masse;-2-;-3-;-4-;-5-;Ma-sse;Mas-se;Mas-se'), allow_alternatives=True)
671 # Masse;-2-;Mas-se;Mas-se;-5-;[Mas-se/Ma-sse];Mas-se;Mas-se
675 def merge(self
, other
, allow_alternatives
=False, prune
=True):
679 for i
, (s_i
, o_i
) in enumerate(zip(self
,other
)):
688 if allow_alternatives
:
689 self
[i
] = u
'[%s/%s]' % (s_i
, o_i
)
693 # self.comment = other.comment or self.comment
694 if self
.comment
!= other
.comment
:
702 raise AssertionError(u
'Merge Error:\n %s\n %s '
703 % (unicode(self
), unicode(other
)))
706 # Prüfe auf Vorkommen von Regeländerungen der Orthographiereform 1996.
708 # >>> entry = WordEntry(u'Würste;Wür-ste')
709 # >>> entry.regelaenderungen()
710 # >>> print unicode(entry)
711 # Würste;-2-;Wür-ste;Würs-te
712 # >>> entry = WordEntry(u'Würste;Würs-te')
713 # >>> entry.regelaenderungen()
714 # >>> print unicode(entry)
715 # Würste;-2-;Wür-ste;Würs-te
716 # >>> entry = WordEntry(u'Hecke;He-cke')
717 # >>> entry.regelaenderungen()
718 # >>> print unicode(entry)
719 # Hecke;-2-;He{ck/k-k}e;He-cke
720 # >>> entry = WordEntry(u'Ligusterhecke;Ligu-ster=he{ck/k-k}e')
721 # >>> entry.regelaenderungen()
722 # >>> print unicode(entry)
723 # Ligusterhecke;-2-;Ligu-ster=he{ck/k-k}e;Ligus-ter=he-cke
724 # >>> entry = WordEntry(u'Hass;Hass')
725 # >>> entry.regelaenderungen()
726 # >>> print unicode(entry)
727 # Hass;-2-;-3-;Hass;Hass
728 # >>> entry = WordEntry(u'fasst;fasst')
729 # >>> entry.regelaenderungen()
730 # >>> print unicode(entry)
731 # fasst;-2-;-3-;fasst;fasst
732 # >>> entry = WordEntry(u'Missbrauch;Miss<brauch')
733 # >>> entry.regelaenderungen()
734 # >>> print unicode(entry)
735 # Missbrauch;-2-;-3-;Miss<brauch;Miss<brauch
736 # >>> entry = WordEntry(u'schlifffest;schliff=fest')
737 # >>> entry.regelaenderungen()
738 # >>> print unicode(entry)
739 # schlifffest;-2-;-3-;schliff=fest
743 def regelaenderungen(self
):
744 # Trennregeländerungen:
745 r1901
= (u
'-st', u
'{ck/k-k}')
746 r1996
= (u
's-t', u
'-ck')
748 w1901
= self
.get('de-1901')
749 w1996
= self
.get('de-1996')
752 if w1901
is None or w1996
is None:
755 for r1
, r2
in zip(r1901
, r1996
):
756 w1901
= w1901
.replace(r2
,r1
)
757 w1996
= w1996
.replace(r1
,r2
)
759 # kein Schluss-ss und sst in de-1901 (ungetrenntes "ss" nur in Ausnahmen)
760 # aber: 'ßt' und Schluß-ß auch in de-1996 möglich (langer Vokal)
765 # Dreikonsonantenregel:
766 if w1901
and re
.search(ur
'(.)\1=\1', w1901
):
770 if w1901
== w1996
: # keine Regeländerung im Wort
776 self
.extend( ['']*(4-len(self
)) )
781 self
.extend( ['']*(4-len(self
)) )
786 self
.append(w_versal
)
792 # Klasse für Einträge (Zeilen) der Wortlisten (neues, kurzes Format)
794 # >>> from wortliste import ShortEntry
796 # >>> aalbst = ShortEntry(u'Aal=be<stand # Test')
800 class ShortEntry(WordEntry
):
802 # Feldbelegung im Kurzformat (Vorschlag)
804 # Sprachtags nach [BCP47]_.
807 # Wort mit Trennungen nach aktueller Rechtschreibung (de-1996).
808 # Einziges Feld, falls andere Varianten über Regeln gewonnen werden können.
810 # "-1-" falls die Schreibung in de-1996 unzulässig ist (-1-;Pro<zeß).
813 # Wort mit Trennung nach de-1901.
814 # Belegt, falls abweichend von der regelbasierten Ableitung aus "de".
816 # "-2-" falls die Schreibung in de-1901 unzulässig ist (Ur<laubs=tipp;-2-).
818 # de-CH oder de-x-versal:
819 # Wort mit ß-Ersatzschreibung, die entweder in
820 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird.
821 # Trennungen nach aktueller Rechtschreibung (de-CH-1996, de-1996-x-versal).
823 # "-3-" falls die Schreibung in de-CH-1996 unzulässig ist.
826 # Wort mit ß-Ersatzschreibung für de-1901 mit Großbuchstaben oder
828 # Belegt, falls abweichend von der Ableitung aus "de-x-versal".
830 # "-4-" falls die abgeleitete Schreibung in de-1901 unzulässig ist.
833 # Wort mit ß-Ersatzschreibung, die der Schweiz benutzt wird.
834 # Insbesondere Wörter mit „sss“ gefolgt von einem Vokal, die wie
835 # andere Dreifachkonsonanten gehandhabt wurden (also anders, als
836 # bei Ersatzschreibung in Deutschland und Österreich), z.B. „süssauer“
837 # Belegt, falls abweichend von der regelbasierten Ableitung aus "de-CH".
839 # "-5-" falls die abgeleitete Schreibung in de-CH-1901 unzulässig ist.
841 # >>> print ShortEntry.feldnamen
842 # ('de', 'de-1901', 'de-CH', 'de-1901-x-versal', 'de-CH-1901')
843 # >>> print aalbst.feldindizes['de'], aalbst.feldindizes['de-CH-1901']
845 # >>> print aalbst.feldindizes['de-x-versal'], aalbst.feldindizes['de-CH']
851 'de': 0, # Deutsch, aktuell (Reform 1996)
852 'de-1996': 0, # Alias
853 'de-1901': 1, # "traditionell" (Reform 1901)
854 'de-CH': 2, # ohne ß (Schweiz oder versal) "aktuell"
855 'de-x-versal': 2, # Alias
856 'de-1996-x-versal': 2, # Alias
857 'de-1901-x-versal': 3, # ohne ß (Schweiz oder versal) "traditionell"
858 'de-CH-1901': 4, # ohne ß (Schweiz) "traditionell" ("süssauer")
861 feldnamen
= ('de', 'de-1901', 'de-CH', 'de-1901-x-versal', 'de-CH-1901')
865 # Wenn sich die getrennten Wörter über Ableitungsregeln gewinnen
866 # lassen, werden Felder weggelassen.
868 # Die folgenden Tupel listen die Ersatzfelder für das ensprechende
871 # Bsp: Ersatz-Felder für 'de-CH-1901':
873 # >>> for i in ShortEntry.ersatzfelder[ShortEntry.feldindizes['de-CH-1901']]:
874 # ... print ShortEntry.feldnamen[i]
880 # Ersatzindizes Index Name
881 ersatzfelder
= (tuple(), # 0 'de'
884 (1, 0), # 3 'de-1901-x-versal'
885 (1, 0), # 4 'de-CH-1901'
892 # Aal=be<stand # Test
893 # >>> print ShortEntry(aalbestand)
894 # Aal=be<stand # Test
895 # >>> dst = ShortEntry(u'Diens-te;Dien-ste')
898 # >>> fluss = ShortEntry(u'Fluss;Fluß')
899 # >>> print unicode(fluss)
901 # >>> tpp = ShortEntry(u'Ur<laubs=tipp;-2-')
904 # >>> drs = unicode(ShortEntry(u'-1-;Dreß'))
908 # # >>> print ShortEntry(u'# Testkommentar')
912 # Wird dem Konstruktor eine WordEntry-Instanz übergeben, so wird
913 # diese in das Kurzformat überführt:
915 # >>> print abenddienste
916 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
917 # >>> print ShortEntry(abenddienste)
919 # >>> print urlaubstipp
920 # Urlaubstipp;-2-;-3-;Ur<laubs=tipp
921 # >>> print ShortEntry(urlaubstipp)
923 # >>> dress = WordEntry(u'Dress;Dress')
924 # >>> print ShortEntry(dress)
926 # >>> dresz = WordEntry(u'Dreß;-2-;Dreß;-4-')
927 # >>> print unicode(ShortEntry(dresz))
928 # -1-;Dreß;-3-;-4-;-5-
929 # >>> boss = ShortEntry(WordEntry(u'Boss;Boss # en.'))
932 # >>> biss = ShortEntry(WordEntry(u'Biss;-2-;-3-;Biss'))
935 # >>> g_ebene = WordEntry(u'Gaußebene;Gauß=ebe-ne')
936 # >>> print unicode(ShortEntry(g_ebene))
937 # Gauß=ebe-ne;Gauß=ebe-ne;-3-;-4-;-5-
938 # >>> print unicode(ShortEntry(WordEntry(u'Abfalllager;-2-;-3-;Ab<fall=la-ger')))
941 # auffrass;-2-;-3-;-4-;auf-frass
942 # >>> print ShortEntry(auffrass)
943 # -1-;-2-;auf-frass;auf-frass;auf-frass
944 # >>> fraesse = WordEntry(u'frässe;-2-;-3-;-4-;-5-;frä-sse;fräs-se;fräs-se')
945 # >>> frs = ShortEntry(fraesse)
946 # >>> print unicode(frs)
947 # -1-;-2-;fräs-se;frä-sse;fräs-se
948 # >>> print ShortEntry(WordEntry(u'Fussballliga;-2-;-3-;-4-;-5-;-6-;Fuss=ball==li-ga'))
949 # -1-;-2-;Fuss=ball==li-ga
950 # >>> print ShortEntry(WordEntry(u'Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga;-7-'))
951 # -1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga
952 # >>> messignal = WordEntry(u'Messignal;-2-;-3-;-4-;-5-;-6-;-7-;Me{ss/ss=s}i-.gnal')
953 # >>> print unicode(ShortEntry(messignal))
954 # -1-;-2-;-3-;-4-;Me{ss/ss=s}i-.gnal
955 # >>> loesz = WordEntry(u'Lößboden;Löß=bo-den')
956 # >>> print unicode(ShortEntry(loesz))
957 # Löß=bo-den;Löß=bo-den;-3-;-4-;-5-
958 # >>> loess = WordEntry(u'Lössboden;-2-;-3-;Löss=bo-den;Löss=bo-den')
959 # >>> print unicode(ShortEntry(loess))
960 # Löss=bo-den;-2-;Löss=bo-den;Löss=bo-den;Löss=bo-den
961 # >>> print ShortEntry(WordEntry(u'Amnesty;Am-nes-ty # en.'))
962 # Am-nes-ty;Am-nes-ty # en.
963 # >>> print ShortEntry(WordEntry(u'# nur Kommentar'))
968 def __init__(self
, line
, delimiter
=';', prune
=True):
970 if type(line
) == WordEntry
:
971 self
.comment
= line
.comment
972 self
.append(line
.getitem(3)) # Deutsch, aktuell (Reform 1996)
973 self
.append(line
.getitem(2)) # "traditionell" (Reform 1901)
974 self
.append(line
.getitem(6)) # ohne ß (Schweiz oder versal) "aktuell"
975 self
.append(line
.getitem(5)) # ohne ß (Schweiz oder versal) "traditionell"
976 self
.append(line
.getitem(7)) # ohne ß (Schweiz) "traditionell" ("süssauer")
977 # Felder zusammenfassen
981 WordEntry
.__init
__(self
, line
, delimiter
)
987 # Bei Einträgen im Kurzformat ist der Schlüssel nicht eindeutig:
988 # durch regelbasierte Ableitung kann ein Wort in verschiedenen
989 # Schreibungen vorkommen.
990 # Standardmäig wird das erste nichtleere Feld ohne Trennzeichen, bei Angabe
991 # des optionalen Arguments ``lang`` die Schreibweise in dieser Rechtschreibung
994 # >>> print fluss.key()
996 # >>> print ShortEntry(u'-1-;Dreß').key()
999 # Die Key-Auswahl kann über das `lang` Argument gesteuert werden:
1001 # >>> print ShortEntry(u'-1-;Dreß').key('de-CH-1901')
1006 def key(self
, lang
=None):
1007 """Erstelle einen Schlüssel (ungetrenntes Wort)."""
1009 return join_word(self
.get(lang
))
1016 # Trennmuster für Sprachvariante ausgeben
1017 # ---------------------------------------
1019 # Suche die passende Sprachvariante (oder generiere sie).
1021 # Ohne Transformation
1023 # >>> aalbst.get('de')
1025 # >>> aalbst.get('de-1996')
1027 # >>> aalbst.get('de-1901')
1029 # >>> aalbst.get('de-x-versal')
1031 # >>> aalbst.get('de-1901-x-versal')
1033 # >>> aalbst.get('de-1996-x-versal')
1035 # >>> aalbst.get('de-CH-1901')
1038 # >>> print dst.get('de')
1040 # >>> print dst.get('de-1901')
1042 # >>> print dst.get('de-1996')
1047 # >>> rs = ShortEntry(u'-1-;-2-;Russ') # versal f. Ruß
1048 # >>> print rs.get('de-x-versal')
1050 # >>> rs.get('de-1901-x-versal')
1053 # >>> print tpp.get('de')
1055 # >>> print tpp.get('de-x-versal')
1057 # >>> tpp.get('de-1901')
1060 # alle Felder mit "get":
1061 # >>> print u';'.join(ShortEntry(u'-1-;-2-;fräs-se').completed())
1064 # Mit Transformation:
1066 # >>> print ShortEntry(u'Es-te').get('de-1901')
1068 # >>> print ShortEntry(u'passt').get('de-1901')
1070 # >>> print ShortEntry(u'passt').get('de-1901-x-versal')
1073 # >>> abus = ShortEntry(u'ab<bü-ßen')
1074 # >>> print abus.get('de-CH')
1076 # >>> print abus.get('de-1901-x-versal')
1078 # >>> print abus.get('de-CH-1901')
1081 # >>> print ShortEntry(u'Bus-se').get('de-1901-x-versal')
1083 # >>> print ShortEntry(u'Pass=sys-tem').get('de-CH-1901')
1084 # Pa{ss/ss=s}y-.stem
1086 # >>> ShortEntry(u'Pro<zess=en-.de;Pro<zeß=en-de;Pro<zess=en-.de').get('de-CH-1901')
1087 # u'Pro<zess=en-.de'
1088 # >>> pstr = ShortEntry(u'Pass=stra-ße;-2-')
1089 # >>> print u';'.join(pstr.completed())
1090 # Pass=stra-ße;;Pass=stras-se;;
1091 # >>> print pstr.get('de-1996') # Deutsch, aktuell (Reform 1996)
1093 # >>> print pstr.get('de-x-versal') # Alias de-CH
1098 def getitem(self
, i
):
1099 """Return item ``i`` or a subsititute"""
1102 except IndexError: # Feld i nicht vorhanden
1104 # Ersatzregeln anwenden
1105 for _i
in self
.ersatzfelder
[i
]:
1108 except IndexError: # Feld i nicht vorhanden
1109 continue # nächsten `tag` versuchen
1111 if not word
: # "ausgekreuzt"
1112 return u
'' # nicht ableiten
1114 # if '1901' in lang and '1901' not in tag:
1115 if i
in (1, 3, 4) and _i
== 0:
1116 word
= ableitung1901(word
)
1117 # if ('-CH' in lang) or ('x-versal' in lang) and (u'ß' in word):
1118 if i
> 1 and (u
'ß' in word
):
1119 word
= versalschreibung(word
, self
.feldnamen
[i
])
1123 # Felder für alle Sprachvarianten ausfüllen
1125 # >>> dst.complete()
1127 # Diens-te;Dien-ste;Diens-te;Dien-ste;Dien-ste
1128 # >>> aalbst.complete()
1130 # Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand;Aal=be<stand # Test
1131 # >>> abus.complete()
1132 # >>> print unicode(abus)
1133 # ab<bü-ßen;ab<bü-ßen;ab<büs-sen;ab<bü-ssen;ab<büs-sen
1134 # >>> boss.complete()
1135 # >>> print unicode(boss)
1136 # Boss;Boss;Boss;Boss;Boss # en.
1137 # >>> frs.complete()
1139 # [u'', u'', u'fr\xe4s-se', u'fr\xe4-sse', u'fr\xe4s-se']
1140 # >>> print unicode(frs)
1141 # -1-;-2-;fräs-se;frä-sse;fräs-se
1142 # >>> tpp.complete()
1144 # [u'Ur<laubs=tipp', u'', u'Ur<laubs=tipp', u'', u'']
1146 # Ur<laubs=tipp;-2-;Ur<laubs=tipp;-4-;-5-
1150 def completed(self
):
1151 return [self
.get(sv
) for sv
in self
.feldnamen
]
1154 if len(self
) == 5: # schon ausführlich
1156 # TODO: schon vorhandene Felder müssen nicht neuberechnet werden...
1157 for i
, field
in enumerate(self
.completed()):
1163 # Felder für Sprachvarianten zusammenfassen
1164 # -----------------------------------------
1166 # Weglassen von Feldern, wenn sich der Inhalt durch Ableitung gewinnen läßt::
1168 # >>> aalbst.prune()
1170 # Aal=be<stand # Test
1175 # >>> print unicode(frs)
1176 # -1-;-2-;fräs-se;frä-sse;fräs-se
1178 # >>> print unicode(abus)
1181 # >>> print unicode(boss)
1187 # >>> entry = ShortEntry(u'-1-;Dreß;-3-;-4-;-5-')
1189 # >>> print unicode(entry)
1190 # -1-;Dreß;-3-;-4-;-5-
1192 # >> foo = ShortEntry(u'ade;de01;deCH;de01v;deCH01')
1193 # >> foo = ShortEntry(u'ade;de01;deCH;de01v')
1200 if len(self
) == 1: # bereits kompakt
1202 for tag
in reversed(self
.feldnamen
[1:len(self
)]):
1204 rekonstruktion
= self
.get(tag
)
1205 if wort
!= rekonstruktion
:
1206 # print tag, repr(self), wort, rekonstruktion
1209 if len(self
) == 1 and not self
[0]:
1213 # Einträge zusammenfassen
1214 # -----------------------
1216 # >>> entry = ShortEntry(u'qua-li-täts=be<wusst;-2-')
1217 # >>> entry.merge(ShortEntry(u'-1-;qua-li-täts=be<wußt'))
1218 # >>> print unicode(entry)
1219 # qua-li-täts=be<wusst
1220 # >>> entry = ShortEntry(u'Qua-li-fi-zie-rungs==maß=nah-me')
1221 # >>> entry.merge(ShortEntry(u'-1-;-2-;Qua-li-fi-zie-rungs==mass=nah-me'))
1222 # >>> print unicode(entry)
1223 # Qua-li-fi-zie-rungs==maß=nah-me
1224 # >>> entry = ShortEntry(u'-1-;-2-;Qua-li-fi-zie-rungs==mass=nah-me')
1225 # >>> entry.merge(ShortEntry(u'Qua-li-fi-zie-rungs==maß=nah-me'))
1226 # >>> print unicode(entry)
1227 # Qua-li-fi-zie-rungs==maß=nah-me
1231 # def merge(self, other, allow_alternatives=False):
1234 # for i, (s_i, o_i) in enumerate(zip(self,other)):
1235 # if s_i == o_i or o_i.startswith(u'-'):
1237 # if s_i.startswith(u'-'):
1240 # if not allow_alternatives:
1243 # raise AssertionError('%s != %s' %(s_i, o_i))
1244 # self[i] = u'[%s/%s]' % (s_i, o_i)
1249 # Umwandeln in Listen von WordEntry Instanzen (Langformat)
1250 # -------------------_------------------------------------
1252 # >> entry = ShortEntry(u'fl')
1253 # >> entry.wordentries(prune=False)
1255 # >>> mse = ShortEntry(u'Mas-se')
1256 # >>> mse.wordentries()
1257 # [[u'Masse', u'Mas-se']]
1258 # >>> mas = ShortEntry(u'Ma-ße')
1259 # >>> for e in mas.wordentries(): e
1260 # [u'Ma\xdfe', u'Ma-\xdfe']
1261 # [u'Masse', u'', u'', u'', u'', u'Ma-sse', u'Mas-se', u'Mas-se']
1263 # >>> def print_langform(line, prune=True):
1264 # ... for e in ShortEntry(line).wordentries(prune):
1265 # ... print unicode(e)
1267 # >>> print_langform(u'ba-den')
1269 # >>> print_langform(u'Wes-te')
1270 # Weste;-2-;We-ste;Wes-te
1271 # >>> print_langform(u'Toll-patsch;-2-')
1272 # Tollpatsch;-2-;-3-;Toll-patsch
1273 # >>> print_langform(u'-1-;rau-he')
1274 # rauhe;-2-;rau-he;-4-
1276 # Bei Rechtschreibänderungen entsteht mehr als ein Eintrag:
1278 # >>> print_langform(u'Ab<fall=la-ger # Rechtschreibänderung')
1279 # Abfalllager;-2-;-3-;Ab<fall=la-ger # Rechtschreibänderung
1280 # Abfallager;-2-;Ab<fa{ll/ll=l}a-.ger;-4- # Rechtschreibänderung
1282 # >>> print_langform(u'Ab<guss')
1283 # Abguss;-2-;-3-;Ab<guss;Ab<guss
1284 # Abguß;-2-;Ab<guß;-4-
1286 # >>> print_langform(u'groß')
1288 # gross;-2-;-3-;-4-;gross
1290 # >>> print_langform(u'gro-ßen')
1292 # grossen;-2-;-3-;-4-;-5-;gro-ssen;gros-sen;gros-sen
1294 # >>> print_langform(u'Spaß')
1296 # Spass;-2-;-3-;-4-;Spass
1297 # >>> print_langform(u'Spass')
1298 # Spass;-2-;-3-;Spass;Spass
1300 # >>> print_langform(u'spa-ßen')
1302 # spassen;-2-;-3-;-4-;-5-;spa-ssen;spas-sen;spas-sen
1304 # >>> print_langform(u'ab<bü-ßen')
1306 # abbüssen;-2-;-3-;-4-;-5-;ab<bü-ssen;ab<büs-sen;ab<büs-sen
1308 # >>> print_langform(u'Boss;Boss # en.')
1310 # >>> print_langform(u'Biss;-2-')
1313 # >>> print_langform(u'Pass=sys-tem')
1314 # Passsystem;-2-;-3-;Pass=sys-tem;-5-;Pass=sy-stem;Pass=sys-tem;-8-
1315 # Paßsystem;-2-;Paß=sy-stem;-4-
1316 # Passystem;-2-;-3-;-4-;-5-;-6-;-7-;Pa{ss/ss=s}y-.stem
1318 # >>> print_langform(u'Press=saft')
1319 # Presssaft;-2-;-3-;Press=saft;-5-;Press=saft;Press=saft;-8-
1320 # Preßsaft;-2-;Preß=saft;-4-
1321 # Pressaft;-2-;-3-;-4-;-5-;-6-;-7-;Pre{ss/ss=s}aft
1323 # >>> print_langform(u'Pro<gramm==maß=nah-me')
1324 # Programmmaßnahme;-2-;-3-;Pro<gramm==maß=nah-me
1325 # Programmaßnahme;-2-;Pro<gra{mm/mm==m}aß=nah-me;-4-
1326 # Programmmassnahme;-2-;-3-;-4-;-5-;-6-;Pro<gramm==mass=nah-me
1327 # Programmassnahme;-2-;-3-;-4-;-5-;Pro<gra{mm/mm==m}ass=nah-me;-7-
1329 # >>> print_langform(u'Pass=stra-ße')
1330 # Passstraße;-2-;-3-;Pass=stra-ße
1331 # Paßstraße;-2-;Paß=stra-ße;-4-
1332 # Passstrasse;-2-;-3-;-4-;-5-;Pass=stra-sse;Pass=stras-se;Pass=stras-se
1334 # Explizit nur eine Schreibung:
1336 # >>> print_langform(u'Abend=dress;Abend=dress')
1337 # Abenddress;Abend=dress
1338 # >>> print_langform(u'-1-;Abend=dreß')
1339 # Abenddreß;-2-;Abend=dreß;-4-
1340 # Abenddress;-2-;-3-;-4-;-5-;Abend=dress;-7-
1342 # Nur Versalschreibung:
1343 # >>> print_langform(u'-1-;-2-;Fuss;Fuss;Fuss')
1344 # Fuss;-2-;-3-;-4-;Fuss
1345 # >>> line = u'Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga'
1346 # >>> print ShortEntry(WordEntry(line), prune=False)
1347 # -1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga
1348 # >>> print_langform(u'-1-;-2-;-3-;Fuss=ba{ll/ll=l}i-.ga;Fuss=ba{ll/ll=l}i-.ga')
1349 # Fussballiga;-2-;-3-;-4-;-5-;Fuss=ba{ll/ll=l}i-.ga;-7-
1350 # >>> print_langform(u'-1-;-2-;Fuss=ball==li-ga')
1351 # Fussballliga;-2-;-3-;-4-;-5-;-6-;Fuss=ball==li-ga
1354 # >>> print_langform(u'# holla')
1357 # >>> print_langform(u'Pro<zess=en-.de;Pro<zeß=en-de;Pro<zess=en-.de')
1358 # Prozessende;-2-;-3-;Pro<zess=en-.de;Pro<zess=en-.de
1359 # Prozeßende;-2-;Pro<zeß=en-de;-4-
1363 def wordentries(self
, prune
=True):
1365 if not self
and self
.comment
: # leerer Kommentar
1366 return [WordEntry(u
'# '+self
.comment
)]
1368 entries
= [] # liste von WordEntry Einträgen
1369 keys
= {} # list-index des Eintrags mit Schlüssel key
1370 # Zuordnung der Indizes: ShortEntry[s] == WordEntry[l]
1371 indices
= (3, 2, 6, 5, 7)
1373 for s
,l
in enumerate(indices
):
1375 word
= self
.getitem(s
)
1376 if not word
: # unklar wo einzutragen da kein passender Schlüssel
1377 continue # -> überspringen.
1378 key
= join_word(word
)
1379 # Felder mit gleichem Schlüssel in eine WordEntry Instanz:
1381 entry
= entries
[keys
[key
]]
1383 # neuen Eintrag mit Schlüssel und Kommentar erzeugen:
1384 entry
= WordEntry(key
)
1385 entry
.comment
= self
.comment
1386 keys
[key
] = len(entries
)
1387 entries
.append(entry
)
1389 # Eintrag in entry[j]:
1390 entry
.setitem(l
, word
)
1392 # Auffüllen und Komprimieren
1393 for entry
in entries
:
1394 while len(entry
) < 8:
1404 # Ableitung von de-1901 aus de-1996
1405 # ---------------------------------
1407 # Ableitung von de-1901 aus de-1996 (Reversion der Reform 1996).
1409 # Mit keep_key == True werden nur Änderungen vorgenommen, die die
1410 # Schreibweise des (ungetrennten) Wortes nicht verändern.
1412 # >>> from wortliste import ableitung1901
1416 def ableitung1901(wort
, keep_key
=False):
1418 # Trennregeländerungen
1419 # ~~~~~~~~~~~~~~~~~~~~
1421 # Diese Regeln ändern nicht das Wort, nur die Trennmöglichkeiten.
1423 # 1. Trenne nie st: Ersetze 's-t' mit u'-st'.
1425 # >>> ableitung1901(u'Diens-te')
1427 # >>> print ableitung1901(u'wuss-te', keep_key=True)
1430 # Keine Trennung nach nur einem Buchstaben:
1432 # >>> ableitung1901(u'Es-te')
1434 # >>> print ableitung1901(u'Nord=os-ten')
1436 # >>> print ableitung1901(u'Po-ly<es-ter')
1438 # >>> print ableitung1901(u'the-is-tisch')
1441 # Keine Trennung von s-theta:
1443 # >>> print ableitung1901(u'Äs-thet')
1446 # >>> print ableitung1901(u'pssst') # Sonderfall: falsche Umwandlung
1449 # 2. Trenne ck als k-k: Ersetze '-ck' mit '{ck/k-k}'.
1451 # >>> ableitung1901(u'Ha-cke')
1453 # >>> ableitung1901(u'Acker')
1455 # >>> ableitung1901(u'Got-tes=acker')
1456 # u'Got-tes=a{ck/k-k}er'
1457 # >>> ableitung1901(u'Aal=beck')
1459 # >>> ableitung1901(u'be<spick-te')
1464 wort
= re
.sub(u
'(?<!s)s-t(?!h)', u
'-st', wort
)
1465 wort
= wort
.replace(u
'-ck', u
'{ck/k-k}')
1466 wort
= re
.sub(u
'(?<=[AEIOUYÄÖÜaeiouyäöü])ck(?=[aeiouyäöü])',
1468 # keine Trennung nach nur 1 Buchstaben:
1469 wort
= re
.sub(u
'((?<=[=<-].)|(?<=^.))-', ur
'', wort
)
1474 # Rechtschreibänderungen
1475 # ~~~~~~~~~~~~~~~~~~~~~~
1477 # Diese Regeln ändern die Schreibung des ungetrennten Worts (und somit den
1478 # Schlüssel im Langformat der Wortliste).
1480 # 3. kein ss und sst am Wortende (ungetrenntes "ss" nur in Ausnahmen):
1482 # Ersetze 'ss' (evt. gefolgt von t), wenn es am Silbenende auftaucht
1483 # (vor [-=<>]) mit ß (und evt. t).
1485 # Aber: 'ßt' und Schluss-ß auch in de-1996 möglich (langer Vokal)
1487 # >>> print ableitung1901(u'passt')
1489 # >>> print ableitung1901(u'Hass')
1491 # >>> print ableitung1901(u'Was-ser')
1493 # >>> print ableitung1901(u'Fass=brau-se')
1495 # >>> print ableitung1901(u'wuss-te')
1500 wort
= re
.sub(u
'ss(t?)(?=[-=<>]|$)', ur
'ß\1', wort
)
1502 # 4. Dreikonsonantenregel:
1504 # Ersetze 'mm=m' mit '{mm/mm=m}' (für alle Konsonanten vor Selbstlaut)
1506 # >>> print ableitung1901(u'Kipp=pflug')
1508 # >>> print ableitung1901(u'Kipp=punkt')
1510 # >>> print ableitung1901(u'Ab<fall=la-ger')
1511 # Ab<fa{ll/ll=l}a-.ger
1512 # >>> print ableitung1901(u'hell>licht')
1514 # >>> print ableitung1901(u'Pro<gramm==maß=nah-me')
1515 # Pro<gra{mm/mm==m}aß=nah-me
1519 wort
= re
.sub(ur
'([bfglmnprt])\1([=>]+)\1(?=[aeiouyäöü])',
1520 ur
'{\1\1/\1\1\2\1}', wort
)
1522 # unterdrücken der Trennung nach nur einem Buchstaben
1523 wort
= re
.sub(ur
'(?<=[=>].}[aeiouyäöü])([-<])\.?', ur
'\1.', wort
)
1528 # Ableitung von de-CH/de-x-versal (SZ-Ersatzschreibung):
1529 # ------------------------------------------------------------------
1531 # >>> from wortliste import versalschreibung
1532 # >>> print versalschreibung(u'paßt')
1534 # >>> print versalschreibung(u'bü-ßen', 'de-CH')
1536 # >>> print versalschreibung(u'bü-ßen', 'de-1996-x-versal')
1538 # >>> print versalschreibung(u'bü-ßen', 'de-CH-1901')
1540 # >>> print versalschreibung(u'äßen', 'de-CH-1901')
1542 # >>> print versalschreibung(u'auf<äßen', 'de-CH-1901')
1544 # >>> print versalschreibung(u'auf<eßt', 'de-CH-1901')
1546 # >>> print versalschreibung(u'Groß=se-gel', 'de-1901-x-versal')
1548 # >>> print versalschreibung(u'Groß=se-gel', 'de-CH-1901')
1549 # Gro{ss/ss=s}e-.gel
1550 # >>> print versalschreibung(u'Pass=sy-ste-me', 'de-CH-1901')
1551 # Pa{ss/ss=s}y-.ste-me
1552 # >>> print versalschreibung(u'Pro<zeß=en-de', 'de-CH-1901')
1554 # >>> print versalschreibung(u'Pro<zess=en-.de', 'de-CH-1901')
1556 # >>> print versalschreibung(u'Fluß==sy-stem', 'de-CH-1901')
1557 # Flu{ss/ss==s}y-.stem
1558 # >>> print versalschreibung(u'Meß==sen-der', 'de-CH-1901')
1559 # Me{ss/ss==s}en-.der
1563 def versalschreibung(wort
, lang
='de'):
1565 # Ersetze 'ß' mit 'ss' ::
1567 wort
= wort
.replace(u
'ß', u
'ss')
1569 # Trennung von Ersatz-ss in de-CH und de-1996 nach Sprechsilbenregel::
1571 if '1901-x-versal' not in lang
:
1572 wort
= re
.sub(u
'(?<=[aeiouyäöü])-\.?ss', u
's-s', wort
)
1573 wort
= re
.sub(u
'(?<=^[aeiouyäöü])ss(?=[aeiouyäöü])', u
's-s', wort
)
1574 wort
= re
.sub(u
'(?<=[=<][aeiouyäöü])ss(?=[aeiouyäöü])', u
's-s', wort
)
1576 # Unterdrückung irreführender Trennung::
1578 wort
= re
.sub(u
'ss(=+)(en|er)([<-])\.?', ur
'ss\1\2\3.', wort
)
1580 # Dreikonsonantenregel für Ersatz-ss in de-CH-1901::
1582 if 'CH-1901' in lang
:
1583 wort
= re
.sub(u
'ss(=+)s(?=[aeiouyäöü])', ur
'{ss/ss\1s}', wort
)
1584 # Unterdrücken der Trennung nach nur einem Buchstaben und irreführender Trennungen
1585 wort
= re
.sub(ur
'(?<=[=>]s}[aeiouyäöü])([-<])\.?', ur
'\1.', wort
)
1586 # wort = re.sub(ur'(?<===s}[aeiouyäöü])([-<])\.?', ur'\1.', wort) # Reißverschus=sy-.stem
1587 wort
= re
.sub(u
'(?<=[=>]s})(en|er)([<-])\.?', ur
'\1\2.', wort
)
1595 # Trennzeichen entfernen::
1597 def join_word(wort
, assert_complete
=False):
1599 # Einfache Trennzeichen:
1601 # == ================================================================
1602 # \· ungewichtete Trennstelle (solche, wo sich noch niemand um die
1603 # Gewichtung gekümmert hat)
1604 # \. unerwünschte Trennstelle (sinnentstellend), z.B. Ur·in.stinkt
1605 # oder ungünstige Trennstelle (verwirrend), z.B. Atom·en.er·gie
1606 # in ungewichteten Wörtern
1607 # \= Trennstelle an Wortfugen (Wort=fu-ge)
1608 # \< Trennstelle nach Präfix (Vor<sil-be)
1609 # \> Trennstelle vor Suffix (Freund>schaf-ten)
1610 # \- Nebentrennstelle (ge-hen)
1611 # == ================================================================
1616 for char
in u
'·.=|-_<>':
1617 table
[ord(char
)] = None
1618 key
= wort
.translate(table
)
1620 # Spezielle Trennungen für die traditionelle Rechtschreibung
1621 # (siehe ../../dokumente/README.wortliste)::
1623 if '{' in key
or '}' in key
:
1624 key
= key
.replace(u
'{ck/kk}', u
'ck')
1625 key
= key
.replace(u
'{ck/k', u
'k')
1626 key
= key
.replace(u
'k}', u
'k')
1627 # Konsonanthäufungen an Wortfuge: '{xx/xxx}' -> 'xx':
1628 key
= re
.sub(ur
'\{(.)\1/\1\1\1\}', ur
'\1\1', key
)
1629 # schon getrennt: ('{xx/xx' -> 'xx' und 'x}' -> 'x'):
1630 key
= re
.sub(ur
'\{(.)\1/\1\1$', ur
'\1\1', key
)
1631 key
= re
.sub(ur
'^(.)\}', ur
'\1', key
)
1633 # Trennstellen in doppeldeutigen Wörtern::
1635 if '[' in key
or ']' in key
:
1636 key
= re
.sub(ur
'\[(.*)/\1\]', ur
'\1', key
)
1638 key
= re
.sub(ur
'\[([^/\[]+)$', ur
'\1', key
)
1639 key
= re
.sub(ur
'^([^/\]]+)\]', ur
'\1', key
)
1641 # Test auf verbliebene komplexe Trennstellen::
1644 for spez
in u
'[{/}]':
1646 raise AssertionError('Spezialtrennung %s, %s' %
1647 (wort
.encode('utf8'), key
.encode('utf8')))
1654 # Zerlege ein Wort mit Trennzeichen in eine Liste von Silben und eine Liste
1657 # >>> from wortliste import zerlege
1659 # >>> zerlege(u'Haupt=stel-le')
1660 # ([u'Haupt', u'stel', u'le'], [u'=', u'-'])
1661 # >>> zerlege(u'Ge<samt=be<triebs=rats==chef')
1662 # ([u'Ge', u'samt', u'be', u'triebs', u'rats', u'chef'], [u'<', u'=', u'<', u'=', u'=='])
1663 # >>> zerlege(u'an<stands>los')
1664 # ([u'an', u'stands', u'los'], [u'<', u'>'])
1665 # >>> zerlege(u'An<al.pha-bet')
1666 # ([u'An', u'al', u'pha', u'bet'], [u'<', u'.', u'-'])
1671 silben
= re
.split(u
'[-·._<>=]+', wort
)
1672 trennzeichen
= re
.split(u
'[^-·._|<>=]+', wort
)
1673 return silben
, [tz
for tz
in trennzeichen
if tz
]
1678 # Fehler beim Übertragen von Trennstellen mit uebertrage_::
1680 class TransferError(ValueError):
1681 def __init__(self
, wort1
, wort2
):
1682 msg
= u
'Inkompatibel: %s %s' % (wort1
, wort2
)
1683 ValueError.__init
__(self
, msg
.encode('utf8'))
1685 def __unicode__(self
):
1686 return str(self
).decode('utf8')
1692 # Übertrage die Trennzeichen von `wort1` auf `wort2`:
1694 # >>> from wortliste import uebertrage, TransferError
1696 # >>> uebertrage(u'Haupt=stel-le', u'Haupt·stel·le')
1699 # Auch teilweise Übertragung, von "kategorisiert" nach "unkategorisiert":
1701 # >>> print uebertrage(u'Haupt=stel-le', u'Haupt=stel·le')
1704 # >>> print uebertrage(u'Haupt·stel-le', u'Haupt=stel·le')
1707 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler')
1710 # >>> print uebertrage(u'Freund>schaf·ten', u'Freund-schaf-ten')
1713 # Übertragung doppelter Marker:
1715 # >>> print uebertrage(u'ver<<aus<ga-be', u'ver<aus<ga-be')
1718 # >>> print uebertrage(u'freund>lich>>keit', u'freund>lich>keit')
1721 # >>> print uebertrage(u'Amts==haupt=stel-le', u'Amts=haupt=stel-le')
1722 # Amts==haupt=stel-le
1724 # Kein Überschreiben doppelter Marker:
1725 # >>> print uebertrage(u'ver<aus<ga-be', u'ver<<aus<ga-be')
1728 # >>> print uebertrage(u'Amts=haupt=stel-le', u'Amts==haupt=stel·le')
1729 # Amts==haupt=stel-le
1731 # Erhalt des Markers für ungünstige Stellen:
1732 # >>> print uebertrage(u'An·al.pha·bet', u'An<al.pha-bet')
1735 # Keine Übertragung, wenn die Zahl oder Position der Trennstellen
1736 # unterschiedlich ist oder bei unterschiedlichen Wörtern:
1739 # ... uebertrage(u'Ha-upt=stel-le', u'Haupt=stel·le')
1740 # ... uebertrage(u'Haupt=ste-lle', u'Haupt=stel·le')
1741 # ... uebertrage(u'Waupt=stel-le', u'Haupt=stel·le')
1742 # ... except TransferError:
1745 # Übertragung auch bei unterschiedlicher Schreibung oder Position der
1746 # Trennstellen mit `strict=False` (für Abgleich zwischen Sprachvarianten):
1748 # >>> uebertrage(u'er-ster', u'ers·ter', strict=False)
1750 # >>> uebertrage(u'Fluß=bett', u'Fluss·bett', strict=False)
1752 # >>> uebertrage(u'ab>bei-ßen', u'ab>beis·sen', strict=False)
1754 # >>> print uebertrage(u'Aus<tausch=dien-stes', u'Aus-tausch=diens-tes', False)
1755 # Aus<tausch=diens-tes
1757 # Auch mit `strict=False` muß die Zahl der Trennstellen übereinstimmen
1758 # (Ausnahmen siehe unten):
1761 # ... uebertrage(u'Ha-upt=ste-lle', u'Haupt=stel·le', strict=False)
1762 # ... except TransferError:
1765 # Akzeptiere unterschiedliche Anzahl von Trennungen bei st und ck nach
1768 # >>> uebertrage(u'acht=ecki-ge', u'acht·e{ck/k·k}i·ge', strict=False)
1769 # u'acht=e{ck/k-k}i-ge'
1770 # >>> uebertrage(u'As-to-ria', u'Asto·ria', strict=False)
1772 # >>> uebertrage(u'Asto-ria', u'As·to·ria', strict=False)
1774 # >>> uebertrage(u'So-fa=ecke', u'So·fa=e{ck/k-k}e', strict=False)
1775 # u'So-fa=e{ck/k-k}e'
1776 # >>> uebertrage(u'Drei=ecks=ecke', u'Drei=ecks==e{ck/k-k}e', strict=False)
1777 # u'Drei=ecks==e{ck/k-k}e'
1779 # Mit ``upgrade=False`` werden nur unspezifische Trennstellen überschrieben:
1781 # >>> print uebertrage(u'an=stel-le', u'an<stel·le', upgrade=False)
1784 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler', upgrade=False)
1787 # >>> print uebertrage(u'Aus-stel-ler', u'Aus<stel-ler', upgrade=False)
1790 # >>> print uebertrage(u'vor<an<<stel-le', u'vor-an<stel·le', upgrade=False)
1795 selbstlaute
= u
'aeiouäöüAEIOUÄÖÜ'
1797 def uebertrage(wort1
, wort2
, strict
=True, upgrade
=True):
1799 silben1
, trennzeichen1
= zerlege(wort1
)
1800 silben2
, trennzeichen2
= zerlege(wort2
)
1801 # Prüfe strikte Übereinstimmung:
1802 if silben1
!= silben2
and strict
:
1803 if u
'<' in trennzeichen1
or u
'·' in trennzeichen2
:
1804 raise TransferError(wort1
, wort2
)
1807 # Prüfe ungefähre Übereinstimmung:
1808 if len(trennzeichen1
) != len(trennzeichen2
):
1809 # Selbstlaut + st oder ck?
1810 for s
in selbstlaute
:
1811 if (wort2
.find(s
+u
'{ck/k·k}') != -1 or
1812 wort2
.find(s
+u
'{ck/k-k}') != -1):
1813 wort1
= re
.sub(u
'%sck([%s])'%(s
,selbstlaute
),
1814 ur
'%s-ck\1'%s, wort1
)
1815 silben1
, trennzeichen1
= zerlege(wort1
)
1816 if wort2
.find(s
+u
's·t') != -1:
1817 wort1
= wort1
.replace(s
+u
'st', s
+u
's-t')
1818 silben1
, trennzeichen1
= zerlege(wort1
)
1819 elif wort1
.find(s
+u
's-t') != -1:
1820 wort1
= wort1
.replace(s
+u
's-t', s
+u
'st')
1821 silben1
, trennzeichen1
= zerlege(wort1
)
1822 # print u'retry:', silben1, trennzeichen1
1823 # immer noch ungleiche Zahl an Trennstellen?
1824 if len(trennzeichen1
) != len(trennzeichen2
):
1825 raise TransferError(wort1
, wort2
)
1827 # Baue wort3 aus silben2 und spezifischeren Trennzeichen:
1828 wort3
= silben2
.pop(0)
1829 for t1
,t2
in zip(trennzeichen1
, trennzeichen2
):
1830 if ((t2
== u
'·' and t1
!= u
'.') # unspezifisch
1832 ((t2
in (u
'-', u
'<') and t1
in (u
'<', u
'<<', u
'<=')) # Praefixe
1833 or (t2
in (u
'-', u
'>') and t1
in (u
'>', u
'>>', u
'=>')) # Suffixe
1834 or (t2
in (u
'-', u
'=') and t1
in (u
'=', u
'==', u
'===')) # W-fugen
1838 elif t2
== u
'.' and t1
not in u
'·.':
1842 wort3
+= silben2
.pop(0)
1846 # Übertrag kategorisierter Trennstellen zwischen den Feldern aller Einträge
1849 def sprachabgleich(entry
, vorbildentry
=None):
1852 return # allgemeine Schreibung
1854 mit_affix
= None # < oder >
1855 kategorisiert
= None # kein ·
1856 unkategorisiert
= None # mindestens ein ·
1857 gewichtet
= None # == oder <= oder =>
1858 for field
in entry
[1:]:
1859 if not field
: # -2-, -3-, ...
1861 if u
'{' in field
and u
'[' in field
: # Bi-ber==be[t=t/{tt/tt=t}]uch
1862 continue # zu komplex
1864 unkategorisiert
= field
1865 elif u
'<' in field
or u
'>' in field
:
1868 kategorisiert
= field
1869 if u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
1872 for field
in vorbildentry
[1:]:
1873 if not field
: # -2-, -3-, ...
1875 if u
'{' in field
and u
'[' in field
: # Bi-ber==be[t=t/{tt/tt=t}]uch
1876 continue # zu komplex
1877 if not mit_affix
and u
'<' in field
or u
'>' in field
:
1879 elif not kategorisiert
and unkategorisiert
and u
'·' not in field
:
1880 kategorisiert
= field
1881 if not gewichtet
and u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
1883 # print 've:', mit_affix, kategorisiert, unkategorisiert
1884 if mit_affix
and (kategorisiert
or unkategorisiert
or gewichtet
):
1885 for i
in range(1,len(entry
)):
1886 if not entry
[i
]: # -2-, -3-, ...
1888 if u
'<' not in entry
[i
] or u
'·' in entry
[i
]:
1890 entry
[i
] = uebertrage(mit_affix
, entry
[i
], strict
=False)
1891 except TransferError
, e
:
1892 if not '/' in entry
[i
]:
1893 print u
'Sprachabgleich:', unicode(e
)
1894 # print mit_affix+u':', unicode(entry)
1895 elif kategorisiert
and unkategorisiert
:
1896 for i
in range(1,len(entry
)):
1897 if u
'·' in entry
[i
]:
1899 entry
[i
] = uebertrage(kategorisiert
, entry
[i
], strict
=False)
1900 except TransferError
, e
:
1901 print u
'Sprachabgleich:', unicode(e
)
1902 # print kategorisiert, unicode(entry)
1904 for i
in range(1,len(entry
)):
1905 if u
'=' in entry
[i
] and not (
1906 u
'{' in entry
[i
] and u
'[' in entry
[i
]):
1908 entry
[i
] = uebertrage(gewichtet
, entry
[i
], strict
=False)
1909 except TransferError
, e
:
1910 print u
'Sprachabgleich:', unicode(e
)
1914 # Großschreibung in Kleinschreibung wandeln und umgekehrt
1916 # Diese Version funktioniert auch für Wörter mit Trennzeichen (während
1917 # str.title() nach jedem Trennzeichen wieder groß anfängt)
1919 # >>> from wortliste import toggle_case
1920 # >>> toggle_case(u'Ha-se')
1922 # >>> toggle_case(u'arm')
1924 # >>> toggle_case(u'frei=bier')
1926 # >>> toggle_case(u'L}a-ger')
1929 # Keine Änderung bei Wörtern mit Großbuchstaben im Inneren:
1931 # >>> toggle_case(u'USA')
1933 # >>> toggle_case(u'iRFD')
1936 # >>> toggle_case(u'gri[f-f/{ff/ff')
1938 # >>> toggle_case(u'Gri[f-f/{ff/ff')
1943 def toggle_case(wort
):
1945 key
= join_word(wort
, assert_complete
=True)
1946 except AssertionError:
1951 return wort
[0].upper() + wort
[1:]
1958 # Duden-Sortierung für die Wortliste
1960 # >>> from wortliste import sortkey_duden
1961 # >>> sortkey_duden([u"Abflußröhren"])
1962 # u'abflussrohren a*bflu*szroehren'
1963 # >>> sortkey_duden([u"Abflußrohren"])
1964 # u'abflussrohren a*bflu*szro*hren'
1965 # >>> sortkey_duden([u"Abflussrohren"])
1968 # >>> s = sorted([[u"Abflußröhren"], [u"Abflußrohren"], [u"Abflussrohren"]],
1969 # ... key=sortkey_duden)
1970 # >>> print ', '.join(e[0] for e in s)
1971 # Abflussrohren, Abflußrohren, Abflußröhren
1975 # Ligaturen auflösen und andere "normalisierunde" Ersetzungen für den
1976 # (Haupt-)Sortierschlüssel (Akzente werden über ``unicodedata.normalize``
1985 # "Zweitschlüssel" zur Unterscheidung von Umlauten/SZ und Basisbuchstaben::
1987 umschrift_subkey
= {
2003 # Schlüssel für die alphabetische Sortierung gemäß Duden-Regeln.
2005 # Argument ist ein Eintrag im WordEntry oder ShortEntry Format oder
2006 # ein (Unicode) String:
2008 # >>> print sortkey_duden(weste) # WordEntry
2010 # >>> print sortkey_duden(dst) # ShortEntry
2013 # >>> print sortkey_duden(u'Stra-ße')
2015 # >>> print sortkey_duden([u'Büh-ne'])
2017 # >>> print sortkey_duden(u'Weiß=flog;Weiß=flog;-3-;-4-;-5-')
2018 # weissflog weiszflo*g
2019 # >>> print sortkey_duden(u'Weiß=flog;Weiß=flog;-3-;-4-;-5-')
2020 # weissflog weiszflo*g
2021 # >>> print sortkey_duden(u'-1-;Meß=sen-der;-3-;-4-;-5-')
2022 # messsender meszsender
2026 def sortkey_duden(entry
):
2028 # ggf. ungetrenntes Wort extrahieren oder generieren::
2030 if isinstance(entry
, list):
2033 except AttributeError:
2035 if len(entry
) == 1: # ein Muster pro Zeile, siehe z.B. pre-1901
2036 key
= join_word(key
)
2038 match
= re
.search(u
'^[-0-9;]*([^;]+)', entry
) # erstes volles Feld
2040 key
= match
.group(1)
2043 key
= join_word(key
)
2044 key
= re
.sub(u
'[0-9;]', ur
'', key
)
2046 # Großschreibung ignorieren:
2048 # Der Duden sortiert Wörter, die sich nur in der Großschreibung unterscheiden
2049 # "klein vor groß" (ASCII sortiert "groß vor klein"). In der
2050 # `Trennmuster-Wortliste` kommen Wörter nur mit der häufiger anzutreffenden
2051 # Großschreibung vor, denn der TeX-Trennalgorithmus ignoriert Großschreibung.
2060 skey
= key
.replace(u
'ß', u
'ss')
2062 # Restliche Akzente weglassen: Wandeln in Darstellung von Buchstaben mit
2063 # Akzent als "Grundzeichen + kombinierender Akzent". Anschließend alle
2064 # nicht-ASCII-Zeichen ignorieren::
2066 skey
= skey
.translate(umschrift_skey
)
2067 skey
= unicodedata
.normalize('NFKD', skey
)
2068 skey
= unicode(skey
.encode('ascii', 'ignore'))
2070 # "Zweitschlüssel" für das eindeutige Einsortieren von Wörtern mit
2071 # gleichem Schlüssel (Masse/Maße, waren/wären, ...):
2073 # * "*" nach aou für die Unterscheidung Grund-/Umlaut
2079 subkey
= key
.translate(umschrift_subkey
)
2080 skey
= u
'%s %s' % (skey
,subkey
)
2082 # Gib den Sortierschlüssel zurück::
2091 # Vergleiche zwei Sequenzen von `WordEntries` (genauer: alle Objekte, die
2092 # sich sinnvoll zu Unicode wandlen lassen).
2096 # >>> from wortliste import udiff
2097 # >>> print udiff([abbeissen, aalbestand], [abbeissen,dresz], 'alt', 'neu')
2101 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
2102 # -Aalbestand;Aal=be<stand # Test
2103 # +Dreß;-2-;Dreß;-4-
2107 def udiff(a
, b
, fromfile
='', tofile
='',
2108 fromfiledate
='', tofiledate
='', n
=1, encoding
='utf8'):
2110 a
= [unicode(entry
).rstrip().encode(encoding
) for entry
in a
]
2111 b
= [unicode(entry
).rstrip().encode(encoding
) for entry
in b
]
2113 diff
= difflib
.unified_diff(a
, b
, fromfile
, tofile
,
2114 fromfiledate
, tofiledate
, n
, lineterm
='')
2117 return '\n'.join(diff
).decode(encoding
)
2125 # Normalisierung und Expansion von Sprachtags nach [BCP47]_
2127 # >>> from wortliste import normalize_language_tag
2128 # >>> normalize_language_tag('de_AT-1901')
2129 # ['de-AT-1901', 'de-AT', 'de-1901', 'de']
2131 # >>> normalize_language_tag('de') # Deutsch, allgemeingültig
2133 # >>> normalize_language_tag('de_1901') # traditionell (Reform 1901)
2135 # >>> normalize_language_tag('de_1996') # reformiert (Reform 1996)
2137 # >>> normalize_language_tag('de_CH') # ohne ß (Schweiz oder versal)
2139 # >>> normalize_language_tag('de-x-versal') # versal
2140 # ['de-x-versal', 'de']
2141 # >>> normalize_language_tag('de-1901-x-versal') # versal
2142 # ['de-1901-x-versal', 'de-1901', 'de-x-versal', 'de']
2143 # >>> normalize_language_tag('de_CH-1996') # Schweiz traditionell (süssauer)
2144 # ['de-CH-1996', 'de-CH', 'de-1996', 'de']
2146 # 'de': 1, # Deutsch, allgemeingültig
2147 # 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
2148 # 'de-1996': 3, # reformierte Reformschreibung (1996)
2149 # 'de-x-versal': 4, # ohne ß (Schweiz oder versal) allgemein
2150 # # 'de-CH': 4, # Alias
2151 # 'de-1901-x-versal': 5, # ohne ß (Schweiz oder versal) "traditionell"
2152 # 'de-1996-x-versal': 6, # ohne ß (Schweiz oder versal) "reformiert"
2153 # # 'de-CH-1996': 6, # Alias
2154 # 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
2159 def normalize_language_tag(tag
):
2160 """Return a list of normalized combinations for a `BCP 47` language tag.
2163 tag
= tag
.replace('_','-')
2164 # split (except singletons, which mark the following tag as non-standard):
2165 tag
= re
.sub(r
'-([a-zA-Z0-9])-', r
'-\1_', tag
)
2167 subtags
= [subtag
.replace('_', '-') for subtag
in tag
.split('-')]
2168 base_tag
= [subtags
.pop(0)]
2169 # find all combinations of subtags
2170 for n
in range(len(subtags
), 0, -1):
2171 # for tags in unique_combinations(subtags, n):
2172 for tags
in itertools
.combinations(subtags
, n
):
2173 taglist
.append('-'.join(base_tag
+list(tags
)))
2182 # Teste Übereinstimmung des ungetrennten Wortes in Feld 1 mit den
2183 # Trennmustern nach Entfernen der Trennmarker. Schreibe Inkonsistenzen auf die
2186 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
2188 def test_keys(wortliste
):
2189 print u
"Teste Schlüssel-Trennmuster-Übereinstimmung:"
2191 for entry
in wortliste
:
2192 # Test der Übereinstimmung ungetrenntes/getrenntes Wort
2195 for wort
in entry
[1:]:
2196 if not wort
: # leere Felder
2198 if key
!= join_word(wort
):
2200 print u
"\nkey '%s' != join_word('%s')" % (key
, wort
),
2201 if key
.lower() == join_word(wort
).lower():
2202 print(u
" Abgleich der Großschreibung mit"
2203 u
"`prepare-patch.py grossabgleich`."),
2208 # Finde Doppeleinträge (teste, ob jeder Schlüssel nur einmal vorkommt).
2209 # Schreibe Inkonsistenzen auf die Standardausgabe.
2211 # Das Argument ist ein Iterator über die Einträge (Klasse `WordEntry`). ::
2213 def test_uniqueness(wortliste
):
2217 for entry
in wortliste
:
2221 print unicode(entry
)
2224 print u
"Doppeleinträge (ohne Berücksichtigung der Großschreibung)."
2226 print u
" Entfernen mit `prepare-patch.py doppelte`."
2227 print u
" Patch vor Anwendung durchsehen!"
2229 # Teste die Wandlung einer Zeile im "wortliste"-Format in eine
2230 # ``WordEntry``-Instanz und zurück::
2232 def test_str_entry_str_conversion(wordfile
):
2234 for line
in file(wordfile
.name
):
2235 line
= line
.rstrip().decode(wordfile
.encoding
)
2236 entry
= WordEntry(line
)
2237 if line
== unicode(entry
):
2241 print u
'+', unicode(entry
)
2243 print OK
, u
"Einträge rekonstruiert"
2246 # Teste Vervollständigung und Zusammenfassung von Einträgen::
2248 def test_completion_pruning(entries
):
2250 for entry
in entries
:
2251 new
= copy
.copy(entry
)
2255 patch
= udiff(entries
, reko
, 'wortliste', 'neu')
2259 print u
"alle Einträge rekonstruiert"
2263 # Aufruf von der Kommandozeile
2264 # ============================
2268 if __name__
== '__main__':
2271 # sys.stdout mit UTF8 encoding (wie in Python 3)
2272 sys
.stdout
= codecs
.getwriter('UTF-8')(sys
.stdout
)
2273 # sys.stderr = codecs.getwriter('UTF-8')(sys.stderr)
2275 print u
"Test der Werkzeuge und inneren Konsistenz der Wortliste\n"
2277 # Ein WordFile Dateiobjekt::
2279 wordfile
= WordFile('../../../wortliste')
2280 print wordfile
.name
, u
"Format", wordfile
.format
2281 # wordfile = WordFile('../../../wlst', format='auto') # wortliste im Kurzformat
2282 # print wordfile.name, u"Format", wordfile.format
2283 # wordfile = WordFile('neu.todo')
2284 # print wordfile.name, u"Format", wordfile.format
2285 # wordfile = WordFile('neu-kurz.todo', format='f5')
2286 # print wordfile.name, u"Format", wordfile.format
2287 # wordfile = WordFile('korrektur.todo', format='auto')
2288 # print wordfile.name, u"Format", wordfile.format
2290 # Liste der Datenfelder (die Klasseninstanz als Argument für `list` liefert
2291 # den Iterator über die Felder, `list` macht daraus eine Liste)::
2293 wordlist
= list(wordfile
)
2294 print len(wordlist
), u
"Einträge"
2300 # sprache = 'de-1901' # traditionell
2301 sprache
= 'de-1996' # Reformschreibung
2302 # sprache = 'de-x-versal' # ohne ß (Schweiz oder versal) allgemein
2303 # sprache = 'de-1901-x-versal' # ohne ß (Schweiz oder versal) "traditionell"
2304 # sprache = 'de-1996-x-versal' # ohne ß (Schweiz oder versal) "reformiert"
2305 # sprache = 'de-CH-1901' # ohne ß (Schweiz) "traditionell" ("süssauer")
2307 # worte = [entry.get(sprache) for entry in wordlist]
2308 # worte = [wort for wort in worte if wort]
2309 # print len(worte), u"Einträge für Sprachvariante", sprache
2312 # Teste Schlüssel-Trennmuster-Übereinstimmung::
2314 # test_keys(wordlist)
2316 # Test auf Doppeleinträge::
2318 # test_uniqueness(wordlist)
2320 # Teste Komplettieren/Zusammenfassen der Einträge::
2322 # test_completion_pruning(wordlist)
2324 # Ein Wörterbuch (dict Instanz)::
2326 # wordfile.seek(0) # Pointer zurücksetzen
2327 # words = wordfile.asdict()
2328 # print len(words), u"Wörterbucheinträge"
2330 # Zeilenrekonstruktion::
2332 #test_str_entry_str_conversion(wordfile)
2338 # .. [BCP47] A. Phillips und M. Davis, (Editoren.),
2339 # `Tags for Identifying Languages`, http://www.rfc-editor.org/rfc/bcp/bcp47.txt
2341 # .. _Wortliste der deutschsprachigen Trennmustermannschaft:
2342 # http://mirrors.ctan.org/language/hyphenation/dehyph-exptl/projektbeschreibung.pdf