Python-Skripte: kleine Korrekturen.
[wortliste.git] / skripte / python / edit_tools / hyphenate_neueintraege.py
blobcae52c0ca7d914dd2b840ed5b1ca9fe85f189191
1 #!/usr/bin/env python3
2 # -*- coding: utf8 -*-
3 # :Copyright: © 2014 Günter Milde.
4 # Released without warranty under the terms of the
5 # GNU General Public License (v. 2 or later)
6 # :Id: $Id: $
8 # hyphenate_neueintraege.py: kategorisierte Trennung mit patgen-patterns.
9 # =======================================================================
11 # ::
13 """Trenne Wörter mittels "hyphenation"-Algorithmus und patgen-patterns¹.
15 Eingabe: Ein ungetrenntes Wort oder Eintrag im Wortliste-Format pro Zeile.²
17 Ausgabe: Wortliste-Einträge (Neueintrag;Neu=ein-trag)
18 ohne Unterscheidung von Sprachvarianten (!)³
19 getrennt nach:
21 identisch rekonstruiert
22 wenn die vorhandene Trennmarkierung der ermittelten
23 entspricht.
24 mit Pattern getrennt
25 wenn die Eingabe ungetrennt ist oder eine abweichende
26 Trennmarkierung aufweist
28 Bsp: python hyphenate_neueintraege.py < missing-words.txt > neu.todo
30 ``neu.todo`` kann (nach Durchsicht!!) mit `prepare_patch.py neu`
31 in die Wortliste eingepflegt werden.³
33 ¹ Verwendet als Voreinstellung "Reformschreibungs" Pattern-Dateien, welche
34 über die "make" Ziele `make pattern-refo`, `make major pattern-refo`,
35 `make fugen pattern-refo` und `make suffix pattern-refo` im
36 Wurzelverzeichnis der Wortliste generiert werden können.
38 ² Tip: mit `filter_wortliste.py -v < neue.txt > wirklich-neue.txt`
39 können in der WORTLISTE vorhandene Wörter aussortiert werden.
41 ³ `prepare_patch.py neu` nimmt auch eine Unterscheidung nach de-1901/de-1996
42 anhand der wesentlichen Regeländerungen (-st/s-t, ck/c-k, ss/ß)
43 vor. (Schweizer Spezialitäten und andere Grenzfälle müssen per Hand
44 eingepflegt werden.)
46 Für Trennungen nach traditioneller Orthographie (de-1091) müssen die
47 Musterdateinen über die Optionen angegeben werden.
48 """
50 # Doctest: Beispiel für Anwendung als Python-Module
52 # >>> from hyphenate_neueintraege import *
54 # ::
56 import sys, os, glob, argparse, re, random
58 # path for local Python modules (parent dir of this file's dir)
59 sys.path.insert(0,
60 os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
62 # import patuse, trennstellenkategorisierung
63 from wortliste import (WordFile, WordEntry, ShortEntry,
64 sortkey_duden, run_filters)
65 from patuse.hyphenation import Hyphenator
68 # Trenne mit Hyphenator
70 # :Eingabe: ungetrenntes Wort
71 # :Ausgabe: Tupel getrennter Wörter:
73 # * kategorisierte Trennungen
74 # * getrennt mit Standard-Mustern: '=<>-' -> '-'
75 # * getrennt mit "Fugen"-Mustern: '=' -> '-'
76 # * getrennt mit "Major"-Mustern: '=<>' -> '-'
77 # * getrennt mit "Suffix"-Mustern: '>' -> '-'
79 # ::
81 def trenne(word, verbose=False):
82 if not word:
83 return ''
84 parts_standard = h_standard.split_word(word)
85 parts_fugen = h_fugen.split_word(word)
86 parts_major = h_major.split_word(word)
87 parts_suffix = h_suffix.split_word(word)
88 if verbose:
89 print('# standard', parts_standard)
90 print('# fugen', parts_fugen)
91 print('# major', parts_major)
92 print('# suffix', parts_suffix)
94 getrennt = ['-'.join(parts_standard),
95 '-'.join(parts_fugen),
96 '-'.join(parts_major),
97 '-'.join(parts_suffix)]
99 parts = [] # Liste von Silben und Trennzeichen, wird am Ende zusammengefügt.
100 p_major = '' # zum Vergleich mit parts_major
101 p_fugen = ''
102 p_suffix = ''
103 # Kategorisierung der Trennstellen
104 for part_standard in parts_standard[:-1]:
105 parts.append(part_standard)
106 p_major += part_standard
107 p_fugen += part_standard
108 p_suffix += part_standard
109 if parts_fugen and p_fugen == parts_fugen[0]:
110 parts_fugen.pop(0)
111 p_fugen = ''
112 try:
113 parts_major.pop(0)
114 except IndexError:
115 pass
116 p_major = ''
117 parts.append('=')
118 elif parts_suffix and p_suffix == parts_suffix[0]:
119 parts_suffix.pop(0)
120 p_suffix = ''
121 parts.append('>')
122 elif parts_major and p_major == parts_major[0]:
123 parts_major.pop(0)
124 p_major = ''
125 parts.append('<')
126 else:
127 parts.append('-')
128 parts.append(parts_standard[-1])
130 # Alternative Kategorisierung über Zerlegung der Teilwörter/Wortteile:
131 # word = '='.join(['<'.join([h_standard.hyphenate_word(part, '-')
132 # for part in h_major.split_word(teilwort)])
133 # for teilwort in h_fugen.split_word(word)])
134 return [''.join(parts)] + getrennt
136 def print_proposal(entry, verbose=False):
137 getrennt = getattr(entry, "hyphenated", '')
138 if proposal and isinstance(proposal, ShortEntry) or len(proposal) > 1:
139 print(' ' + str(entry))
140 print('# ' + getrennt[0])
141 if verbose:
142 print('# ' + ', '.join(getrennt))
143 # print('# ' + getrennt[-1])
146 # Hauptfunktion::
148 if __name__ == '__main__':
150 # Optionen::
152 # Die neuesten Pattern-Dateien, welche über die "make"-Ziele
154 # make pattern-refo
155 # make major pattern-refo
156 # make fugen pattern-refo
157 # make suffix pattern-refo
159 # im Wurzelverzeichnis der wortliste generiert werden
160 # (--help zeigt das Erstelldatum) ::
162 # Pfad zum Wurzelverzeichnis ("../../../") unabhängig vom Arbeitsverzeichnis::
164 root_dir = os.path.relpath(
165 os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
166 os.path.abspath(__file__))))) )
168 patterns = {}
169 for cat in ('', '-major', '-fugen', '-suffix'):
170 ppath = os.path.join(root_dir, 'dehyphn-x%s/dehyphn-x%s-*.pat'%(cat,cat))
171 patterns[cat] = sorted(glob.glob(ppath))[-1]
173 parser = argparse.ArgumentParser(description = __doc__,
174 formatter_class=argparse.RawDescriptionHelpFormatter)
175 parser.add_argument('-p', '--patterns',
176 help='Pattern-Datei (alle Trennstellen), '
177 'Vorgabe "%s"'%patterns[''], default=patterns[''])
178 parser.add_argument('--patterns_major',
179 help='Pattern-Datei (Trennstellen an Morphemgrenzen), '
180 'Vorgabe "%s"'%patterns['-major'],
181 default=patterns['-major'])
182 parser.add_argument('--patterns_fugen',
183 help='Pattern-Datei (Trennstellen an Wortfugen), '
184 'Vorgabe "%s"'%patterns['-fugen'],
185 default=patterns['-fugen'])
186 parser.add_argument('--patterns_suffix',
187 help='Pattern-Datei (Trennstellen vor Suffixen), '
188 'Vorgabe "%s"'%patterns['-suffix'],
189 default=patterns['-suffix'])
190 parser.add_argument('-a', '--nur-abweichungen', action='store_true',
191 help='Nur abweichende Trennungen ausgeben')
192 parser.add_argument('-k', '--kurzformat', action='store_true',
193 help='Eingabe ist im Kurzformat')
194 parser.add_argument('-l', '--language', metavar='SPRACHE',
195 help='Sprachvariante (Vorgabe: "de-1996").',
196 default="de-1996")
197 parser.add_argument('-u', '--unsortiert', action='store_true',
198 help='Ausgabe ohne Sortierung')
199 parser.add_argument('-v', '--verbose', action='store_true',
200 help='Zusatzinformation ausgeben')
201 parser.add_argument('-w', '--read_probability',
202 help='Wahrscheinlichkeit mit der eine Zeile '
203 'der Eingabe gelesen wird. Vorgabe 1',
204 type=float, default=1)
205 args = parser.parse_args()
208 if args.kurzformat:
209 eintragsklasse = ShortEntry
210 else:
211 eintragsklasse = WordEntry
213 # Trenner-Instanzen::
215 h_standard = Hyphenator(args.patterns)
216 h_major = Hyphenator(args.patterns_major)
217 h_fugen = Hyphenator(args.patterns_fugen)
218 h_suffix = Hyphenator(args.patterns_suffix)
221 # Erstellen der neuen Einträge::
223 proposals = [eintragsklasse(line.strip())
224 for line in sys.stdin
225 if line.strip() and not line.startswith('#')
226 and (random.random() < args.read_probability)]
228 if args.read_probability != 1:
229 print("## Eintragsverarbeitungswahrscheinlichkeit",
230 args.read_probability)
232 # Trennen und ggf. unsortierte Ausgabe::
234 results = []
235 keys = set()
237 for proposal in proposals:
238 key = proposal.key(lang=args.language)
239 getrennt = trenne(key)
240 # getrennt = trenne(key, verbose=args.verbose)
241 try:
242 word = getrennt[0]
243 except IndexError:
244 print("no hyphenation of", proposal, getrennt, file=sys.stderr)
245 continue
246 if not word:
247 continue
249 # unsortierte Ausgabe
250 if args.unsortiert:
251 if key not in keys:
252 keys.add(key)
253 print('# ' + word)
254 if isinstance(proposal, ShortEntry) or len(proposal) > 1:
255 print(' ' + str(proposal))
256 else:
257 proposal.hyphenated = getrennt
258 results.append(proposal)
260 if args.unsortiert:
261 sys.exit()
263 # Vergleich mit Original
265 # Abweichungen von "Proposal" (in gewählter Sprachvariante) wenn der
266 # entprechende Trennstilfilter auf Proposal angewendet wird::
268 x_standard = [] # Abweichung der Standard-Trennstellen
269 x_major = [] # Abweichung bei major ("=" "<" ">")
270 x_fugen = [] # Abweichung bei fugen ("=")
271 x_suffix = [] # Abweichung bei suffix (">")
272 ok = [] # Keine Unterschiede bei der Trennung (bis auf Wichtung)
274 ungetrennt = [] # Eingabe war ungetrennt
276 # ::
278 for proposal in results:
279 (wort, standard, fugen, major, suffix) = proposal.hyphenated
281 if (not proposal
282 or (args.kurzformat == False and len(proposal) == 1)):
283 ungetrennt.append(wort)
284 continue
286 # Anwenden der Trennstile auf Proposals und Prüfen auf Übereinstimmung::
288 p_generisch = run_filters(['morphemisch', 'standard'],
289 proposal.get(args.language))
291 if standard != run_filters(['einfach'], p_generisch):
292 x_standard.append(proposal)
293 continue
295 p_major = run_filters(['morphemgrenzen', 'einfach'], p_generisch)
296 if major != p_major:
297 if args.verbose:
298 proposal.hyphenated.append('major: %s != %s'%(major, p_major))
299 x_major.append(proposal)
300 continue
302 p_fugen = re.sub('([-<.]+=*|=*>+)', '', p_generisch)
303 p_fugen = run_filters(['einfach'], p_fugen)
304 if fugen != p_fugen:
305 if args.verbose:
306 proposal.hyphenated.append('fugen: %s != %s'%(fugen, p_fugen))
307 x_fugen.append(proposal)
308 continue
310 p_suffix = re.sub('[-<=.]+', '', p_generisch)
311 if suffix != run_filters(['einfach'], p_suffix):
312 if args.verbose:
313 proposal.hyphenated.append('suffix: %s != %s'%(suffix, p_suffix))
314 x_suffix.append(proposal)
315 continue
317 else:
318 ok.append(proposal)
320 # Ausgabe::
322 if ungetrennt:
323 print('\n## Trennung mit Pattern (Eingabe ohne Trennungen)')
324 for entry in sorted(ungetrennt, key=sortkey_duden):
325 print(entry)
326 if x_standard:
327 print('\n## Abweichung bei Standard-Trennstellen')
328 for entry in sorted(x_standard, key=sortkey_duden):
329 print_proposal(entry, args.verbose)
330 if not args.nur_abweichungen:
331 if x_major:
332 print('\n## Abweichung bei major ("=" "<" ">")')
333 for entry in sorted(x_major, key=sortkey_duden):
334 print_proposal(entry, args.verbose)
335 if x_fugen:
336 print('\n## Abweichung bei fugen ("=")')
337 for entry in sorted(x_fugen, key=sortkey_duden):
338 print_proposal(entry, args.verbose)
339 if x_suffix:
340 print('\n## Abweichung bei suffix (">")')
341 for entry in sorted(x_suffix, key=sortkey_duden):
342 print_proposal(entry, args.verbose)
343 if ok:
344 print('\n## Gleiche Trennung außer ggf. Wichtung/Unterdrückung:')
345 for entry in sorted(ok, key=sortkey_duden):
346 print_proposal(entry, args.verbose)
348 print('\n## Statistik')
349 # print('#', len(ok), 'gleich')
350 print('#', len(ok), 'gleich (evt. Unterschiede in Wichtung/Unterdrückung)')
351 print('#', len(x_suffix), 'Abweichung bei suffix (">")')
352 print('#', len(x_fugen), 'Abweichung bei fugen ("=")')
353 print('#', len(x_major), 'Abweichung bei major ("=" und "<")')
354 print('#', len(x_standard), 'Abweichung der Standard-Trennstellen')
355 print('#', len(ungetrennt), 'Vorgabe ohne Trennung')