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
, codecs
33 from copy
import copy
, deepcopy
36 from werkzeug
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 key
= line
.split(';')[0]
90 korrekturen
[key
] = line
92 wortliste
= list(wordfile
)
93 wortliste_neu
= [] # korrigierte Liste
95 for entry
in wortliste
:
97 if key
in korrekturen
:
98 korrektur
= korrekturen
.pop(key
)
100 entry
= WordEntry(korrektur
)
103 entry
.set(korrektur
, sprachvariante
)
105 wortliste_neu
.append(entry
)
108 print korrekturen
# übrige Einträge
110 return (wortliste
, wortliste_neu
)
117 def fehleintraege(wordfile
, datei
):
118 """Entfernen der Einträge aus einer Liste von Fehleinträgen """
120 # Fehleinträge aus Datei.
123 # Ein Eintrag/Zeile, mit oder ohne Trennzeichen
128 datei
= 'fehleintraege.todo'
131 # Dekodieren, Zeilenende entfernen, Trennzeichen entfernen
132 korrekturen
= set(join_word(
133 line
.decode('utf8').strip().replace(u
';', u
' ').split()[0])
134 for line
in open(datei
, 'r')
135 if line
.strip() and not line
.startswith('#'))
136 wortliste
= list(wordfile
)
137 wortliste_neu
= [] # korrigierte Liste
138 for entry
in wortliste
:
139 if entry
[0] in korrekturen
: # nicht kopieren
140 korrekturen
.discard(entry
[0]) # erledigt
142 wortliste_neu
.append(entry
)
145 print 'nicht gefunden:'
146 for w
in korrekturen
:
147 print w
.encode('utf8')
149 return (wortliste
, wortliste_neu
)
152 # Groß-/Kleinschreibung ändern
153 # ----------------------------
155 # Umstellen der Groß- oder Kleinschreibung auf die Variante in der Datei
156 # ``grossklein.todo``
159 # ein Eintrag oder Wort pro Zeile, mit vorhandener Groß-/Kleinschreibung.
163 def grossklein(wordfile
, datei
):
164 """Groß-/Kleinschreibung umstellen"""
167 datei
= 'grossklein.todo'
170 wortliste
= list(wordfile
)
172 # Dekodieren, Feldtrenner zu Leerzeichen
173 korrekturen
= [line
.decode('utf8').replace(';',' ')
174 for line
in open(datei
, 'r')
175 if not line
.startswith('#')]
177 # erstes Feld, Trennzeichen entfernen
178 korrekturen
= [join_word(line
.split()[0]) for line
in korrekturen
179 if line
.strip() and not line
.startswith('#')]
180 korrekturen
= set(korrekturen
)
181 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
183 for entry
in wortliste_neu
:
184 if entry
[0] in korrekturen
:
185 korrekturen
.discard(entry
[0]) # gefunden
186 # Anfangsbuchstabe mit geänderter Großschreibung:
187 if entry
[0][0].islower():
188 anfangsbuchstabe
= entry
[0][0].title()
190 anfangsbuchstabe
= entry
[0][0].lower()
191 # Einträge korrigieren:
192 for i
in range(len(entry
)):
193 if entry
[i
].startswith('-'): # -2-, -3-, ...
195 entry
[i
] = anfangsbuchstabe
+ entry
[i
][1:]
198 print korrekturen
# übrige Einträge
200 return (wortliste
, wortliste_neu
)
202 # Anpassung der Großschreibung der Trennmuster an das erste Feld
203 # (ungetrenntes Wort). Siehe "werkzeug.py" für einen Test auf Differenzen.
204 # (Differenzen sind größtenteils auf unvorsichtiges Ersetzen mit Texteditor
208 def grossabgleich(wordfile
):
209 wortliste
= list(wordfile
)
210 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
211 for entry
in wortliste_neu
:
212 # Übertrag des Anfangsbuchstabens
213 for i
in range(1,len(entry
)):
214 if entry
[i
].startswith('-'):
216 entry
[i
] = entry
[0][0] + entry
[i
][1:]
217 return (wortliste
, wortliste_neu
)
220 # Sprachvariante ändern
221 # ---------------------
223 # Einträge mit allgemeingültiger (oder sonstwie mehrfacher) Sprachvariante
224 # in "nur in Reformschreibung" (allgemein) ändern.
227 # ein Wort/(Alt-)Eintrag pro Zeile.
231 def reformschreibung(wordfile
, datei
):
232 """Wörter die nur in (allgemeiner) Reformschreibung existieren"""
235 datei
='reformschreibung.todo'
237 # Dekodieren, Zeilenende entfernen
238 korrekturen
= [line
.decode('utf8').strip()
239 for line
in open(datei
, 'r')
240 if not line
.startswith('#')
243 korrekturen
= [line
.split(';')[0] for line
in korrekturen
]
244 korrekturen
= set(korrekturen
)
246 wortliste
= list(wordfile
)
247 wortliste_neu
= [] # korrigierte Liste
249 for entry
in wortliste
:
250 if entry
[0] in korrekturen
:
252 wort
= entry
.get('de-1996')
253 entry
= WordEntry('%s;-2-;-3-;%s;%s' % (key
, wort
, wort
))
254 korrekturen
.discard(key
) # erledigt
255 wortliste_neu
.append(entry
)
258 print korrekturen
# übrige Einträge
259 return wortliste
, wortliste_neu
262 # Getrennte Einträge für Sprachvarianten
263 # --------------------------------------
265 # Korrigiere fehlende Spezifizierung nach Sprachvarianten, z.B.
268 # + System;-2-;Sy-stem;Sys-tem
272 def sprachvariante_split(wordfile
, alt
, neu
,
273 altsprache
='de-1901', neusprache
='de-1996'):
275 wortliste
= list(wordfile
)
276 wortliste_neu
= [] # korrigierte Liste
278 for entry
in wortliste
:
279 if len(entry
) == 2: # Allgemeine Schreibung
280 altwort
= entry
.get(altsprache
)
281 neuwort
= altwort
.replace(alt
, neu
)
282 if altwort
!= neuwort
:
283 entry
= WordEntry('%s;-2-;3;4' % (join_word(altwort
)))
284 entry
.set(altwort
, altsprache
)
285 entry
.set(neuwort
, neusprache
)
286 wortliste_neu
.append(entry
)
287 return (wortliste
, wortliste_neu
)
291 # Neueinträge prüfen und vorbereiten
292 # ----------------------------------
294 # Die in einer Datei (ein Neueintrag pro Zeile) gesammelten Vorschläge auf
295 # auf Neuwert testen (vorhandene Wörter werden ignoriert, unabhängig von der
296 # Groß-/Kleinschreibung) und einsortieren.
298 # Akzeptierte Formate:
300 # * vollständiger Eintrag (für Wörter mit Sprachvarianten oder Kommentaren).
302 # * ein Wort mit Trennstellen:
304 # - Schlüssel wird generiert und vorangestellt (durch Semikolon getrennt).
305 # - Test auf einfache/häufige Reformänderungen 1996:
306 # s-t/-st, {ck/k-k}/c-k
310 def neu(wordfile
, datei
):
311 """Neueinträge prüfen und vorbereiten."""
316 korrekturen
= open(datei
, 'r')
318 wortliste
= list(wordfile
)
319 wortliste_neu
= copy(wortliste
)
320 words
= set(entry
[0] for entry
in wortliste
) # vorhandene Wörter
323 r1901
= (u
'-st', u
'{ck/k-k}')
324 r1996
= (u
's-t', u
'-ck')
326 for line
in korrekturen
:
327 if line
.startswith('#'):
329 # Dekodieren, Zeilenende entfernen
330 line
= line
.decode('utf8').strip()
333 # Eintrag ggf. komplettieren:
335 key
= line
.split(u
';')[0]
337 key
= join_word(line
)
338 for r1
, r2
in zip(r1901
, r1996
):
340 line
= u
'%s;-2-;%s;%s' % (key
, line
, line
.replace(r1
,r2
))
342 line
= u
'%s;-2-;%s;%s' % (key
, line
.replace(r2
,r1
), line
)
343 # 'ßt' und Schluß-ß auch in de-1996 möglich (langer Vokal)
344 if u
'sst' in line
or line
.endswith(u
'ss'):
345 line
= u
'%s;-2-;%s;' % (key
, line
)
346 if u
';' not in line
: # keine Regeländerung im Wort
347 line
= u
'%s;%s' % (key
, line
)
348 # Test auf "Neuwert":
350 print key
, 'schon vorhanden'
352 if key
.lower() in words
or key
.title() in words
:
353 print key
, 'mit anderer Groß-/Kleinschreibung vorhanden'
355 wortliste_neu
.append(WordEntry(line
))
359 wortliste_neu
.sort(key
=sortkey_duden
)
361 return (wortliste
, wortliste_neu
)
364 def doppelte(wordfile
, use_first
=False):
365 """Doppeleinträge entfernen (ohne Berücksichtigung der Großschreibung).
367 Boolscher Wert `use_first` bestimmt, ob der erste oder der letzte von
368 Einträgen mit gleichem Schlüssel in der Liste verbleibt.
370 Die neue Liste ist sortiert.
372 wortliste
= list(wordfile
)
374 for entry
in wortliste
:
375 key
= entry
[0].lower()
376 if use_first
and key
in worddict
:
378 worddict
[key
] = entry
380 wortliste_neu
= worddict
.values() # korrigierte Liste
381 wortliste_neu
.sort(key
=sortkey_duden
)
383 print len(wortliste
) - len(wortliste_neu
), u
"Einträge entfernt"
384 return (wortliste
, wortliste_neu
)
388 if __name__
== '__main__':
390 # sys.stdout mit UTF8 encoding.
391 sys
.stdout
= codecs
.getwriter('UTF-8')(sys
.stdout
)
395 usage
= '%prog [Optionen] AKTION\n' + __doc__
397 parser
= optparse
.OptionParser(usage
=usage
)
398 parser
.add_option('-i', '--file', dest
='wortliste',
399 help='Eingangsdatei, Vorgabe "../../wortliste"',
400 default
='../../wortliste')
401 parser
.add_option('-k', '--todofile', dest
='todo',
402 help='Korrekturdatei, Vorgabe "<AKTION>.todo"')
403 parser
.add_option('-o', '--outfile', dest
='patchfile',
404 help='Ausgangsdatei (Patch), Vorgabe "wortliste.patch"',
405 default
='wortliste.patch')
407 (options
, args
) = parser
.parse_args()
412 print 'Nichts zu tun: AKTION Argument fehlt.', '\n'
417 wordfile
= WordFile(options
.wortliste
)
419 # Da der Aufruf von `wortliste = list(wordfile)` lange dauert, wird er
420 # in den Aktionsroutinen nach dem Test auf die Eingabedatei ausgeführt.
425 (wortliste
, wortliste_neu
) = neu(wordfile
, options
.todo
)
426 elif aktion
== 'doppelte':
427 (wortliste
, wortliste_neu
) = doppelte(wordfile
)
428 elif aktion
== 'fehleintraege':
429 (wortliste
, wortliste_neu
) = fehleintraege(wordfile
, options
.todo
)
430 elif aktion
== 'grossklein':
431 (wortliste
, wortliste_neu
) = grossklein(wordfile
, options
.todo
)
432 elif aktion
== 'grossabgleich':
433 (wortliste
, wortliste_neu
) = grossabgleich(wordfile
)
434 elif aktion
== 'korrektur':
435 (wortliste
, wortliste_neu
) = korrektur(wordfile
, options
.todo
)
436 elif aktion
== 'reformschreibung':
437 (wortliste
, wortliste_neu
) = reformschreibung(wordfile
, options
.todo
)
439 print 'Unbekannte AKTION', '\n'
443 # (wortliste, wortliste_neu) = sprachvariante_split(wordfile,
444 # u'knien', u'kni-en')
448 patch
= udiff(wortliste
, wortliste_neu
, 'wortliste', 'wortliste-neu',
449 encoding
=wordfile
.encoding
)
452 patchfile
= open(options
.patchfile
, 'w')
453 patchfile
.write(patch
+ '\n')
454 print u
'Änderungen nach %s geschrieben' % options
.patchfile
456 print u
'keine Änderungen'