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")
35 # Klasse zum Lesen und Schreiben der `Wortliste`::
49 # Die spezielle Funktion `__iter__` wird aufgerufen wenn über eine
50 # Klasseninstanz iteriert wird.
52 # Liefer einen Iterator über die "geparsten" Zeilen (Datenfelder)::
55 line
= self
.readline().rstrip().decode(self
.encoding
)
58 line
= self
.readline().rstrip().decode(self
.encoding
)
63 # Lies Datei und trage die Zeilen mit ungetrenntem Wort
64 # als `key` und den Datenfeldern als `value` in ein `dictionary`
65 # (assoziatives Array) ein::
70 words
[entry
[0]] = entry
76 # Schreibe eine Liste von `unicode` Strings (Zeilen ohne Zeilenendezeichen)
77 # in die Datei `destination`::
79 def writelines(self
, lines
, destination
, encoding
=None):
80 outfile
= codecs
.open(destination
, 'w',
81 encoding
=(encoding
or self
.encoding
))
82 outfile
.write(u
'\n'.join(lines
))
88 # Schreibe eine Liste von Datenfeldern (geparste Zeilen) in die Datei
91 def write_entry(self
, wortliste
, destination
, encoding
=None):
92 lines
= [unicode(entry
) for entry
in wortliste
]
93 self
.writelines(lines
, destination
, encoding
)
99 # Klasse für Einträge (Zeilen) der Wortliste
103 # >>> from wortliste import WordEntry
105 # >>> aalbestand = WordEntry(u'Aalbestand;Aal=be<stand # Test')
106 # >>> print aalbestand
107 # Aalbestand;Aal=be<stand # Test
111 class WordEntry(list):
116 # Kommentare (aktualisiert, wenn Kommentar vorhanden)::
123 # 2. Wort mit Trennungen, falls für alle Varianten identisch,
125 # 3. falls Feld 2 leer, Trennung nach traditioneller Rechtschreibung
126 # 4. falls Feld 2 leer, Trennung nach reformierter Rechtschreibung (2006)
127 # 5. falls Feld 2 leer, Trennung für Wortform, die entweder in
128 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird
129 # und für traditionelle und reformierte Rechtschreibung identisch ist
130 # 6. falls Feld 5 leer, Trennung für Wortform, die entweder in
131 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
132 # traditionelle Rechtschreibung
133 # 7. falls Feld 5 leer, Trennung für Wortform, die entweder in
134 # der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
135 # reformierte Rechtschreibung (2006)
136 # 8. falls Feld 5 leer, Trennung nach (deutsch)schweizerischer
137 # Rechtschreibung; insbesondere Wörter mit "sss" gefolgt von
138 # einem Vokal, die wie andere Dreifachkonsonanten gehandhabt wurden
139 # (also anders, als der Duden früher vorgeschrieben hat), z.B.
142 # Sprachvarianten (Tags nach [BCP47]_) (Die Zählung der Indizes beginn in
146 'de': 1, # Deutsch, allgemeingültig
147 'de-1901': 2, # "traditionell" (nach Rechtschreibreform 1901)
148 'de-1996': 3, # reformierte Reformschreibung (1996)
149 'de-x-GROSS': 4, # ohne ß (Schweiz oder GROSS) allgemein
150 'de-1901-x-GROSS': 5, # ohne ß (Schweiz oder GROSS) "traditionell"
151 'de-1996-x-GROSS': 6, # ohne ß (Schweiz oder GROSS) "reformiert"
152 # 'de-CH-1996': 6, # Alias für 'de-1996-x-GROSS'
153 'de-CH-1901': 7, # ohne ß (Schweiz) "traditionell" ("süssauer")
159 def __init__(self
, line
, delimiter
=';'):
160 self
.delimiter
= delimiter
162 # eventuell vorhandenen Kommentar abtrennen und speichern::
165 line
= line
.split(u
'#')
166 self
.comment
= u
'#'.join(line
[1:])
167 line
= line
[0].rstrip()
169 # Zerlegen in Datenfelder, in Liste eintragen::
171 list.__init
__(self
, line
.split(delimiter
))
174 # Rückverwandlung in String
175 # -----------------------------------
177 # Erzeugen eines Eintrag-Strings (Zeile) aus der Liste der Datenfelder und
180 # >>> unicode(aalbestand)
181 # u'Aalbestand;Aal=be<stand # Test'
185 def __unicode__(self
):
186 line
= ';'.join(self
)
188 line
+= ' #' + self
.comment
193 return unicode(self
).encode('utf8')
198 # Index des zur Sprachvariante gehörenden Datenfeldes:
200 # >>> aalbestand.lang_index('de')
202 # >>> aalbestand.lang_index('de-1901')
204 # >>> aalbestand.lang_index('de-1996')
206 # >>> aalbestand.lang_index('de-x-GROSS')
208 # >>> aalbestand.lang_index('de-1901-x-GROSS')
210 # >>> aalbestand.lang_index('de-1996-x-GROSS')
212 # >>> abbeissen = WordEntry(
213 # ... u'abbeissen;-2-;-3-;-4-;-5-;ab<bei-ssen;ab<beis-sen;ab<beis-sen')
214 # >>> print abbeissen.lang_index('de')
216 # >>> print abbeissen.lang_index('de-x-GROSS')
218 # >>> abbeissen.lang_index('de-CH-1901')
223 def lang_index(self
, lang
):
225 assert lang
in self
.sprachvarianten
, \
226 'Sprachvariante "%s" nicht in %s' % (lang
,
227 self
.sprachvarianten
.keys())
229 # Einfacher Fall: eine allgemeine Schreibweise::
234 # Spezielle Schreibung::
237 i
= self
.sprachvarianten
[lang
]
240 if i
> 4 and len(self
) == 5:
241 return 4 # Allgemeine Schweiz/GROSS Schreibung:
242 return None # Feld nicht vorhanden
244 if feld
.startswith('-'): # '-1-', '-2-', ...
245 return None # leeres Feld
249 # Trennmuster für Sprachvariante ausgeben
251 # >>> aalbestand.get('de')
253 # >>> aalbestand.get('de-1901')
255 # >>> aalbestand.get('de-1996')
257 # >>> aalbestand.get('de-x-GROSS')
259 # >>> aalbestand.get('de-1901-x-GROSS')
261 # >>> aalbestand.get('de-1996-x-GROSS')
263 # >>> aalbestand.get('de-CH-1901')
266 # >>> print abbeissen.get('de')
268 # >>> print abbeissen.get('de-x-GROSS')
270 # >>> print abbeissen.get('de,de-x-GROSS')
272 # >>> abbeissen.get('de-1901-x-GROSS')
274 # >>> abbeissen.get('de,de-1901,de-1901-x-GROSS')
276 # >>> abbeissen.get('de-CH-1901')
281 def get(self
, sprachvarianten
):
282 for lang
in sprachvarianten
.split(','):
283 i
= self
.lang_index(lang
) # integer>0 or None
288 # Trennmuster für Sprachvariante setzen
290 # >>> abbeissen.set('test', 'de-1901-x-GROSS')
291 # >>> print abbeissen
292 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
294 # >>> abbeissen.set('test', 'de-1901')
295 # Traceback (most recent call last):
297 # IndexError: kann kein leeres Feld setzen
299 # >>> abbeissen.set('test', 'de-1901,de-1901-x-GROSS')
300 # >>> print abbeissen
301 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
305 def set(self
, wort
, sprachvarianten
):
306 for lang
in sprachvarianten
.split(','):
307 i
= self
.lang_index(lang
)
314 raise IndexError, "kann kein leeres Feld setzen"
316 # Felder für alle Sprachvarianten ausfüllen
318 # >>> print str(aalbestand), len(aalbestand)
319 # Aalbestand;Aal=be<stand # Test 2
320 # >>> aalbestand.expand_fields()
321 # >>> print len(aalbestand)
323 # >>> auffrass = WordEntry('auffrass;-2-;-3-;-4-;auf-frass')
324 # >>> auffrass.expand_fields()
326 # auffrass;-2-;-3-;-4-;auf-frass;auf-frass;auf-frass;auf-frass
330 def expand_fields(self
):
331 fields
= [self
.get(sv
) or '-%d-' % (self
.sprachvarianten
[sv
] + 1)
332 for sv
in sorted(self
.sprachvarianten
.keys(),
333 key
=self
.sprachvarianten
.get
)]
335 for i
, field
in enumerate(fields
):
337 self
[i
+1] = field
# Feld 1 ist "key" (ungetrennt)
342 # Felder für Sprachvarianten zusammenfassen
344 # >>> aalbestand.conflate_fields()
345 # >>> print aalbestand
346 # Aalbestand;Aal=be<stand # Test
347 # >>> auffrass.conflate_fields()
349 # auffrass;-2-;-3-;-4-;auf-frass
350 # >>> entry = WordEntry(u'distanziert;-2-;di-stan-ziert;di-stan-ziert')
351 # >>> entry.conflate_fields()
353 # distanziert;di-stan-ziert
355 # Aber nicht, wenn die Trennstellen sich unterscheiden:
357 # >>> abenddienste = WordEntry(
358 # ... u'Abenddienste;-2-;Abend=dien-ste;Abend=diens-te')
359 # >>> abenddienste.conflate_fields()
360 # >>> print abenddienste
361 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
365 def conflate_fields(self
):
367 if self
[7] == self
[6] == self
[5]:
368 self
[4] = self
[5] # umschreiben auf GROSS-allgemein
373 if self
[4] == self
[2]: # de-x-GROSS == de-1901
378 if self
[3] == self
[2]: # de-1996 == de-1901
379 self
[1] = self
[2] # Umschreiben auf de (allgemein)
384 # Prüfe auf Vorkommen von Regeländerungen der Orthographiereform 1996.
386 # >>> entry = WordEntry(u'Würste;Wür-ste')
387 # >>> entry.regelaenderungen()
388 # >>> print unicode(entry)
389 # Würste;-2-;Wür-ste;Würs-te
390 # >>> entry = WordEntry(u'Würste;Würs-te')
391 # >>> entry.regelaenderungen()
392 # >>> print unicode(entry)
393 # Würste;-2-;Wür-ste;Würs-te
394 # >>> entry = WordEntry(u'Hecke;He-cke')
395 # >>> entry.regelaenderungen()
396 # >>> print unicode(entry)
397 # Hecke;-2-;He{ck/k-k}e;He-cke
398 # >>> entry = WordEntry(u'Ligusterhecke;Ligu-ster=he{ck/k-k}e')
399 # >>> entry.regelaenderungen()
400 # >>> print unicode(entry)
401 # Ligusterhecke;-2-;Ligu-ster=he{ck/k-k}e;Ligus-ter=he-cke
402 # >>> entry = WordEntry(u'Hass;Hass')
403 # >>> entry.regelaenderungen()
404 # >>> print unicode(entry)
405 # Hass;-2-;-3-;Hass;Hass
406 # >>> entry = WordEntry(u'fasst;fasst')
407 # >>> entry.regelaenderungen()
408 # >>> print unicode(entry)
409 # fasst;-2-;-3-;fasst;fasst
413 def regelaenderungen(self
):
415 r1901
= (u
'-st', u
'{ck/k-k}')
416 r1996
= (u
's-t', u
'-ck')
417 # kein Schluss-ss und sst in de-1901
418 # aber: 'ßt' und Schluß-ß auch in de-1996 möglich (langer Vokal)
420 w1901
= self
.get('de-1901')
421 w1996
= self
.get('de-1996')
423 if w1901
is None or w1996
is None:
426 for r1
, r2
in zip(r1901
, r1996
):
427 w1901
= w1901
.replace(r2
,r1
)
428 w1996
= w1996
.replace(r1
,r2
)
429 if u
'sst' in w1901
or w1901
.endswith(u
'ss'):
432 if w1901
== w1996
: # keine Regeländerung im Wort
434 self
.conflate_fields()
437 self
.extend( ['']*(5-len(self
)) )
443 self
.extend( ['']*(4-len(self
)) )
456 # Trennzeichen entfernen::
458 def join_word(word
, assert_complete
=False):
460 # Einfache Trennzeichen:
462 # == ================================================================
463 # \· ungewichtete Trennstelle (solche, wo sich noch niemand um die
464 # Gewichtung gekümmert hat)
465 # \. unerwünschte Trennstelle (sinnentstellend), z.B. Ur·in.stinkt
466 # oder ungünstige Trennstelle (verwirrend), z.B. Atom·en.er·gie
467 # in ungewichteten Wörtern
468 # \= Trennstelle an Wortfugen (Wort=fu-ge)
469 # \< Trennstelle nach Präfix (Vor<sil-be)
470 # \> Trennstelle vor Suffix (Freund>schaf-ten)
471 # \- Nebentrennstelle (ge-hen)
472 # == ================================================================
477 for char
in u
'·.=|-_<>':
478 table
[ord(char
)] = None
479 key
= word
.translate(table
)
481 # Spezielle Trennungen für die traditionelle Rechtschreibung
482 # (siehe ../../dokumente/README.wortliste)::
484 if '{' in key
or '}' in key
:
485 key
= key
.replace(u
'{ck/kk}', u
'ck')
486 key
= key
.replace(u
'{ck/k', u
'k')
487 key
= key
.replace(u
'k}', u
'k')
488 # Konsonanthäufungen an Wortfuge: '{xx/xxx}' -> 'xx':
489 key
= re
.sub(ur
'\{(.)\1/\1\1\1\}', ur
'\1\1', key
)
490 # schon getrennt: ('{xx/xx' -> 'xx' und 'x}' -> 'x'):
491 key
= re
.sub(ur
'\{(.)\1/\1\1$', ur
'\1\1', key
)
492 key
= re
.sub(ur
'^(.)\}', ur
'\1', key
)
494 # Trennstellen in doppeldeutigen Wörtern::
496 if '[' in key
or ']' in key
:
497 key
= re
.sub(ur
'\[(.*)/\1\]', ur
'\1', key
)
499 key
= re
.sub(ur
'\[([^/\[]+)$', ur
'\1', key
)
500 key
= re
.sub(ur
'^([^/\]]+)\]', ur
'\1', key
)
502 # Test auf verbliebene komplexe Trennstellen::
505 for spez
in u
'[{/}]':
507 raise AssertionError('Spezialtrennung %s, %s' %
508 (word
.encode('utf8'), key
.encode('utf8')))
515 # Zerlege ein Wort mit Trennzeichen in eine Liste von Silben und eine Liste
518 # >>> from wortliste import zerlege
520 # >>> zerlege(u'Haupt=stel-le')
521 # ([u'Haupt', u'stel', u'le'], [u'=', u'-'])
522 # >>> zerlege(u'Ge<samt=be<triebs=rats==chef')
523 # ([u'Ge', u'samt', u'be', u'triebs', u'rats', u'chef'], [u'<', u'=', u'<', u'=', u'=='])
524 # >>> zerlege(u'an<stands>los')
525 # ([u'an', u'stands', u'los'], [u'<', u'>'])
526 # >>> zerlege(u'An<al.pha-bet')
527 # ([u'An', u'al', u'pha', u'bet'], [u'<', u'.', u'-'])
532 silben
= re
.split(u
'[-·._<>=]+', wort
)
533 trennzeichen
= re
.split(u
'[^-·._|<>=]+', wort
)
534 return silben
, [tz
for tz
in trennzeichen
if tz
]
539 # Fehler beim Übertragen von Trennstellen mit uebertrage_::
541 class TransferError(ValueError):
542 def __init__(self
, wort1
, wort2
):
543 msg
= u
'Inkompatibel: %s %s' % (wort1
, wort2
)
544 ValueError.__init
__(self
, msg
.encode('utf8'))
546 def __unicode__(self
):
547 return str(self
).decode('utf8')
553 # Übertrage die Trennzeichen von `wort1` auf `wort2`:
555 # >>> from wortliste import uebertrage, TransferError
557 # >>> uebertrage(u'Haupt=stel-le', u'Haupt·stel·le')
560 # Auch teilweise Übertragung, von "kategorisiert" nach "unkategorisiert":
562 # >>> print uebertrage(u'Haupt=stel-le', u'Haupt=stel·le')
565 # >>> print uebertrage(u'Haupt·stel-le', u'Haupt=stel·le')
568 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler')
571 # >>> print uebertrage(u'Freund>schaf·ten', u'Freund-schaf-ten')
574 # Übertragung doppelter Marker:
576 # >>> print uebertrage(u'ver<<aus<ga-be', u'ver<aus<ga-be')
579 # >>> print uebertrage(u'freund>lich>>keit', u'freund>lich>keit')
582 # >>> print uebertrage(u'Amts==haupt=stel-le', u'Amts=haupt=stel-le')
583 # Amts==haupt=stel-le
585 # Kein Überschreiben doppelter Marker:
586 # >>> print uebertrage(u'ver<aus<ga-be', u'ver<<aus<ga-be')
589 # >>> print uebertrage(u'Amts=haupt=stel-le', u'Amts==haupt=stel·le')
590 # Amts==haupt=stel-le
592 # Erhalt des Markers für ungünstige Stellen:
593 # >>> print uebertrage(u'An·al.pha·bet', u'An<al.pha-bet')
596 # Keine Übertragung, wenn die Zahl oder Position der Trennstellen
597 # unterschiedlich ist oder bei unterschiedlichen Wörtern:
600 # ... uebertrage(u'Ha-upt=stel-le', u'Haupt=stel·le')
601 # ... uebertrage(u'Haupt=ste-lle', u'Haupt=stel·le')
602 # ... uebertrage(u'Waupt=stel-le', u'Haupt=stel·le')
603 # ... except TransferError:
606 # Übertragung auch bei unterschiedlicher Schreibung oder Position der
607 # Trennstellen mit `strict=False` (für Abgleich zwischen Sprachvarianten):
609 # >>> uebertrage(u'er-ster', u'ers·ter', strict=False)
611 # >>> uebertrage(u'Fluß=bett', u'Fluss·bett', strict=False)
613 # >>> uebertrage(u'ab>bei-ßen', u'ab>beis·sen', strict=False)
615 # >>> print uebertrage(u'Aus<tausch=dien-stes', u'Aus-tausch=diens-tes', False)
616 # Aus<tausch=diens-tes
618 # Auch mit `strict=False` muß die Zahl der Trennstellen übereinstimmen
619 # (Ausnahmen siehe unten):
622 # ... uebertrage(u'Ha-upt=ste-lle', u'Haupt=stel·le', strict=False)
623 # ... except TransferError:
626 # Akzeptiere unterschiedliche Anzahl von Trennungen bei st und ck nach
629 # >>> uebertrage(u'acht=ecki-ge', u'acht·e{ck/k·k}i·ge', strict=False)
630 # u'acht=e{ck/k-k}i-ge'
631 # >>> uebertrage(u'As-to-ria', u'Asto·ria', strict=False)
633 # >>> uebertrage(u'Asto-ria', u'As·to·ria', strict=False)
635 # >>> uebertrage(u'So-fa=ecke', u'So·fa=e{ck/k-k}e', strict=False)
636 # u'So-fa=e{ck/k-k}e'
638 # Mit ``upgrade=False`` werden nur unspezifische Trennstellen überschrieben:
640 # >>> print uebertrage(u'an=stel-le', u'an<stel·le', upgrade=False)
643 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler', upgrade=False)
646 # >>> print uebertrage(u'Aus-stel-ler', u'Aus<stel-ler', upgrade=False)
649 # >>> print uebertrage(u'vor<an<<stel-le', u'vor-an<stel·le', upgrade=False)
654 selbstlaute
= u
'aeiouäöüAEIOUÄÖÜ'
656 def uebertrage(wort1
, wort2
, strict
=True, upgrade
=True):
658 silben1
, trennzeichen1
= zerlege(wort1
)
659 silben2
, trennzeichen2
= zerlege(wort2
)
660 # Prüfe strikte Übereinstimmung:
661 if silben1
!= silben2
and strict
:
662 if u
'<' in trennzeichen1
or u
'·' in trennzeichen2
:
663 raise TransferError(wort1
, wort2
)
666 # Prüfe ungefähre Übereinstimmung:
667 if len(trennzeichen1
) != len(trennzeichen2
):
668 # Selbstlaut + st oder ck?
669 for s
in selbstlaute
:
670 if (wort2
.find(s
+u
'{ck/k·k}') != -1 or
671 wort2
.find(s
+u
'{ck/k-k}') != -1):
672 wort1
= wort1
.replace(s
+u
'ck', s
+u
'-ck')
673 silben1
, trennzeichen1
= zerlege(wort1
)
674 if wort2
.find(s
+u
's·t') != -1:
675 wort1
= wort1
.replace(s
+u
'st', s
+u
's-t')
676 silben1
, trennzeichen1
= zerlege(wort1
)
677 elif wort1
.find(s
+u
's-t') != -1:
678 wort1
= wort1
.replace(s
+u
's-t', s
+u
'st')
679 silben1
, trennzeichen1
= zerlege(wort1
)
680 # print u'retry:', silben1, trennzeichen1
681 # immer noch ungleiche Zahl an Trennstellen?
682 if len(trennzeichen1
) != len(trennzeichen2
):
683 raise TransferError(wort1
, wort2
)
685 # Baue wort3 aus silben2 und spezifischeren Trennzeichen:
686 wort3
= silben2
.pop(0)
687 for t1
,t2
in zip(trennzeichen1
, trennzeichen2
):
688 if ((t2
== u
'·' and t1
!= u
'.') # unspezifisch
690 ((t2
in (u
'-', u
'<') and t1
in (u
'<', u
'<<', u
'<=')) # Praefixe
691 or (t2
in (u
'-', u
'>') and t1
in (u
'>', u
'>>', u
'=>')) # Suffixe
692 or (t2
in (u
'-', u
'=') and t1
in (u
'=', u
'==', u
'===')) # W-fugen
696 elif t2
== u
'.' and t1
not in u
'·.':
700 wort3
+= silben2
.pop(0)
704 # Übertrag kategorisierter Trennstellen zwischen den Feldern aller Einträge
707 def sprachabgleich(entry
, vorbildentry
=None):
710 return # allgemeine Schreibung
712 mit_affix
= None # < oder >
713 kategorisiert
= None # kein ·
714 unkategorisiert
= None # mindestens ein ·
715 gewichtet
= None # == oder <= oder =>
716 for field
in entry
[1:]:
717 if field
.startswith('-'): # -2-, -3-, ...
720 unkategorisiert
= field
721 elif u
'<' in field
or u
'>' in field
:
724 kategorisiert
= field
725 if u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
728 for field
in vorbildentry
[1:]:
729 if field
.startswith('-'): # -2-, -3-, ...
731 if not mit_affix
and u
'<' in field
or u
'>' in field
:
733 elif not kategorisiert
and unkategorisiert
and u
'·' not in field
:
734 kategorisiert
= field
735 if not gewichtet
and u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
737 # print 've:', mit_affix, kategorisiert, unkategorisiert
738 if mit_affix
and (kategorisiert
or unkategorisiert
or gewichtet
):
739 for i
in range(1,len(entry
)):
740 if entry
[i
].startswith('-'): # -2-, -3-, ...
742 if u
'<' not in entry
[i
] or u
'·' in entry
[i
]:
744 entry
[i
] = uebertrage(mit_affix
, entry
[i
], strict
=False)
745 except TransferError
, e
:
746 print u
'Sprachabgleich:', unicode(e
)
747 # print mit_affix+u':', unicode(entry)
748 elif kategorisiert
and unkategorisiert
:
749 for i
in range(1,len(entry
)):
752 entry
[i
] = uebertrage(kategorisiert
, entry
[i
], strict
=False)
753 except TransferError
, e
:
754 print u
'Sprachabgleich:', unicode(e
)
755 # print kategorisiert, unicode(entry)
757 for i
in range(1,len(entry
)):
760 entry
[i
] = uebertrage(gewichtet
, entry
[i
], strict
=False)
761 except TransferError
, e
:
762 print u
'Sprachabgleich:', unicode(e
)
766 # Großschreibung in Kleinschreibung wandeln und umgekehrt
768 # Diese Version funktioniert auch für Wörter mit Trennzeichen (während
769 # str.title() nach jedem Trennzeichen wieder groß anfängt)
771 # >>> from wortliste import toggle_case
772 # >>> toggle_case(u'Ha-se')
774 # >>> toggle_case(u'arm')
776 # >>> toggle_case(u'frei=bier')
778 # >>> toggle_case(u'L}a-ger')
781 # Keine Änderung bei Wörtern mit Großbuchstaben im Inneren:
783 # >>> toggle_case(u'USA')
785 # >>> toggle_case(u'iRFD')
788 # >>> toggle_case(u'gri[f-f/{ff/ff')
790 # >>> toggle_case(u'Gri[f-f/{ff/ff')
795 def toggle_case(wort
):
797 key
= join_word(wort
, assert_complete
=True)
798 except AssertionError:
803 return wort
[0].upper() + wort
[1:]
810 # Duden-Sortierung für die Wortliste
812 # >>> from wortliste import sortkey_duden
813 # >>> sortkey_duden([u"Abflußröhren"])
814 # u'abflussrohren a*bflu*szroehren'
815 # >>> sortkey_duden([u"Abflußrohren"])
816 # u'abflussrohren a*bflu*szro*hren'
817 # >>> sortkey_duden([u"Abflussrohren"])
820 # >>> s = sorted([[u"Abflußröhren"], [u"Abflußrohren"], [u"Abflussrohren"]],
821 # ... key=sortkey_duden)
822 # >>> print ', '.join(e[0] for e in s)
823 # Abflussrohren, Abflußrohren, Abflußröhren
827 # Ligaturen auflösen und andere "normalisierunde" Ersetzungen für den
828 # (Haupt-)Sortierschlüssel (Akzente werden über ``unicodedata.normalize``
837 # "Zweitschlüssel" zur Unterscheidung von Umlauten/SZ und Basisbuchstaben::
855 # Sortiere nach erstem Feld, alphabetisch gemäß Duden-Regeln::
857 def sortkey_duden(entry
):
859 # Sortieren nach erstem Feld (ungetrenntes Wort)::
863 if len(entry
) == 1: # ein Muster pro Zeile, siehe z.B. pre-1901
866 # Großschreibung ignorieren:
868 # Der Duden sortiert Wörter, die sich nur in der Großschreibung unterscheiden
869 # "klein vor groß" (ASCII sortiert "groß vor klein"). In der
870 # `Trennmuster-Wortliste` kommen Wörter nur mit der häufiger anzutreffenden
871 # Großschreibung vor, denn der TeX-Trennalgorithmus ignoriert Großschreibung.
880 skey
= key
.replace(u
'ß', u
'ss')
882 # Restliche Akzente weglassen: Wandeln in Darstellung von Buchstaben mit
883 # Akzent als "Grundzeichen + kombinierender Akzent". Anschließend alle
884 # nicht-ASCII-Zeichen ignorieren::
886 skey
= skey
.translate(umschrift_skey
)
887 skey
= unicodedata
.normalize('NFKD', skey
)
888 skey
= unicode(skey
.encode('ascii', 'ignore'))
890 # "Zweitschlüssel" für das eindeutige Einsortieren von Wörtern mit
891 # gleichem Schlüssel (Masse/Maße, waren/wären, ...):
893 # * "*" nach aou für die Unterscheidung Grund-/Umlaut
899 subkey
= key
.translate(umschrift_subkey
)
900 skey
= u
'%s %s' % (skey
,subkey
)
902 # Gib den Sortierschlüssel zurück::
911 # Vergleiche zwei Sequenzen von `WordEntries`, gib einen "unified diff" als
912 # Byte-String zurück (weil difflib nicht mit Unicode-Strings arbeiten kann).
916 # >>> from wortliste import udiff
917 # >>> print udiff([abbeissen, aalbestand], [abbeissen], 'alt', 'neu')
921 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
922 # -Aalbestand;Aal=be<stand # Test
926 def udiff(a
, b
, fromfile
='', tofile
='',
927 fromfiledate
='', tofiledate
='', n
=1, encoding
='utf8'):
929 a
= [unicode(entry
).encode(encoding
) for entry
in a
]
930 b
= [unicode(entry
).encode(encoding
) for entry
in b
]
932 diff
= difflib
.unified_diff(a
, b
, fromfile
, tofile
,
933 fromfiledate
, tofiledate
, n
, lineterm
='')
936 return '\n'.join(diff
)
941 def test_keys(wortliste
):
942 """Teste Übereinstimmung des ungetrennten Wortes in Feld 1
943 mit den Trennmustern nach Entfernen der Trennmarker.
944 Schreibe Inkonsistenzen auf die Standardausgabe.
946 `wortliste` ist ein Iterator über die Einträge (Klasse `WordEntry`)
949 for entry
in wortliste
:
950 # Test der Übereinstimmung ungetrenntes/getrenntes Wort
953 for wort
in entry
[1:]:
954 if wort
.startswith(u
'-'): # leere Felder
956 if key
!= join_word(wort
):
958 print u
"\nkey '%s' != '%s'" % (key
, wort
),
959 if key
.lower() == join_word(wort
).lower():
960 print(u
" Abgleich der Großschreibung mit"
961 u
"`prepare-patch.py grossabgleich`."),
970 if __name__
== '__main__':
973 # sys.stdout mit UTF8 encoding (wie in Python 3)
974 sys
.stdout
= codecs
.getwriter('UTF-8')(sys
.stdout
)
976 print u
"Test der Werkzeuge und inneren Konsistenz der Wortliste\n"
978 wordfile
= WordFile('../../../wortliste')
979 # print 'Dateiobjekt:', wordfile
981 # Liste der Datenfelder (die Klasseninstanz als Argument für `list` liefert
982 # den Iterator über die Felder, `list` macht daraus eine Liste)::
984 wortliste
= list(wordfile
)
985 print len(wortliste
), u
"Einträge\n"
991 # sprache = 'de-1901' # traditionell
992 # sprache = 'de-1996' # Reformschreibung
993 # sprache = 'de-x-GROSS' # ohne ß (Schweiz oder GROSS) allgemein
994 # sprache = 'de-1901-x-GROSS' # ohne ß (Schweiz oder GROSS) "traditionell"
995 # sprache = 'de-1996-x-GROSS' # ohne ß (Schweiz oder GROSS) "reformiert"
996 # sprache = 'de-CH-1901' # ohne ß (Schweiz) "traditionell" ("süssauer")
998 # worte = [entry.get(sprache) for entry in wortliste if wort is not None]
999 # print len(worte), u"Einträge für Sprachvariante", sprache
1004 print u
"Teste Schlüssel-Trennmuster-Übereinstimmung:",
1005 if test_keys(wortliste
):
1013 for entry
in wortliste
:
1014 key
= entry
[0].lower()
1017 print unicode(entry
)
1020 print u
"Doppeleinträge (ohne Berücksichtigung der Großschreibung)."
1022 print u
" Entfernen mit `prepare-patch.py doppelte`."
1023 print u
" Patch vor Anwendung durchsehen!"
1026 # Ein Wörterbuch (dict Instanz)::
1028 # wordfile.seek(0) # Pointer zurücksetzen
1029 # words = wordfile.asdict()
1031 # print len(words), u"Wörterbucheinträge"
1033 # Zeilenrekonstruktion::
1035 # am Beispiel der Scheiterbeige:
1036 # original = u'beige;beige # vgl. Scheiter-bei-ge'
1037 # entry = words[u"beige"]
1038 # line = unicode(entry)
1039 # assert original == line, "Rejoined %s != %s" % (line, original)
1042 wordfile
.seek(0) # Pointer zurücksetzen
1044 line
= wordfile
.readline().rstrip().decode(wordfile
.encoding
)
1046 entry
= WordEntry(line
)
1047 if line
== unicode(entry
):
1051 print u
'+', unicode(entry
)
1052 line
= wordfile
.readline().rstrip().decode(wordfile
.encoding
)
1054 print OK
, u
"Einträge rekonstruiert"
1061 # .. [BCP47] A. Phillips und M. Davis, (Editoren.),
1062 # `Tags for Identifying Languages`, http://www.rfc-editor.org/rfc/bcp/bcp47.txt
1064 # .. _Wortliste der deutschsprachigen Trennmustermannschaft:
1065 # http://mirrors.ctan.org/language/hyphenation/dehyph-exptl/projektbeschreibung.pdf