3 # :Copyright: © 2011 Günter Milde.
4 # Released without warranty under the terms of the
5 # GNU General Public License (v. 2 or later)
8 # prepare_patch.py: Helfer für kleine Editieraufgaben
9 # ===================================================
13 Erstelle einen Patch für kleinere Korrekturen der Wortliste.
14 Ausgangspunkt sind Dateien mit einer Korrektur pro Zeile.
15 (Zeilen, die mit ``#`` starten, werden ignoriert.)
18 doppelte: Einträge mit gleichem Schlüssel entfernen,
19 fehleintraege: Einträge entfernen,
20 grossklein: Großschreibung ändern,
21 grossabgleich: Großschreibung der Trennmuster wie erstes Feld,
22 korrektur: Einträge durch alternative Version ersetzen.
23 neu: Einträge hinzufügen,
24 reformschreibung: Eintrag in "nur Reformschreibung" ändern.
27 # Die ``<AKTION>.todo`` Dateien in diesem Verzeichnis beschreiben das
28 # jeweils erforderliche Datenformat im Dateikopf.
32 import optparse
, sys
, os
33 from copy
import copy
, deepcopy
36 from werkzeug
import WordFile
, WordEntry
, join_word
, udiff
37 # sort.py im Überverzeichnis:
38 sys
.path
.append(os
.path
.join(os
.path
.dirname(__file__
), '..'))
39 from sort
import sortkey_wl
, sortkey_duden
42 def teste_datei(datei
):
43 """Teste, ob Datei geöffnet werden kann."""
46 file = open(datei
, 'r')
49 sys
.stderr
.write("Kann '" + datei
+ "' nicht öffnen\n" )
55 # Sprach-Tag nach [BCP47]_::
57 sprachvariante
= 'de-1901' # "traditionell"
58 # sprachvariante = 'de-1996' # Reformschreibung
59 # sprachvariante = 'de-x-GROSS' # ohne ß (Großbuchstaben und Kapitälchen)
60 # sprachvariante = 'de-1901-x-GROSS' # ohne ß (Schweiz oder GROSS)
61 # sprachvariante = 'de-1996-x-GROSS' # ohne ß (Schweiz oder GROSS)
62 # sprachvariante = 'de-CH-1901' # ohne ß (Schweiz) ("süssauer")
65 # Allgemeine Korrektur (z.B. Fehltrennung)
68 # * ein Wort mit Trennstellen (für "Sprachvariante"):
69 # * vollständiger Eintrag (für Wörter mit Sprachvarianten).
73 def korrektur(wordfile
, datei
):
74 """Patch aus korrigierten Einträgen"""
77 datei
= 'korrektur.todo'
81 for line
in open(datei
, 'r'):
82 if line
.startswith('#'):
84 # Dekodieren, Zeilenende entfernen
85 line
= line
.decode('utf8').strip()
88 # Eintrag ggf. komplettieren
90 key
= line
.split(';')[0]
93 korrekturen
[key
] = line
95 wortliste
= list(wordfile
)
96 wortliste_neu
= [] # korrigierte Liste
98 for entry
in wortliste
:
100 if key
in korrekturen
:
101 korrektur
= korrekturen
.pop(key
)
102 if u
';' in korrektur
:
103 entry
= WordEntry(korrektur
)
106 entry
.set(korrektur
, sprachvariante
)
108 wortliste_neu
.append(entry
)
111 print korrekturen
# übrige Einträge
113 return (wortliste
, wortliste_neu
)
120 def fehleintraege(wordfile
, datei
):
121 """Entfernen der Einträge aus einer Liste von Fehleinträgen """
123 # Fehleinträge aus Datei.
126 # Ein Eintrag/Zeile, mit oder ohne Trennzeichen
131 datei
= 'fehleintraege.todo'
134 # Dekodieren, Zeilenende entfernen, Trennzeichen entfernen
135 korrekturen
= set(join_word(
136 line
.decode('utf8').strip().replace(u
';', u
' ').split()[0])
137 for line
in open(datei
, 'r')
138 if line
.strip() and not line
.startswith('#'))
139 wortliste
= list(wordfile
)
140 wortliste_neu
= [] # korrigierte Liste
141 for entry
in wortliste
:
142 if entry
[0] in korrekturen
: # nicht kopieren
143 korrekturen
.discard(entry
[0]) # erledigt
145 wortliste_neu
.append(entry
)
148 print 'nicht gefunden:'
149 for w
in korrekturen
:
150 print w
.encode('utf8')
152 return (wortliste
, wortliste_neu
)
155 # Groß-/Kleinschreibung ändern
156 # ----------------------------
158 # Umstellen der Groß- oder Kleinschreibung auf die Variante in der Datei
159 # ``grossklein.todo``
162 # ein Eintrag oder Wort pro Zeile, mit vorhandener Groß-/Kleinschreibung.
166 def grossklein(wordfile
, datei
):
167 """Groß-/Kleinschreibung umstellen"""
170 datei
= 'grossklein.todo'
173 wortliste
= list(wordfile
)
175 # Dekodieren, Feldtrenner zu Leerzeichen
176 korrekturen
= [line
.decode('utf8').replace(';',' ')
177 for line
in open(datei
, 'r')
178 if not line
.startswith('#')]
180 # erstes Feld, Trennzeichen entfernen
181 korrekturen
= [join_word(line
.split()[0]) for line
in korrekturen
182 if line
.strip() and not line
.startswith('#')]
183 korrekturen
= set(korrekturen
)
184 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
186 for entry
in wortliste_neu
:
187 if entry
[0] in korrekturen
:
188 korrekturen
.discard(entry
[0]) # gefunden
189 # Anfangsbuchstabe mit geänderter Großschreibung:
190 if entry
[0][0].islower():
191 anfangsbuchstabe
= entry
[0][0].title()
193 anfangsbuchstabe
= entry
[0][0].lower()
194 # Einträge korrigieren:
195 for i
in range(len(entry
)):
196 if entry
[i
].startswith('-'): # -2-, -3-, ...
198 entry
[i
] = anfangsbuchstabe
+ entry
[i
][1:]
201 print korrekturen
# übrige Einträge
203 return (wortliste
, wortliste_neu
)
205 # Anpassung der Großschreibung der Trennmuster an das erste Feld
206 # (ungetrenntes Wort). Siehe "werkzeug.py" für einen Test auf Differenzen.
207 # (Differenzen sind größtenteils auf unvorsichtiges Ersetzen mit Texteditor
211 def grossabgleich(wordfile
):
212 wortliste
= list(wordfile
)
213 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
214 for entry
in wortliste_neu
:
215 # Übertrag des Anfangsbuchstabens
216 for i
in range(1,len(entry
)):
217 if entry
[i
].startswith('-'):
219 entry
[i
] = entry
[0][0] + entry
[i
][1:]
220 return (wortliste
, wortliste_neu
)
223 # Sprachvariante ändern
224 # ---------------------
226 # Einträge mit allgemeingültiger (oder sonstwie mehrfacher) Sprachvariante
227 # in "nur in Reformschreibung" (allgemein) ändern.
230 # ein Wort/(Alt-)Eintrag pro Zeile.
234 def reformschreibung(wordfile
, datei
):
235 """Wörter die nur in (allgemeiner) Reformschreibung existieren"""
238 datei
='reformschreibung.todo'
240 # Dekodieren, Zeilenende entfernen
241 korrekturen
= [line
.decode('utf8').strip()
242 for line
in open(datei
, 'r')
243 if not line
.startswith('#')
246 korrekturen
= [line
.split(';')[0] for line
in korrekturen
]
247 korrekturen
= set(korrekturen
)
249 wortliste
= list(wordfile
)
250 wortliste_neu
= [] # korrigierte Liste
252 for entry
in wortliste
:
253 if entry
[0] in korrekturen
:
255 wort
= entry
.get('de-1996')
256 entry
= WordEntry('%s;-2-;-3-;%s;%s' % (key
, wort
, wort
))
257 korrekturen
.discard(key
) # erledigt
258 wortliste_neu
.append(entry
)
261 print korrekturen
# übrige Einträge
262 return wortliste
, wortliste_neu
265 # Getrennte Einträge für Sprachvarianten
266 # --------------------------------------
268 # Korrigiere fehlende Spezifizierung nach Sprachvarianten, z.B.
271 # + System;-2-;Sy-stem;Sys-tem
275 def sprachvariante_split(wordfile
, alt
, neu
,
276 altsprache
='de-1901', neusprache
='de-1996'):
278 wortliste
= list(wordfile
)
279 wortliste_neu
= [] # korrigierte Liste
281 for entry
in wortliste
:
282 if len(entry
) == 2: # Allgemeine Schreibung
283 altwort
= entry
.get(altsprache
)
284 neuwort
= altwort
.replace(alt
, neu
)
285 if altwort
!= neuwort
:
286 entry
= WordEntry('%s;-2-;3;4' % (join_word(altwort
)))
287 entry
.set(altwort
, altsprache
)
288 entry
.set(neuwort
, neusprache
)
289 wortliste_neu
.append(entry
)
290 return (wortliste
, wortliste_neu
)
294 # Neueinträge prüfen und vorbereiten
295 # ----------------------------------
297 # Die in einer Datei (ein Neueintrag pro Zeile) gesammelten Vorschläge auf
298 # auf Neuwert testen (vorhandene Wörter werden ignoriert, unabhängig von der
299 # Groß-/Kleinschreibung) und einsortieren.
301 # Akzeptierte Formate:
303 # * vollständiger Eintrag (für Wörter mit Sprachvarianten oder Kommentaren).
305 # * ein Wort mit Trennstellen:
307 # - Schlüssel wird generiert und vorangestellt (durch Semikolon getrennt).
308 # - Test auf einfache/häufige Reformänderungen 1996:
309 # s-t/-st, {ck/k-k}/c-k
313 def neu(wordfile
, datei
):
314 """Neueinträge prüfen und vorbereiten."""
319 korrekturen
= open(datei
, 'r')
321 wortliste
= list(wordfile
)
322 wortliste_neu
= deepcopy(wortliste
)
323 words
= set(entry
[0] for entry
in wortliste
) # vorhandene Wörter
326 r1901
= (u
'-st', u
'{ck/k-k}')
327 r1996
= (u
's-t', u
'-ck')
329 for line
in korrekturen
:
330 if line
.startswith('#'):
332 # Dekodieren, Zeilenende entfernen
333 line
= line
.decode('utf8').strip()
336 # Eintrag ggf. komplettieren:
338 key
= u
';'.split(line
)[0]
340 key
= join_word(line
)
341 for r1
, r2
in zip(r1901
, r1996
):
343 line
= u
'%s;-2-;%s;%s' % (key
, line
, line
.replace(r1
,r2
))
345 line
= u
'%s;-2-;%s;%s' % (key
, line
.replace(r2
,r1
), line
)
346 # 'ßt' und Schluß-ß auch in de-1996 möglich (langer Vokal)
347 if u
'sst' in line
or line
.endswith(u
'ss'):
348 line
= u
'%s;-2-;%s;' % (key
, line
)
349 if u
';' not in line
: # keine Regeländerung im Wort
350 line
= u
'%s;%s' % (key
, line
)
351 # Test auf "Neuwert":
353 print key
.encode('utf8'), 'schon vorhanden'
355 if key
.lower() in words
or key
.title() in words
:
356 print (key
.encode('utf8'),
357 'mit anderer Groß-/Kleinschreibung vorhanden')
359 wortliste_neu
.append(WordEntry(line
))
362 wortliste_neu
.sort(key
=sortkey_duden
)
364 return (wortliste
, wortliste_neu
)
367 def doppelte(wordfile
, use_first
=False):
368 """Doppeleinträge entfernen (ohne Berücksichtigung der Großschreibung).
370 Boolscher Wert `use_first` bestimmt, ob der erste oder der letzte von
371 Einträgen mit gleichem Schlüssel in der Liste verbleibt.
373 Die neue Liste ist sortiert.
375 wortliste
= list(wordfile
)
377 for entry
in wortliste
:
378 key
= entry
[0].lower()
379 if use_first
and key
in worddict
:
381 worddict
[key
] = entry
383 wortliste_neu
= worddict
.values() # korrigierte Liste
384 # wortliste_neu.sort(key=sortkey_wl)
385 wortliste_neu
.sort(key
=sortkey_duden
)
387 print len(wortliste
) - len(wortliste_neu
), u
"Einträge entfernt"
388 return (wortliste
, wortliste_neu
)
392 if __name__
== '__main__':
396 usage
= '%prog [Optionen] AKTION\n' + __doc__
398 parser
= optparse
.OptionParser(usage
=usage
)
399 parser
.add_option('-i', '--file', dest
='wortliste',
400 help='Eingangsdatei, Vorgabe "../../wortliste"',
401 default
='../../wortliste')
402 parser
.add_option('-k', '--todofile', dest
='todo',
403 help='Korrekturdatei, Vorgabe "<AKTION>.todo"')
404 parser
.add_option('-o', '--outfile', dest
='patchfile',
405 help='Ausgangsdatei (Patch), Vorgabe "wortliste.patch"',
406 default
='wortliste.patch')
408 (options
, args
) = parser
.parse_args()
413 print 'Nichts zu tun: AKTION Argument fehlt.', '\n'
418 wordfile
= WordFile(options
.wortliste
)
420 # Da der Aufruf von `wortliste = list(wordfile)` lange dauert, wird er
421 # in den Aktionsroutinen nach dem Test auf die Eingabedatei ausgeführt.
426 (wortliste
, wortliste_neu
) = neu(wordfile
, options
.todo
)
427 elif aktion
== 'doppelte':
428 (wortliste
, wortliste_neu
) = doppelte(wordfile
)
429 elif aktion
== 'fehleintraege':
430 (wortliste
, wortliste_neu
) = fehleintraege(wordfile
, options
.todo
)
431 elif aktion
== 'grossklein':
432 (wortliste
, wortliste_neu
) = grossklein(wordfile
, options
.todo
)
433 elif aktion
== 'grossabgleich':
434 (wortliste
, wortliste_neu
) = grossabgleich(wordfile
)
435 elif aktion
== 'korrektur':
436 (wortliste
, wortliste_neu
) = korrektur(wordfile
, options
.todo
)
437 elif aktion
== 'reformschreibung':
438 (wortliste
, wortliste_neu
) = reformschreibung(wordfile
, options
.todo
)
440 print 'Unbekannte AKTION', '\n'
444 # (wortliste, wortliste_neu) = sprachvariante_split(wordfile,
445 # u'knien', u'kni-en')
449 patch
= udiff(wortliste
, wortliste_neu
, 'wortliste', 'wortliste-neu',
450 encoding
=wordfile
.encoding
)
453 patchfile
= open(options
.patchfile
, 'w')
454 patchfile
.write(patch
+ '\n')
455 print u
'Änderungen nach %s geschrieben' % options
.patchfile
457 print u
'keine Änderungen'