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 # 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.
25 zusammenfassen: Sprachvarianten zusammenfassen wenn gleich.
28 # Die ``<AKTION>.todo`` Dateien in diesem Verzeichnis beschreiben das
29 # jeweils erforderliche Datenformat im Dateikopf.
33 import optparse
, sys
, os
, codecs
34 from copy
import copy
, deepcopy
37 from wortliste
import WordFile
, WordEntry
, join_word
, udiff
, sortkey_duden
39 def teste_datei(datei
):
40 """Teste, ob Datei geöffnet werden kann."""
43 file = open(datei
, 'r')
46 sys
.stderr
.write("Kann '" + datei
+ "' nicht öffnen\n" )
52 # Sprach-Tag nach [BCP47]_::
54 sprachvariante
= 'de-1901' # "traditionell"
55 # sprachvariante = 'de-1996' # Reformschreibung
56 # sprachvariante = 'de-x-GROSS' # ohne ß (Großbuchstaben und Kapitälchen)
57 # sprachvariante = 'de-1901-x-GROSS' # ohne ß (Schweiz oder GROSS)
58 # sprachvariante = 'de-1996-x-GROSS' # ohne ß (Schweiz oder GROSS)
59 # sprachvariante = 'de-CH-1901' # ohne ß (Schweiz) ("süssauer")
62 # Allgemeine Korrektur (z.B. Fehltrennung)
65 # * ein Wort mit Trennstellen (für "Sprachvariante"):
66 # * vollständiger Eintrag (für Wörter mit Sprachvarianten).
70 def korrektur(wordfile
, datei
):
71 """Patch aus korrigierten Einträgen"""
74 datei
= 'korrektur.todo'
78 for line
in open(datei
, 'r'):
79 if line
.startswith('#'):
81 # Dekodieren, Zeilenende entfernen
82 line
= line
.decode('utf8').strip()
85 # Eintrag ggf. komplettieren
87 line
= u
'%s;%s' % (join_word(line
), line
)
88 entry
= WordEntry(line
)
90 entry
.regelaenderungen() # teste auf Dinge wie s-t/-st
92 korrekturen
[key
] = entry
94 wortliste
= list(wordfile
)
95 wortliste_neu
= [] # korrigierte Liste
97 for entry
in wortliste
:
99 if key
in korrekturen
:
100 entry
= korrekturen
.pop(key
)
101 wortliste_neu
.append(entry
)
104 print korrekturen
# übrige Einträge
106 return (wortliste
, wortliste_neu
)
113 def fehleintraege(wordfile
, datei
):
114 """Entfernen der Einträge aus einer Liste von Fehleinträgen """
116 # Fehleinträge aus Datei.
119 # Ein Eintrag/Zeile, mit oder ohne Trennzeichen
124 datei
= 'fehleintraege.todo'
127 # Dekodieren, Zeilenende entfernen, Trennzeichen entfernen
128 korrekturen
= set(join_word(
129 line
.decode('utf8').strip().replace(u
';', u
' ').split()[0])
130 for line
in open(datei
, 'r')
131 if line
.strip() and not line
.startswith('#'))
132 wortliste
= list(wordfile
)
133 wortliste_neu
= [] # korrigierte Liste
134 for entry
in wortliste
:
135 if entry
[0] in korrekturen
: # nicht kopieren
136 korrekturen
.discard(entry
[0]) # erledigt
138 wortliste_neu
.append(entry
)
141 print 'nicht gefunden:'
142 for w
in korrekturen
:
143 print w
.encode('utf8')
145 return (wortliste
, wortliste_neu
)
148 # Groß-/Kleinschreibung ändern
149 # ----------------------------
151 # Umstellen der Groß- oder Kleinschreibung auf die Variante in der Datei
152 # ``grossklein.todo``
155 # ein Eintrag oder Wort pro Zeile, mit vorhandener Groß-/Kleinschreibung.
159 def grossklein(wordfile
, datei
):
160 """Groß-/Kleinschreibung umstellen"""
163 datei
= 'grossklein.todo'
166 wortliste
= list(wordfile
)
168 # Dekodieren, Feldtrenner zu Leerzeichen
169 korrekturen
= [line
.decode('utf8').replace(';',' ')
170 for line
in open(datei
, 'r')
171 if not line
.startswith('#')]
173 # erstes Feld, Trennzeichen entfernen
174 korrekturen
= [join_word(line
.split()[0]) for line
in korrekturen
175 if line
.strip() and not line
.startswith('#')]
176 korrekturen
= set(korrekturen
)
177 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
179 for entry
in wortliste_neu
:
180 if entry
[0] in korrekturen
:
181 korrekturen
.discard(entry
[0]) # gefunden
182 # Anfangsbuchstabe mit geänderter Großschreibung:
183 if entry
[0][0].islower():
184 anfangsbuchstabe
= entry
[0][0].title()
186 anfangsbuchstabe
= entry
[0][0].lower()
187 # Einträge korrigieren:
188 for i
in range(len(entry
)):
189 if entry
[i
].startswith('-'): # -2-, -3-, ...
191 entry
[i
] = anfangsbuchstabe
+ entry
[i
][1:]
194 print korrekturen
# übrige Einträge
196 return (wortliste
, wortliste_neu
)
198 # Anpassung der Großschreibung der Trennmuster an das erste Feld
199 # (ungetrenntes Wort). Siehe "wortliste.py" für einen Test auf Differenzen.
200 # (Differenzen sind größtenteils auf unvorsichtiges Ersetzen mit Texteditor
204 def grossabgleich(wordfile
):
205 wortliste
= list(wordfile
)
206 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
207 for entry
in wortliste_neu
:
208 # Übertrag des Anfangsbuchstabens
209 for i
in range(1,len(entry
)):
210 if entry
[i
].startswith('-'):
212 entry
[i
] = entry
[0][0] + entry
[i
][1:]
213 return (wortliste
, wortliste_neu
)
216 # Sprachvariante ändern
217 # ---------------------
219 # Einträge mit allgemeingültiger (oder sonstwie mehrfacher) Sprachvariante
220 # in "nur in Reformschreibung" (allgemein) ändern.
223 # ein Wort/(Alt-)Eintrag pro Zeile.
227 def reformschreibung(wordfile
, datei
):
228 """Wörter die nur in (allgemeiner) Reformschreibung existieren"""
231 datei
='reformschreibung.todo'
233 # Dekodieren, Zeilenende entfernen
234 korrekturen
= [line
.decode('utf8').strip()
235 for line
in open(datei
, 'r')
236 if not line
.startswith('#')
239 korrekturen
= [line
.split(';')[0] for line
in korrekturen
]
240 korrekturen
= set(korrekturen
)
242 wortliste
= list(wordfile
)
243 wortliste_neu
= [] # korrigierte Liste
245 for entry
in wortliste
:
246 if entry
[0] in korrekturen
:
248 wort
= entry
.get('de-1996')
249 entry
= WordEntry('%s;-2-;-3-;%s;%s' % (key
, wort
, wort
))
250 korrekturen
.discard(key
) # erledigt
251 wortliste_neu
.append(entry
)
254 print korrekturen
# übrige Einträge
255 return wortliste
, wortliste_neu
258 # Getrennte Einträge für Sprachvarianten
259 # --------------------------------------
261 # Korrigiere fehlende Spezifizierung nach Sprachvarianten, z.B.
264 # + System;-2-;Sy-stem;Sys-tem
268 def sprachvariante_split(wordfile
, alt
, neu
,
269 altsprache
='de-1901', neusprache
='de-1996'):
271 wortliste
= list(wordfile
)
272 wortliste_neu
= [] # korrigierte Liste
274 for entry
in wortliste
:
275 if len(entry
) == 2: # Allgemeine Schreibung
276 altwort
= entry
.get(altsprache
)
277 neuwort
= altwort
.replace(alt
, neu
)
278 if altwort
!= neuwort
:
279 entry
= WordEntry('%s;-2-;3;4' % (join_word(altwort
)))
280 entry
.set(altwort
, altsprache
)
281 entry
.set(neuwort
, neusprache
)
282 wortliste_neu
.append(entry
)
283 return (wortliste
, wortliste_neu
)
287 # Neueinträge prüfen und vorbereiten
288 # ----------------------------------
290 # Die in einer Datei (ein Neueintrag pro Zeile) gesammelten Vorschläge auf
291 # auf Neuwert testen (vorhandene Wörter werden ignoriert, unabhängig von der
292 # Groß-/Kleinschreibung) und einsortieren.
294 # Akzeptierte Formate:
296 # * vollständiger Eintrag (für Wörter mit Sprachvarianten oder Kommentaren).
298 # * ein Wort mit Trennstellen:
300 # - Schlüssel wird generiert und vorangestellt (durch Semikolon getrennt).
301 # - Test auf einfache/häufige Reformänderungen 1996:
302 # s-t/-st, {ck/k-k}/c-k
306 def neu(wordfile
, datei
):
307 """Neueinträge prüfen und vorbereiten."""
312 korrekturen
= open(datei
, 'r')
314 wortliste
= list(wordfile
)
315 wortliste_neu
= copy(wortliste
)
316 words
= set(entry
[0] for entry
in wortliste
) # vorhandene Wörter
318 for line
in korrekturen
:
319 if line
.startswith('#'):
321 # Dekodieren, Zeilenende entfernen
322 line
= line
.decode('utf8').strip()
325 # Eintrag ggf. komplettieren:
327 line
= u
'%s;%s' % (join_word(line
), line
)
328 entry
= WordEntry(line
)
330 # Test auf "Neuwert":
332 print key
.encode('utf8'), 'schon vorhanden'
334 if key
.lower() in words
or key
.title() in words
:
335 print key
.encode('utf8'), 'mit anderer Großschreibung vorhanden'
338 entry
.regelaenderungen() # teste auf Dinge wie s-t/-st
339 wortliste_neu
.append(entry
)
343 wortliste_neu
.sort(key
=sortkey_duden
)
345 return (wortliste
, wortliste_neu
)
348 def doppelte(wordfile
, use_first
=False):
349 """Doppeleinträge entfernen (ohne Berücksichtigung der Großschreibung).
351 Boolscher Wert `use_first` bestimmt, ob der erste oder der letzte von
352 Einträgen mit gleichem Schlüssel in der Liste verbleibt.
354 Die neue Liste ist sortiert.
356 wortliste
= list(wordfile
)
358 for entry
in wortliste
:
359 key
= entry
[0].lower()
360 if use_first
and key
in worddict
:
362 worddict
[key
] = entry
364 wortliste_neu
= worddict
.values() # korrigierte Liste
365 wortliste_neu
.sort(key
=sortkey_duden
)
367 print len(wortliste
) - len(wortliste_neu
), "Einträge entfernt"
368 return (wortliste
, wortliste_neu
)
371 def conflate(wortliste
):
373 wortliste
= list(wordfile
)
374 wortliste_neu
= [] # korrigierte Liste
376 for entry
in wortliste
:
378 # Felder zusammenfassen:
380 entry
.conflate_fields()
381 wortliste_neu
.append(entry
)
383 return (wortliste
, wortliste_neu
)
388 if __name__
== '__main__':
392 usage
= '%prog [Optionen] AKTION\n' + __doc__
394 parser
= optparse
.OptionParser(usage
=usage
)
395 parser
.add_option('-i', '--file', dest
='wortliste',
396 help='Eingangsdatei, Vorgabe "../../../wortliste"',
397 default
='../../../wortliste')
398 parser
.add_option('-k', '--todofile', dest
='todo',
399 help='Korrekturdatei, Vorgabe "<AKTION>.todo"')
400 parser
.add_option('-o', '--outfile', dest
='patchfile',
401 help='Ausgangsdatei (Patch), Vorgabe "wortliste.patch"',
402 default
='wortliste.patch')
404 (options
, args
) = parser
.parse_args()
409 print 'Nichts zu tun: AKTION Argument fehlt.', '\n'
414 wordfile
= WordFile(options
.wortliste
)
416 # Da der Aufruf von `wortliste = list(wordfile)` lange dauert, wird er
417 # in den Aktionsroutinen nach dem Test auf die Eingabedatei ausgeführt.
422 (wortliste
, wortliste_neu
) = neu(wordfile
, options
.todo
)
423 elif aktion
== 'doppelte':
424 (wortliste
, wortliste_neu
) = doppelte(wordfile
)
425 elif aktion
== 'fehleintraege':
426 (wortliste
, wortliste_neu
) = fehleintraege(wordfile
, options
.todo
)
427 elif aktion
== 'grossklein':
428 (wortliste
, wortliste_neu
) = grossklein(wordfile
, options
.todo
)
429 elif aktion
== 'grossabgleich':
430 (wortliste
, wortliste_neu
) = grossabgleich(wordfile
)
431 elif aktion
== 'korrektur':
432 (wortliste
, wortliste_neu
) = korrektur(wordfile
, options
.todo
)
433 elif aktion
== 'reformschreibung':
434 (wortliste
, wortliste_neu
) = reformschreibung(wordfile
, options
.todo
)
435 elif aktion
== 'zusammenfassen':
436 (wortliste
, wortliste_neu
) = conflate(wordfile
)
438 print 'Unbekannte AKTION', '\n'
442 # (wortliste, wortliste_neu) = sprachvariante_split(wordfile,
443 # u'knien', u'kni-en')
447 patch
= udiff(wortliste
, wortliste_neu
, 'wortliste', 'wortliste-neu',
448 encoding
=wordfile
.encoding
)
451 patchfile
= open(options
.patchfile
, 'w')
452 patchfile
.write(patch
+ '\n')
453 print 'Änderungen nach %s geschrieben' % options
.patchfile
455 print 'keine Änderungen'