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 werkzeug 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 # >>> abbeissen = WordEntry(
207 # ... u'abbeissen;-2-;-3-;-4-;-5-;ab<bei-ssen;ab<beis-sen;ab<beis-sen')
208 # >>> print abbeissen.lang_index('de')
210 # >>> print abbeissen.lang_index('de-x-GROSS')
212 # >>> abbeissen.lang_index('de-CH-1901')
217 def lang_index(self
, sprachvariante
):
219 assert sprachvariante
in self
.sprachvarianten
, \
220 'Sprachvariante nicht in ' + str(self
.sprachvarianten
.keys())
222 # Einfacher Fall: eine allgemeine Schreibweise::
227 # Spezielle Schreibung::
230 i
= self
.sprachvarianten
[sprachvariante
]
233 if i
> 4 and len(self
) == 5:
234 return 4 # Allgemeine Schweiz/GROSS Schreibung:
235 return None # Feld nicht vorhanden
237 if feld
.startswith('-'): # '-1-', '-2-', ...
238 return None # leeres Feld
242 # Trennmuster für Sprachvariante ausgeben
244 # >>> aalbestand.get('de')
246 # >>> aalbestand.get('de-1901')
248 # >>> aalbestand.get('de-1996')
250 # >>> aalbestand.get('de-x-GROSS')
252 # >>> aalbestand.get('de-1901-x-GROSS')
254 # >>> aalbestand.get('de-1996-x-GROSS')
256 # >>> aalbestand.get('de-CH-1901')
259 # >>> print abbeissen.get('de')
261 # >>> print abbeissen.get('de-x-GROSS')
263 # >>> abbeissen.get('de-1901-x-GROSS')
265 # >>> abbeissen.get('de-CH-1901')
270 def get(self
, sprachvariante
):
272 return self
[self
.lang_index(sprachvariante
)]
273 except TypeError: # Muster in `sprachvariante` nicht vorhanden
276 # Trennmuster für Sprachvariante setzen
278 # >>> abbeissen.set('test', 'de-1901-x-GROSS')
279 # >>> print abbeissen
280 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
282 # >>> abbeissen.set('test', 'de-1901')
283 # Traceback (most recent call last):
285 # IndexError: kann kein leeres Feld setzen
289 def set(self
, wort
, sprachvariante
):
290 i
= self
.lang_index(sprachvariante
)
292 raise IndexError, "kann kein leeres Feld setzen"
295 # Felder für alle Sprachvarianten ausfüllen
297 # >>> print str(aalbestand), len(aalbestand)
298 # Aalbestand;Aal=be<stand # Test 2
299 # >>> aalbestand.expand_fields()
300 # >>> print len(aalbestand)
302 # >>> auffrass = WordEntry('auffrass;-2-;-3-;-4-;auf-frass')
303 # >>> auffrass.expand_fields()
305 # auffrass;-2-;-3-;-4-;auf-frass;auf-frass;auf-frass;auf-frass
309 def expand_fields(self
):
310 fields
= [self
.get(sv
) or '-%d-' % (self
.sprachvarianten
[sv
] + 1)
311 for sv
in sorted(self
.sprachvarianten
.keys(),
312 key
=self
.sprachvarianten
.get
)]
314 for i
, field
in enumerate(fields
):
316 self
[i
+1] = field
# Feld 1 ist "key" (ungetrennt)
321 # Felder für Sprachvarianten zusammenfassen
323 # >>> aalbestand.conflate_fields()
324 # >>> print aalbestand
325 # Aalbestand;Aal=be<stand # Test
326 # >>> auffrass.conflate_fields()
328 # auffrass;-2-;-3-;-4-;auf-frass
329 # >>> entry = WordEntry(u'distanziert;-2-;di-stan-ziert;di-stan-ziert')
330 # >>> entry.conflate_fields()
332 # distanziert;di-stan-ziert
333 # Aber nicht, wenn die Trennstellen sich unterscheiden:
335 # >>> abenddienste = WordEntry(
336 # ... u'Abenddienste;-2-;Abend=dien-ste;Abend=diens-te')
337 # >>> abenddienste.conflate_fields()
338 # >>> print abenddienste
339 # Abenddienste;-2-;Abend=dien-ste;Abend=diens-te
343 def conflate_fields(self
):
345 if self
[7] == self
[6] == self
[5]:
346 self
[4] = self
[5] # umschreiben auf GROSS-allgemein
351 if self
[4] == self
[2]: # de-x-GROSS == de-1901
356 if self
[3] == self
[2]: # de-1996 == de-1901
357 self
[1] = self
[2] # Umschreiben auf de (allgemein)
362 # Prüfe auf Vorkommen von Regeländerungen der Orthographiereform 1996.
364 # >>> entry = WordEntry(u'Würste;Wür-ste')
365 # >>> entry.regelaenderungen()
366 # >>> print unicode(entry)
367 # Würste;-2-;Wür-ste;Würs-te
368 # >>> entry = WordEntry(u'Würste;Würs-te')
369 # >>> entry.regelaenderungen()
370 # >>> print unicode(entry)
371 # Würste;-2-;Wür-ste;Würs-te
372 # >>> entry = WordEntry(u'Hecke;He-cke')
373 # >>> entry.regelaenderungen()
374 # >>> print unicode(entry)
375 # Hecke;-2-;He{ck/k-k}e;He-cke
376 # >>> entry = WordEntry(u'Ligusterhecke;Ligu-ster=he{ck/k-k}e')
377 # >>> entry.regelaenderungen()
378 # >>> print unicode(entry)
379 # Ligusterhecke;-2-;Ligu-ster=he{ck/k-k}e;Ligus-ter=he-cke
380 # >>> entry = WordEntry(u'Hass;Hass')
381 # >>> entry.regelaenderungen()
382 # >>> print unicode(entry)
383 # Hass;-2-;-3-;Hass;Hass
384 # >>> entry = WordEntry(u'fasst;fasst')
385 # >>> entry.regelaenderungen()
386 # >>> print unicode(entry)
387 # fasst;-2-;-3-;fasst;fasst
391 def regelaenderungen(self
):
393 r1901
= (u
'-st', u
'{ck/k-k}')
394 r1996
= (u
's-t', u
'-ck')
395 # kein Schluss-ss und sst in de-1901
396 # aber: 'ßt' und Schluß-ß auch in de-1996 möglich (langer Vokal)
398 w1901
= self
.get('de-1901')
399 w1996
= self
.get('de-1996')
401 if w1901
is None or w1996
is None:
404 for r1
, r2
in zip(r1901
, r1996
):
405 w1901
= w1901
.replace(r2
,r1
)
406 w1996
= w1996
.replace(r1
,r2
)
407 if u
'sst' in w1901
or w1901
.endswith(u
'ss'):
410 if w1901
== w1996
: # keine Regeländerung im Wort
412 self
.conflate_fields()
415 self
.extend( ['']*(5-len(self
)) )
421 self
.extend( ['']*(4-len(self
)) )
434 # Trennzeichen entfernen::
436 def join_word(word
, assert_complete
=False):
438 # Einfache Trennzeichen:
440 # == ================================================================
441 # \· ungewichtete Trennstelle (solche, wo sich noch niemand um die
442 # Gewichtung gekümmert hat)
443 # \. unerwünschte Trennstelle (sinnentstellend), z.B. Ur·in.stinkt
444 # oder ungünstige Trennstelle (verwirrend), z.B. Atom·en.er·gie
445 # in ungewichteten Wörtern
446 # \= Trennstelle an Wortfugen (Wort=fu-ge)
447 # \< Trennstelle nach Präfix (Vor<sil-be)
448 # \> Trennstelle vor Suffix (Freund>schaf-ten)
449 # \- Nebentrennstelle (ge-hen)
450 # == ================================================================
455 for char
in u
'·.=|-_<>':
456 table
[ord(char
)] = None
457 key
= word
.translate(table
)
459 # Spezielle Trennungen für die traditionelle Rechtschreibung
460 # (siehe ../../dokumente/README.wortliste)::
462 if '{' in key
or '}' in key
:
463 key
= key
.replace(u
'{ck/kk}', u
'ck')
464 key
= key
.replace(u
'{ck/k', u
'k')
465 key
= key
.replace(u
'k}', u
'k')
466 # Konsonanthäufungen an Wortfuge: '{xx/xxx}' -> 'xx':
467 key
= re
.sub(ur
'\{(.)\1/\1\1\1\}', ur
'\1\1', key
)
468 # schon getrennt: ('{xx/xx' -> 'xx' und 'x}' -> 'x'):
469 key
= re
.sub(ur
'\{(.)\1/\1\1$', ur
'\1\1', key
)
470 key
= re
.sub(ur
'^(.)\}', ur
'\1', key
)
472 # Trennstellen in doppeldeutigen Wörtern::
474 if '[' in key
or ']' in key
:
475 key
= re
.sub(ur
'\[(.*)/\1\]', ur
'\1', key
)
477 key
= re
.sub(ur
'\[([^/\[]+)$', ur
'\1', key
)
478 key
= re
.sub(ur
'^([^/\]]+)\]', ur
'\1', key
)
480 # Test auf verbliebene komplexe Trennstellen::
483 for spez
in u
'[{/}]':
485 raise AssertionError('Spezialtrennung %s, %s' %
486 (word
.encode('utf8'), key
.encode('utf8')))
493 # Zerlege ein Wort mit Trennzeichen in eine Liste von Silben und eine Liste
496 # >>> from werkzeug import zerlege
498 # >>> zerlege(u'Haupt=stel-le')
499 # ([u'Haupt', u'stel', u'le'], [u'=', u'-'])
500 # >>> zerlege(u'Ge<samt=be<triebs=rats==chef')
501 # ([u'Ge', u'samt', u'be', u'triebs', u'rats', u'chef'], [u'<', u'=', u'<', u'=', u'=='])
502 # >>> zerlege(u'an<stands>los')
503 # ([u'an', u'stands', u'los'], [u'<', u'>'])
504 # >>> zerlege(u'An<al.pha-bet')
505 # ([u'An', u'al', u'pha', u'bet'], [u'<', u'.', u'-'])
510 silben
= re
.split(u
'[-·._<>=]+', wort
)
511 trennzeichen
= re
.split(u
'[^-·._|<>=]+', wort
)
512 return silben
, [tz
for tz
in trennzeichen
if tz
]
517 # Fehler beim Übertragen von Trennstellen mit uebertrage_::
519 class TransferError(ValueError):
520 def __init__(self
, wort1
, wort2
):
521 msg
= u
'Inkompatibel: %s %s' % (wort1
, wort2
)
522 ValueError.__init
__(self
, msg
.encode('utf8'))
524 def __unicode__(self
):
525 return str(self
).decode('utf8')
531 # Übertrage die Trennzeichen von `wort1` auf `wort2`:
533 # >>> from werkzeug import uebertrage, TransferError
535 # >>> uebertrage(u'Haupt=stel-le', u'Haupt·stel·le')
538 # Auch teilweise Übertragung, von "kategorisiert" nach "unkategorisiert":
540 # >>> print uebertrage(u'Haupt=stel-le', u'Haupt=stel·le')
543 # >>> print uebertrage(u'Haupt·stel-le', u'Haupt=stel·le')
546 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler')
549 # >>> print uebertrage(u'Freund>schaf·ten', u'Freund-schaf-ten')
552 # Übertragung doppelter Marker:
554 # >>> print uebertrage(u'ver<<aus<ga-be', u'ver<aus<ga-be')
557 # >>> print uebertrage(u'freund>lich>>keit', u'freund>lich>keit')
560 # >>> print uebertrage(u'Amts==haupt=stel-le', u'Amts=haupt=stel-le')
561 # Amts==haupt=stel-le
563 # Kein Überschreiben doppelter Marker:
564 # >>> print uebertrage(u'ver<aus<ga-be', u'ver<<aus<ga-be')
567 # >>> print uebertrage(u'Amts=haupt=stel-le', u'Amts==haupt=stel·le')
568 # Amts==haupt=stel-le
570 # Erhalt des Markers für ungünstige Stellen:
571 # >>> print uebertrage(u'An·al.pha·bet', u'An<al.pha-bet')
574 # Keine Übertragung, wenn die Zahl oder Position der Trennstellen
575 # unterschiedlich ist oder bei unterschiedlichen Wörtern:
578 # ... uebertrage(u'Ha-upt=stel-le', u'Haupt=stel·le')
579 # ... uebertrage(u'Haupt=ste-lle', u'Haupt=stel·le')
580 # ... uebertrage(u'Waupt=stel-le', u'Haupt=stel·le')
581 # ... except TransferError:
584 # Übertragung auch bei unterschiedlicher Schreibung oder Position der
585 # Trennstellen mit `strict=False` (für Abgleich zwischen Sprachvarianten):
587 # >>> uebertrage(u'er-ster', u'ers·ter', strict=False)
589 # >>> uebertrage(u'Fluß=bett', u'Fluss·bett', strict=False)
591 # >>> uebertrage(u'ab>bei-ßen', u'ab>beis·sen', strict=False)
593 # >>> print uebertrage(u'Aus<tausch=dien-stes', u'Aus-tausch=diens-tes', False)
594 # Aus<tausch=diens-tes
596 # Auch mit `strict=False` muß die Zahl der Trennstellen übereinstimmen
597 # (Ausnahmen siehe unten):
600 # ... uebertrage(u'Ha-upt=ste-lle', u'Haupt=stel·le', strict=False)
601 # ... except TransferError:
604 # Akzeptiere unterschiedliche Anzahl von Trennungen bei st und ck nach
607 # >>> uebertrage(u'acht=ecki-ge', u'acht·e{ck/k·k}i·ge', strict=False)
608 # u'acht=e{ck/k-k}i-ge'
609 # >>> uebertrage(u'As-to-ria', u'Asto·ria', strict=False)
611 # >>> uebertrage(u'Asto-ria', u'As·to·ria', strict=False)
613 # >>> uebertrage(u'So-fa=ecke', u'So·fa=e{ck/k-k}e', strict=False)
614 # u'So-fa=e{ck/k-k}e'
616 # Mit ``upgrade=False`` werden nur unspezifische Trennstellen überschrieben:
618 # >>> print uebertrage(u'an=stel-le', u'an<stel·le', upgrade=False)
621 # >>> print uebertrage(u'Aus<stel-ler', u'Aus-stel-ler', upgrade=False)
624 # >>> print uebertrage(u'Aus-stel-ler', u'Aus<stel-ler', upgrade=False)
627 # >>> print uebertrage(u'vor<an<<stel-le', u'vor-an<stel·le', upgrade=False)
632 selbstlaute
= u
'aeiouäöüAEIOUÄÖÜ'
634 def uebertrage(wort1
, wort2
, strict
=True, upgrade
=True):
636 silben1
, trennzeichen1
= zerlege(wort1
)
637 silben2
, trennzeichen2
= zerlege(wort2
)
638 # Prüfe strikte Übereinstimmung:
639 if silben1
!= silben2
and strict
:
640 if u
'<' in trennzeichen1
or u
'·' in trennzeichen2
:
641 raise TransferError(wort1
, wort2
)
644 # Prüfe ungefähre Übereinstimmung:
645 if len(trennzeichen1
) != len(trennzeichen2
):
646 # Selbstlaut + st oder ck?
647 for s
in selbstlaute
:
648 if (wort2
.find(s
+u
'{ck/k·k}') != -1 or
649 wort2
.find(s
+u
'{ck/k-k}') != -1):
650 wort1
= wort1
.replace(s
+u
'ck', s
+u
'-ck')
651 silben1
, trennzeichen1
= zerlege(wort1
)
652 if wort2
.find(s
+u
's·t') != -1:
653 wort1
= wort1
.replace(s
+u
'st', s
+u
's-t')
654 silben1
, trennzeichen1
= zerlege(wort1
)
655 elif wort1
.find(s
+u
's-t') != -1:
656 wort1
= wort1
.replace(s
+u
's-t', s
+u
'st')
657 silben1
, trennzeichen1
= zerlege(wort1
)
658 # print u'retry:', silben1, trennzeichen1
659 # immer noch ungleiche Zahl an Trennstellen?
660 if len(trennzeichen1
) != len(trennzeichen2
):
661 raise TransferError(wort1
, wort2
)
663 # Baue wort3 aus silben2 und spezifischeren Trennzeichen:
664 wort3
= silben2
.pop(0)
665 for t1
,t2
in zip(trennzeichen1
, trennzeichen2
):
666 if ((t2
== u
'·' and t1
!= u
'.') # unspezifisch
668 ((t2
in (u
'-', u
'<') and t1
in (u
'<', u
'<<', u
'<=')) # Praefixe
669 or (t2
in (u
'-', u
'>') and t1
in (u
'>', u
'>>', u
'=>')) # Suffixe
670 or (t2
in (u
'-', u
'=') and t1
in (u
'=', u
'==', u
'===')) # W-fugen
674 elif t2
== u
'.' and t1
not in u
'·.':
678 wort3
+= silben2
.pop(0)
682 # Übertrag kategorisierter Trennstellen zwischen den Feldern aller Einträge
685 def sprachabgleich(entry
, vorbildentry
=None):
688 return # allgemeine Schreibung
690 mit_affix
= None # < oder >
691 kategorisiert
= None # kein ·
692 unkategorisiert
= None # mindestens ein ·
693 gewichtet
= None # == oder <= oder =>
694 for field
in entry
[1:]:
695 if field
.startswith('-'): # -2-, -3-, ...
698 unkategorisiert
= field
699 elif u
'<' in field
or u
'>' in field
:
702 kategorisiert
= field
703 if u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
706 for field
in vorbildentry
[1:]:
707 if field
.startswith('-'): # -2-, -3-, ...
709 if not mit_affix
and u
'<' in field
or u
'>' in field
:
711 elif not kategorisiert
and unkategorisiert
and u
'·' not in field
:
712 kategorisiert
= field
713 if not gewichtet
and u
'==' in field
or u
'<=' in field
or u
'=>' in field
:
715 # print 've:', mit_affix, kategorisiert, unkategorisiert
716 if mit_affix
and (kategorisiert
or unkategorisiert
or gewichtet
):
717 for i
in range(1,len(entry
)):
718 if entry
[i
].startswith('-'): # -2-, -3-, ...
720 if u
'<' not in entry
[i
] or u
'·' in entry
[i
]:
722 entry
[i
] = uebertrage(mit_affix
, entry
[i
], strict
=False)
723 except TransferError
, e
:
724 print u
'Sprachabgleich:', unicode(e
)
725 print mit_affix
+u
':', unicode(entry
)
726 elif kategorisiert
and unkategorisiert
:
727 for i
in range(1,len(entry
)):
730 entry
[i
] = uebertrage(kategorisiert
, entry
[i
], strict
=False)
731 except TransferError
, e
:
732 print u
'Sprachabgleich:', unicode(e
)
733 # print kategorisiert, unicode(entry)
735 for i
in range(1,len(entry
)):
738 entry
[i
] = uebertrage(gewichtet
, entry
[i
], strict
=False)
739 except TransferError
, e
:
740 print u
'Sprachabgleich:', unicode(e
)
744 # Großschreibung in Kleinschreibung wandeln und umgekehrt
746 # Diese Version funktioniert auch für Wörter mit Trennzeichen (während
747 # str.title() nach jedem Trennzeichen wieder groß anfängt)
749 # >>> from werkzeug import toggle_case
750 # >>> toggle_case(u'Ha-se')
752 # >>> toggle_case(u'arm')
754 # >>> toggle_case(u'frei=bier')
756 # >>> toggle_case(u'L}a-ger')
759 # Keine Änderung bei Wörtern mit Großbuchstaben im Inneren:
761 # >>> toggle_case(u'USA')
763 # >>> toggle_case(u'iRFD')
766 # >>> toggle_case(u'gri[f-f/{ff/ff')
768 # >>> toggle_case(u'Gri[f-f/{ff/ff')
773 def toggle_case(wort
):
775 key
= join_word(wort
, assert_complete
=True)
776 except AssertionError:
781 return wort
[0].upper() + wort
[1:]
788 # Duden-Sortierung für die Wortliste
790 # >>> from werkzeug import sortkey_duden
791 # >>> sortkey_duden([u"Abflußröhren"])
792 # u'abflussrohren a*bflu*szroehren'
793 # >>> sortkey_duden([u"Abflußrohren"])
794 # u'abflussrohren a*bflu*szro*hren'
795 # >>> sortkey_duden([u"Abflussrohren"])
798 # >>> s = sorted([[u"Abflußröhren"], [u"Abflußrohren"], [u"Abflussrohren"]],
799 # ... key=sortkey_duden)
800 # >>> print ', '.join(e[0] for e in s)
801 # Abflussrohren, Abflußrohren, Abflußröhren
805 # Ligaturen auflösen und andere "normalisierunde" Ersetzungen für den
806 # (Haupt-)Sortierschlüssel (Akzente werden über ``unicodedata.normalize``
815 # "Zweitschlüssel" zur Unterscheidung von Umlauten/SZ und Basisbuchstaben::
833 # Sortiere nach erstem Feld, alphabetisch gemäß Duden-Regeln::
835 def sortkey_duden(entry
):
837 # Sortieren nach erstem Feld (ungetrenntes Wort)::
841 if len(entry
) == 1: # ein Muster pro Zeile, siehe z.B. pre-1901
844 # Großschreibung ignorieren:
846 # Der Duden sortiert Wörter, die sich nur in der Großschreibung unterscheiden
847 # "klein vor groß" (ASCII sortiert "groß vor klein"). In der
848 # `Trennmuster-Wortliste` kommen Wörter nur mit der häufiger anzutreffenden
849 # Großschreibung vor, denn der TeX-Trennalgorithmus ignoriert Großschreibung.
858 skey
= key
.replace(u
'ß', u
'ss')
860 # Restliche Akzente weglassen: Wandeln in Darstellung von Buchstaben mit
861 # Akzent als "Grundzeichen + kombinierender Akzent". Anschließend alle
862 # nicht-ASCII-Zeichen ignorieren::
864 skey
= skey
.translate(umschrift_skey
)
865 skey
= unicodedata
.normalize('NFKD', skey
)
866 skey
= unicode(skey
.encode('ascii', 'ignore'))
868 # "Zweitschlüssel" für das eindeutige Einsortieren von Wörtern mit
869 # gleichem Schlüssel (Masse/Maße, waren/wären, ...):
871 # * "*" nach aou für die Unterscheidung Grund-/Umlaut
877 subkey
= key
.translate(umschrift_subkey
)
878 skey
= u
'%s %s' % (skey
,subkey
)
880 # Gib den Sortierschlüssel zurück::
889 # Vergleiche zwei Sequenzen von `WordEntries`, gib einen "unified diff" als
890 # Byte-String zurück (weil difflib nicht mit Unicode-Strings arbeiten kann).
894 # >>> from werkzeug import udiff
895 # >>> print udiff([abbeissen, aalbestand], [abbeissen], 'alt', 'neu')
899 # abbeissen;-2-;-3-;-4-;-5-;test;ab<beis-sen;ab<beis-sen
900 # -Aalbestand;Aal=be<stand # Test
904 def udiff(a
, b
, fromfile
='', tofile
='',
905 fromfiledate
='', tofiledate
='', n
=1, encoding
='utf8'):
907 a
= [unicode(entry
).encode(encoding
) for entry
in a
]
908 b
= [unicode(entry
).encode(encoding
) for entry
in b
]
910 diff
= difflib
.unified_diff(a
, b
, fromfile
, tofile
,
911 fromfiledate
, tofiledate
, n
, lineterm
='')
914 return '\n'.join(diff
)
919 def test_keys(wortliste
):
920 """Teste Übereinstimmung des ungetrennten Wortes in Feld 1
921 mit den Trennmustern nach Entfernen der Trennmarker.
922 Schreibe Inkonsistenzen auf die Standardausgabe.
924 `wortliste` ist ein Iterator über die Einträge (Klasse `WordEntry`)
927 for entry
in wortliste
:
928 # Test der Übereinstimmung ungetrenntes/getrenntes Wort
931 for wort
in entry
[1:]:
932 if wort
.startswith(u
'-'): # leere Felder
934 if key
!= join_word(wort
):
936 print u
"\nkey '%s' != '%s'" % (key
, wort
),
937 if key
.lower() == join_word(wort
).lower():
938 print(u
" Abgleich der Großschreibung mit"
939 u
"`prepare-patch.py grossabgleich`."),
948 if __name__
== '__main__':
951 # sys.stdout mit UTF8 encoding (wie in Python 3)
952 sys
.stdout
= codecs
.getwriter('UTF-8')(sys
.stdout
)
954 print u
"Test der Werkzeuge und inneren Konsistenz der Wortliste\n"
956 # wordfile = WordFile('../../wortliste-binnen-s')
957 wordfile
= WordFile('../../wortliste')
958 # print 'Dateiobjekt:', wordfile
960 # Liste der Datenfelder (die Klasseninstanz als Argument für `list` liefert
961 # den Iterator über die Felder, `list` macht daraus eine Liste)::
963 wortliste
= list(wordfile
)
964 print len(wortliste
), u
"Einträge\n"
970 # sprache = 'de-1901' # traditionell
971 # sprache = 'de-1996' # Reformschreibung
972 # sprache = 'de-x-GROSS' # ohne ß (Schweiz oder GROSS) allgemein
973 # sprache = 'de-1901-x-GROSS' # ohne ß (Schweiz oder GROSS) "traditionell"
974 # sprache = 'de-1996-x-GROSS' # ohne ß (Schweiz oder GROSS) "reformiert"
975 # sprache = 'de-CH-1901' # ohne ß (Schweiz) "traditionell" ("süssauer")
977 # worte = [entry.get(sprache) for entry in wortliste if wort is not None]
978 # print len(worte), u"Einträge für Sprachvariante", sprache
983 print u
"Teste Schlüssel-Trennmuster-Übereinstimmung:",
984 if test_keys(wortliste
):
992 for entry
in wortliste
:
993 key
= entry
[0].lower()
999 print u
"Doppeleinträge (ohne Berücksichtigung der Großschreibung)."
1001 print u
" Entfernen mit `prepare-patch.py doppelte`."
1002 print u
" Patch vor Anwendung durchsehen!"
1005 # Ein Wörterbuch (dict Instanz)::
1007 # wordfile.seek(0) # Pointer zurücksetzen
1008 # words = wordfile.asdict()
1010 # print len(words), u"Wörterbucheinträge"
1012 # Zeilenrekonstruktion::
1014 # am Beispiel der Scheiterbeige:
1015 # original = u'beige;beige # vgl. Scheiter-bei-ge'
1016 # entry = words[u"beige"]
1017 # line = unicode(entry)
1018 # assert original == line, "Rejoined %s != %s" % (line, original)
1021 wordfile
.seek(0) # Pointer zurücksetzen
1023 line
= wordfile
.readline().rstrip().decode(wordfile
.encoding
)
1025 entry
= WordEntry(line
)
1026 if line
== unicode(entry
):
1030 print u
'+', unicode(entry
)
1031 line
= wordfile
.readline().rstrip().decode(wordfile
.encoding
)
1033 print OK
, u
"Einträge rekonstruiert"
1040 # .. [BCP47] A. Phillips und M. Davis, (Editoren.),
1041 # `Tags for Identifying Languages`, http://www.rfc-editor.org/rfc/bcp/bcp47.txt
1043 # .. _Wortliste der deutschsprachigen Trennmustermannschaft:
1044 # http://mirrors.ctan.org/language/hyphenation/dehyph-exptl/projektbeschreibung.pdf