3 # :Copyright: © 2014 Günter Milde.
4 # Released without warranty under the terms of the
5 # GNU General Public License (v. 2 or later)
8 # hyphenate_neueintraege.py: kategorisierte Trennung mit patgen-patterns.
9 # =======================================================================
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 (!)³
21 identisch rekonstruiert
22 wenn die vorhandene Trennmarkierung der ermittelten
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 (Fehler bei
37 `make fugen pattern-refo` und `make suffix pattern-refo` können ignoriert
40 ² Tip: mit `abgleich_neueintraege.py --filter < neue.txt > wirklich-neue.txt`
41 können in der WORTLISTE vorhandene Wörter aussortiert werden.
43 ³ `prepare_patch.py neu` nimmt auch eine Unterscheidung nach de-1901/de-1996
44 anhand der wesentlichen Regeländerungen (-st/s-t, ck/c-k, ss/ß)
45 vor. (Schweizer Spezialitäten und andere Grenzfälle müssen per Hand
48 Für Trennungen nach traditioneller Orthographie (de-1091) müssen die
49 Musterdateinen über die Optionen angegeben werden.
52 # Doctest: Beispiel für Anwendung als Python-Module
54 # >>> from hyphenate_neueintraege import *
58 import sys
, os
, codecs
, glob
, copy
, argparse
, re
, random
60 # path for local Python modules (parent dir of this file's dir)
62 os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
64 # import patuse, trennstellenkategorisierung
65 from wortliste
import (WordFile
, WordEntry
, ShortEntry
,
66 join_word
, toggle_case
, sortkey_duden
)
67 from patuse
.hyphenation
import Hyphenator
70 # Trenne mit Hyphenator
72 # Eingabe: ungetrenntes Wort
73 # Ausgabe: Wort mit kategorisierten Trennungen
77 def trenne(word
, verbose
=False):
80 parts_fugen
= h_fugen
.split_word(word
)
81 parts_major
= h_major
.split_word(word
)
82 parts_suffix
= h_suffix
.split_word(word
)
83 parts_all
= h_all
.split_word(word
)
85 print('#' + parts_fugen
)
86 print('#' + parts_major
)
87 print('#' + parts_suffix
)
88 print('#' + parts_all
)
90 parts
= [] # Liste von Silben und Trennzeichen, wird am Ende zusammengefügt.
91 p_major
= '' # zum Vergleich mit parts_major
94 # Kategorisierung der Trennstellen
95 for part_all
in parts_all
[:-1]:
96 parts
.append(part_all
)
100 if parts_fugen
and p_fugen
== parts_fugen
[0]:
109 elif parts_suffix
and p_suffix
== parts_suffix
[0]:
113 elif parts_major
and p_major
== parts_major
[0]:
119 parts
.append(parts_all
[-1])
120 word
= ''.join(parts
)
122 # Alternative Kategorisierung über Zerlegung der Teilwörter/Wortteile:
123 # word = '='.join(['<'.join([h_all.hyphenate_word(part, '-')
124 # for part in h_major.split_word(teilwort)])
125 # for teilwort in h_fugen.split_word(word)])
129 def print_proposal(entry
):
130 proposal
= getattr(entry
, "proposal", '')
131 if proposal
and isinstance(proposal
, ShortEntry
) or len(proposal
) > 1:
132 print(' ' + str(proposal
))
133 print('# ' + str(entry
))
138 if __name__
== '__main__':
142 # Die neuesten Pattern-Dateien, welche über die "make"-Ziele
145 # make major pattern-refo
146 # make fugen pattern-refo
147 # make suffix pattern-refo
149 # im Wurzelverzeichnis der wortliste generiert werden
150 # (--help zeigt das Erstelldatum) ::
152 # Pfad zum Wurzelverzeichnis ("../../../") unabhängig vom Arbeitsverzeichnis::
154 root_dir
= os
.path
.relpath(
155 os
.path
.dirname(os
.path
.dirname(os
.path
.dirname(os
.path
.dirname(
156 os
.path
.abspath(__file__
))))) )
159 for cat
in ('', '-major', '-fugen', '-suffix'):
160 ppath
= os
.path
.join(root_dir
, 'dehyphn-x%s/dehyphn-x%s-*.pat'%(cat
,cat
))
161 patterns
[cat
] = sorted(glob
.glob(ppath
))[-1]
163 parser
= argparse
.ArgumentParser(description
= __doc__
,
164 formatter_class
=argparse
.RawDescriptionHelpFormatter
)
165 parser
.add_argument('-p', '--patterns',
166 help='Pattern-Datei (alle Trennstellen), '
167 'Vorgabe "%s"'%patterns
[''], default
=patterns
[''])
168 parser
.add_argument('--patterns_major',
169 help='Pattern-Datei (Trennstellen an Morphemgrenzen), '
170 'Vorgabe "%s"'%patterns
['-major'],
171 default
=patterns
['-major'])
172 parser
.add_argument('--patterns_fugen',
173 help='Pattern-Datei (Trennstellen an Wortfugen), '
174 'Vorgabe "%s"'%patterns
['-fugen'],
175 default
=patterns
['-fugen'])
176 parser
.add_argument('--patterns_suffix',
177 help='Pattern-Datei (Trennstellen vor Suffixen), '
178 'Vorgabe "%s"'%patterns
['-suffix'],
179 default
=patterns
['-suffix'])
180 parser
.add_argument('-k', '--kurzformat', action
='store_true',
181 help='Eingabe ist im Kurzformat')
182 parser
.add_argument('-l', '--language', metavar
='SPRACHE',
183 help='Sprachvariante (Vorgabe: "de-1996").',
185 parser
.add_argument('-w', '--read_probability',
186 help='Wahrscheinlichkeit mit der eine Zeile '
187 'der Eingabe gelesen wird. Vorgabe 1',
188 type=float, default
=1)
189 args
= parser
.parse_args()
193 eintragsklasse
= ShortEntry
195 eintragsklasse
= WordEntry
197 # Trenner-Instanzen::
199 h_all
= Hyphenator(args
.patterns
)
200 h_major
= Hyphenator(args
.patterns_major
)
201 h_fugen
= Hyphenator(args
.patterns_fugen
)
202 h_suffix
= Hyphenator(args
.patterns_suffix
)
205 # Erstellen der neuen Einträge::
207 #proposals = [WordEntry(line.strip().replace('-', ''))
208 proposals
= [eintragsklasse(line
.strip())
209 for line
in sys
.stdin
210 if line
.strip() and not line
.startswith('#')
211 and (random
.random() < args
.read_probability
)]
213 if args
.read_probability
!= 1:
214 print("## Eintragsverarbeitungswahrscheinlichkeit",
215 args
.read_probability
)
221 for proposal
in proposals
:
222 word
= trenne(proposal
.key(lang
=args
.language
))
225 entry
= ShortEntry(word
)
226 entry
.proposal
= proposal
227 entries
.append(entry
)
229 # Vergleich mit Original::
231 gleiche
= [] # Übereinstimmung für Sprachvariante
232 ungewichtete
= [] # Übereinstimmung bis auf Wichtung ("==" vs. "=")
233 unkategorisierte
= [] # Übereinstimmung bis Kategorisierung (=/</>/-/·)
234 andere
= [] # Unterschiede bei der Trennung
235 ungetrennt
= [] # Eingabe war ungetrennt
238 # >>> re.sub('([=<>])+', '\\1', 'Park==ei-sen=bahn')
240 # >>> re.sub('([=<>])+', '\\1', 'All<=heil=mit-tel')
242 # >>> re.sub('([=<>.])+', '-', 'All<=heil=mit-tel')
244 # >>> re.sub('[-=<>]*\\.', '', 'An<.al-.ge-ti-ka')
249 for entry
in entries
:
250 # print(entry.proposal),
251 # print(entry.get('de-1996'),)
252 # print(re.sub('=+', '=', entry.proposal.get('de-1996')))
253 if (not entry
.proposal
254 or (args
.kurzformat
== False and len(entry
.proposal
) == 1)):
255 ungetrennt
.append(entry
)
258 getrennt
= entry
.get(args
.language
) or ''
259 proposal
= entry
.proposal
.get(args
.language
) or ''
260 if getrennt
== proposal
:
261 gleiche
.append(entry
)
264 ungewichtet
= re
.sub('([=<>])[-=<>.]+', '\\1', proposal
) # Verdoppelungen und Unterdrückungspunkt
265 aehnlich
= re
.sub('[=<>]?·', '', ungewichtet
) # Gesangs-Trennungen
266 aehnlich
= re
.sub('[=<>.]', '-', aehnlich
) # Vereinheitlichen
267 # print(proposal, '->', aehnlich)
269 if getrennt
== ungewichtet
:
270 ungewichtete
.append(entry
)
271 elif re
.sub('[=<>]+', '-', getrennt
) == aehnlich
:
272 unkategorisierte
.append(entry
)
279 print('\n## Trennung mit Pattern (Eingabe ohne Trennungen)')
280 for entry
in sorted(ungetrennt
, key
=sortkey_duden
):
281 print_proposal(entry
)
283 print('\n## Vergleich: Eingabe vs. Trennung mit Pattern')
284 print('\n## Unterschiedliche Trennung: Eingabe/# Trennung mit Pattern')
285 for entry
in sorted(andere
, key
=sortkey_duden
):
286 print_proposal(entry
)
288 print('\n## Gleiche Trennung außer Kategorisierung:')
289 for entry
in sorted(unkategorisierte
, key
=sortkey_duden
):
290 print_proposal(entry
)
292 print('\n## Gleiche Trennung außer Wichtung/Unterdrückung:')
293 for entry
in sorted(ungewichtete
, key
=sortkey_duden
):
294 print_proposal(entry
)
296 print('\n## Gleiche Trennung für', args
.language
)
297 for entry
in sorted(gleiche
, key
=sortkey_duden
):
298 print_proposal(entry
)
300 print('\n## Statistik')
301 print('#', len(gleiche
), 'gleich')
302 print('#', len(ungewichtete
), 'gleich (bis auf Wichtung/Unterdrückung)')
303 print('#', len(unkategorisierte
), 'gleich (bis auf Kategorisierung)')
304 print('#', len(andere
), 'anders getrennt')
305 print('#', len(ungetrennt
), 'Vorgabe ohne Trennung')