[Trennmuster] Entscheidungsmuster für Binnen- und Schluss-S in Frakturschriften
Guenter Milde
milde at users.sf.net
Mi Feb 1 23:27:49 CET 2012
Hallo Stephan,
On 30.01.12, Stephan Hennig wrote:
> Am 26.01.2012 00:04, schrieb Guenter Milde:
> > * Erstellen eines freien Wörterbuchs für Rechtschreibprüfprogramme (aspell,
> > ...).
> Selbstverständlich kann unsere Liste dafür verwendet werden. Aber es
> gibt auch schon einiges,
> <URL:http://projekte.dante.de/Trennmuster/Korpora#Frei>
Ich meinte die generierte Wortliste mit Lang-ſ Schreibung.
Ist unter den Korpora so etwas schon dabei?
> > * Automatische s-Konversion von Dokumenten anhand der Wortliste.
> >
> > * Übernahme der Regeln in ein LuaTeX Paket.
> Taco und Hans haben (schon vor einiger Zeit) den Vorschlag, die
> Rund-s/Lang-s-Unterscheidung in LuaTeX durch entsprechende Muster zu
> ermöglichen, positiv aufgenommen. Den Trennalgorithmus wiederum wollen
> sie so erweitern, dass neben musterbasierter Trennung auch eine
> regelbasierte Trennung möglich wird (die Regeln wären dann vermutlich in
> LPEG zu formulieren). Meinst du, die Unterscheidung Rund-s/Lang-s
> ließe sich rein formal durch Regeln erschlagen?
Wenn ein Trennalgorithmus existiert, der ein Wort mit eingetragenen Haupt-
und Nebentrennstellen zurückliefert, kann die S-Schreibung mit einfachen
Regeln bestimmt werden. Die Zahl der Ausnahmen (wie Pilſner) ist beschränkt,
Algorithmus und Ausnahmelisten hätten Platz in einem kleinen Lua-Paket.
Schwieriger wird es, wenn der Trennalgorithmus nach "guten" und "weniger
guten" Trennstellen unterscheidet, weil diese Unterscheidung deutlich
weniger mit den Regeln zur S-Schreibung korreliert.
> > * Wörter mit ſ am Wort oder Silbenende:
...
> > - sz (und st, sp in Reformschreibung): aber rundes s am Wortende
> Was meinst du damit?
Die Verbindungen ss, sp, st und sz werden zu ſſ, ſp, ſt und ſz auch
wenn sie durch eine Nebentrennstelle getrennt sind.
s bleibt rund vor einer Haupttrennstelle, im Auslaut und nach Vorsilben wie
(r)aus-, dis-, konfis-, ple-bis- auch wenn p, s, t oder z folgt:
Weſ-pe, aber dis=putieren
Allgemeinwiſ-ſen aber Abfahrts=ſki
Faſ-zination aber Abfahrts-zeichen
abbürſ-ten (Reformtrennung) aber Abbildungs=technik
In einem Anfall von Rechtschreibschwäche war ich davon ausgegangen, daß in
traditioneller Rechtschreibung auch sp ungetrennt bleibt - damit habe ich mir
eine ganze Menge Fehlschreibungen (wie "Wes-pe" mit rund-s) eingehandelt.
Ein weiterer Fehler war die Behandlung von Doppel-s: da Vorsilben
gegenwärtig mit "-" abgetrennt sind, führt die Wandlung "s-s" -> "ſ-ſ" zu
Fehlern wie "Auſ-ſchuß".
Nach Beheben dieser Fehlerquellen ist die Bilanz des automatischen
Konversion erst einmal deutlich weniger beeindruckend. Die Zahl der
offenen Fälle steigt von 8 auf fast 10000:
Gesamtwortzahl (traditionelle Rechtschreibung) 417629
Automatisch konvertiert 401591
Wichtung der Trennstellen fehlt 6426
noch offen 9612
Die korrigierte Version des Konversionsskripts hängt an.
Günter
#!/usr/bin/env python
# -*- coding: utf8 -*-
# :Copyright: © 2012 Günter Milde.
# :Licence: This work may be distributed and/or modified under
# the conditions of the `LaTeX Project Public License`,
# either version 1.3 of this license or (at your option)
# any later version.
# :Version: 0.1 (2012-01-25)
# ===================================================================
# s2long-s.py: Langes oder rundes S: Entscheidung nach Silbentrennung
# ===================================================================
#
# ::
"""Bestimme die Schreibung mit Rund- oder Lang-s
aus der `Wortliste der deutschsprachigen Trennmustermannschaft`.
"""
# Importe
# =======
#
# Funktionen und Klassen für reguläre Ausdrücke::
import re
# Ausgangsbasis
# =============
#
# Die freie `Wortliste der deutschsprachigen Trennmustermannschaft`_
# ("Lehmansche Liste")
#
# ::
wordfile = file('wortliste') # volle Liste (≅ 400 000 Wörter
# wordfile = file('wortliste-binnen-s') # vorsortierte Liste (≅ 200 000 Wörter)
# Trennzeichen
# ------------
#
# Die Trennzeichen der Wortliste sind
#
# == ================================================================
# \· ungewichtete Trennstellen (solche, wo noch niemand sich um die
# Gewichtung gekümmert hat)
# . unerwünschte Trennstellen (sinnverwirrend), z.B. Ur-in.stinkt
# = Haupttrennstellen
# \- Nebentrennstellen
# _ ungünstige Nebentrennstellen, z.B. Pol=ge_bie-te
# == ================================================================
#
#
# Funktionen
# ==========
#
# ſ-Regeln
# --------
#
# Siehe [wikipedia]_ und ("DDR"-) [Duden]_ (Regeln K 44,45)::
def s_ersetzen(word):
# ſ steht im Silbenanlaut::
word = re.sub(ur'^s', ur'ſ', word)
word = re.sub(ur'([·\-=.])s', ur'\1ſ', word)
# ſ steht im Inlaut als stimmhaftes s zwischen Vokalen
# (gilt auch für ungetrenntes ss zwischen Selbstlauten, z.B. Hausse, Baisse)::
word = re.sub(ur'([AEIOUYÄÖÜaeiouäöü])s([aeiouyäöü])', ur'\1ſ\2', word)
word = re.sub(ur'([AEIOUYÄÖÜaeiouäöü])ss([aeiouyäöü])', ur'\1ſſ\2', word)
# ſ steht in den Verbindungen sp, st, sch und in Digraphen::
word = word.replace(u'st', u'ſt')
word = word.replace(u'sp', u'ſp')
word = word.replace(u'sch', u'ſch')
word = word.replace(u'ps', u'pſ') # ψ
word = word.replace(u'Ps', u'Pſ') # Ψ
# ſ vor Trennstellen
# ~~~~~~~~~~~~~~~~~~
#
# Die Verbindungen ss, sp, st und sz werden zu ſſ, ſp, ſt und ſz auch
# wenn sie durch eine Nebentrennstelle (Trennung innerhalb eines
# Wortbestandteiles) getrennt sind.
#
# s bleibt rund vor einer Haupttrennstelle (Trennung an der Grenze zweier
# Wortbestandteile (Vorsilbe=Stamm, Bestimmungswort=Grundwort), im Auslaut und
# nach Vorsilben wie (r)aus-, dis-, konfis-, ple-bis- (zur Zeit mit
# Nebentrennstelle markiert)::
# word = word.replace(u's-ſ', u'ſ-ſ')
word = word.replace(u's.ſ', u'ſ.ſ')
# word = word.replace(u's-p', u'ſ-p')
word = word.replace(u's.p', u'ſ.p')
# word = word.replace(u's-t', u'ſ-t') # Reformschreibung
word = word.replace(u's.t', u'ſ.t')
# für sz/ſz wurden Spezialregeln erstellt, die Vorkommnisse
# in der Wortliste erfassen
# TODO: Vorsilben mit Haupttrennstelle markieren oder Ausnahmeliste erstellen.
#
# Spezialfälle
# ~~~~~~~~~~~~
#
# ſz trotz Trennzeichen::
word = re.sub(ur'es[-·]zen', ur'eſ-zen', word) # Adoleszenz, ...
word = re.sub(ur's[-·]zil', ur'ſ-zil', word) # Os-zil-la-ti-on
word = re.sub(ur's[-·]zi-n', ur'ſ-zi-n', word) # faszinieren, ...
# ſ wird geschrieben, wenn der S-Laut nur scheinbar im Auslaut steht weil ein
# folgendes unbetontes e ausfällt::
# Basel, Beisel, Pilsen, drechseln, wechseln, häckseln
word = word.replace(u'Bas-ler', u'Baſ-ler')
word = word.replace(u'Pils·ner', u'Pilſ-ner')
word = word.replace(u'Pils-ner', u'Pilſ-ner')
word = word.replace(u'echs-ler', u'echſ-ler') # Dechsler, Wechsler
word = word.replace(u'äcks-ler', u'äckſ-ler') # Häcksler
# Insel (Rheininsler), zünseln (Maiszünsler)
word = word.replace(u'ins·ler', u'inſ-ler')
word = word.replace(u'ins-ler', u'inſ-ler')
word = word.replace(u'üns·ler', u'ünſ-ler')
word = word.replace(u'üns-ler', u'ünſ-ler')
# unsre, unsrige, ...
word = word.replace(u'uns-r', u'unſ-r')
# Häusl, Lisl, bissl, Glasl, Rössl
word = word.replace(u'sl', u'ſl')
word = word.replace(u'ssl', u'ſſl')
# word = re.sub(ur'sl$', ur'ſl', word)
# word = re.sub(ur'ssl$', ur'ſſl', word)
# Fremdwörter
# ~~~~~~~~~~~
#
# Schreibung nach Regeln der Herkunftssprache (im Fraktursatz werden nicht
# eingedeutschte Wörter in Antiqua mit rund-s geschrieben).
#
# English:
# The long, medial, or descending ess, as distinct from the short or
# terminal ess. In Roman script, the long ess was used everywhere except at
# the end of words, where the short ess was used, and frequently in what is
# now the digraph <ss>, which was often written <ſs> rather than <ſſ>
# [en.wiktionary.org]_.
#
# ::
word = word.replace(u'sh', u'ſh') # (englisch)
word = word.replace(u'sc', u'ſc') # (englisch)
word = word.replace(u'Csar', u'Cſar') # Cs -> Tsch (ungarisch)
word = word.replace(u'sz', u'ſz') # polnisch, ungarisch
word = re.sub(ur'([Tt])s([aeiouy])', ur'\1ſ\2', word) # ts (chinesisch)
return word
# s-Regeln
# --------
#
# Test auf verbliebene Unklarheiten
#
# Wenn ein Wort "s" nur an Stellen enthält wo die Regeln rundes S vorsehen,
# ist die automatische Konversion abgeschlossen. ::
def is_complete(word):
# Ersetze s an Stellen wo es rund zu schreiben ist durch ~ und teste auf
# verbliebene Vorkommen:
#
# Einzelfälle mit rundem S (substrings)::
spezialfaelle = [u'Dresd·ne', # Dresd·ner/Dresd·ner·in
]
for fall in spezialfaelle:
word = word.replace(fall, u'~')
# s steht am Wortende, auch in Zusammensetzungen (vor Haupttrennstellen).
# Dasselbe gilt für Doppel-s (aus Fremdwörtern) in der traditionellen
# Rechtschreibung::
word = re.sub(ur's(=|$)', ur'~\1', word)
word = re.sub(ur'ss(=|$)', ur'~~\1', word)
# s steht am Silbenende (nach Nebentrennstellen), wenn kein p, t, z oder ſ
# folgt (in der traditionellen Schreibung, wird st nicht getrennt)::
# word = re.sub(ur'ss?([·.\-][^ptzſ])', ur'~\1', word) # konservativ
word = re.sub(ur'ss?([·.\-][^pzſ])', ur'~\1', word) # traditionell
# s steht nach den Vorsilben (r)aus-, dis-, konfis-,
# ple-bis-, (ergänzen)::
word = re.sub(ur'(^|[·.\-=])[Rr]?[Aa]us([·.\-=])', ur'\1~\2', word)
word = re.sub(ur'(^|[·.\-=])[Dd]is([·.\-=])', ur'\1~\2', word)
word = word.replace(u'on-fis-zie', u'on-fi~-zie')
word = word.replace(u'le-bis-z', u'le-bi~-z')
# s steht im Inlaut vor k, n, w::
word = re.sub(ur's([knw])', ur'~\1', word)
# und suche nach übrigen Vorkommen::
return 's' not in word
# Trennzeichen entfernen
# ----------------------
#
# ::
def join_word(word):
# Spezielle Trennungen für die traditionelle Rechtschreibung::
if '{' in word:
word = word.replace(u'{ck/k·k}', u'ck')
word = word.replace(u'{ff/ff·f}', u'ff')
word = word.replace(u'{ll/ll·l}', u'll')
word = word.replace(u'{mm/mm·m}', u'mm')
word = word.replace(u'{nn/nn·n}', u'nn')
word = word.replace(u'{rr/rr·r}', u'rr')
# Trennstellen in doppeldeutigen Wörtern::
if '[' in word:
word = word.replace(u'[cker·/ck·er.]', u'cker')
word = word.replace(u'[·cker·/ck·er.]', u'cker')
word = word.replace(u'[ll·/ll]', u'll')
word = word.replace(u'[·ker·/k·er.]', u'ker')
word = word.replace(u'[·ſt/ſt·]', u'ſt')
# Verbliebene komplexe Trennstellen::
# if ('[' in word) or ('{' in word):
# print word.encode('utf8')
# Einfache Trennzeichen::
table = {ord(u'·'): None,
ord('='): None,
ord('-'): None,
ord('_'): None,
ord('.'): None,
}
return word.translate(table)
# Kategorien
# ==========
#
# Der Algorithmus sortiert die Wörter der Trennliste in die folgenden
# Kategorien:
#
#
# Menge aller Wörter der Liste (ohne Trennmuster)::
words = set()
# Automatisch konvertierte Wörter (ohne Trennmuster)::
completed = []
# Offene Fälle mit Trennmuster-Feldern wie im Original:
#
# Zur automatischen Konvertierung fehlt die Unterscheidung in Haupt- und
# Nebentrennstellen (Wichtung)::
ungewichtet = []
# Der Algorithmus kann die Schreibweise (noch) nicht ermitteln
# (mit teilweisen Ersetzungen)::
offen = []
# (Noch) nicht behandelt::
reformschreibung = [] # Schreibweisen nach neuer Rechtschreibung
sz_ersetzung = [] # Schreibweisen mit ß-Ersetzungen
# Die originale Wortliste ist mit 'latin-1' kodiert aber lokal nach
# utf8 konvertiert. Versuche zunächst ::
source_encoding = 'utf8'
# und stelle bei Fehlern auf 'latin-1' um.
#
#
# Hauptschleife
# =============
#
# Iteration über alle Zeilen der Wortliste::
for line in wordfile:
# Dekodieren und Entfernen der Zeilenendezeichen
# ----------------------------------------------
#
# ::
try:
line = line.rstrip().decode(source_encoding)
except UnicodeError:
source_encoding = 'latin1'
line = line.rstrip().decode(source_encoding)
# Kommentare ignorieren (werden mit # eingeleitet)::
line = line.split(u'#')[0]
# Zerlegen in Felder
# ------------------
#
# 1. Wort ungetrennt
# 2. Wort mit Trennungen, falls für alle Varianten identisch,
# anderenfalls leer
# 3. falls Feld 2 leer, Trennung nach traditioneller Rechtschreibung
# 4. falls Feld 2 leer, Trennung nach reformierter Rechtschreibung (2006)
# 5. falls Feld 2 leer, Trennung für Wortform, die entweder in
# der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird
# und für traditionelle und reformierte Rechtschreibung identisch ist
# 6. falls Feld 5 leer, Trennung für Wortform, die entweder in
# der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
# traditionelle Rechtschreibung
# 7. falls Feld 5 leer, Trennung für Wortform, die entweder in
# der Schweiz oder mit Großbuchstaben oder Kapitälchen benutzt wird,
# reformierte Rechtschreibung (2006)
# 8. falls Feld 5 leer, Trennung nach (deutsch)schweizerischer
# Rechtschreibung; insbesondere Wörter mit "sss" gefolgt von
# einem Vokal, die wie andere Dreifachkonsonanten gehandhabt wurden
# (also anders, als der Duden früher vorgeschrieben hat), z.B.
# "süssauer"
#
# ::
fields = line.split(';')
# Auswahl der Schreibweise
# ------------------------
#
# Zur Zeit werden nur Wörter in traditioneller Schreibweise behandelt::
i = 1 # 2. Feld: allgemeingültige Trennung
if fields[i] == '-2-': # keine allgemeingültige Trennung
i = 2 # 2. Feld: traditionelle Trennung
if fields[i] == '-3-': # Wort existiert nicht in traditioneller Schreibung
if fields[3] == '-4-': # Wort existiert nicht in Reformschreibung
sz_ersetzung.append(fields)
else:
reformschreibung.append(fields)
continue
word = fields[i] # Wort mit Trennstellen
# Menge aller Wörter der gewählten Schreibweise (ohne Trennstellen)::
words.add(fields[0])
# Vorsortieren
# ------------
#
# Wörter ohne Binnen-s müssen nicht konvertiert werden. Damit wird die
# Wortliste ungefähr um die Hälfte kürzer::
if 's' not in fields[0][:-1]:
completed.append(fields[0])
continue
# # nur vorsortieren:
# offen.append(fields)
# continue
# Regelbasierte s-ſ-Schreibung::
word = s_ersetzen(word)
# Einsortieren nach Vollständigkeit der Ersetzungen::
if is_complete(word):
completed.append(join_word(word))
continue
fields[i] = word # Rückschreiben von teilweisen Ersetzungen
if word.find(u's·') != -1:
ungewichtet.append(fields)
continue
offen.append(fields)
# --------------------------------------------------------------------------
#
# Ende der Hauptschleife
#
# Feedback
# ========
#
# ::
print "Gesamtwortzahl (traditionelle Rechtschreibung)", len(words)
print "Automatisch konvertiert", len(completed)
print "nur in neuer Rechtschreibung", len(reformschreibung)
print "Schweizer und Großschreibvarianten", len(sz_ersetzung)
print "Wichtung der Trennstellen fehlt", len(ungewichtet)
print "noch offen", len(offen)
print "\nkonvertiert+offen", len(completed) + len(ungewichtet) + len(offen)
# Diskussion
# ==========
#
# Statistik
# ---------
#
# | Gesamtwortzahl (traditionelle Rechtschreibung) 417629
# | Automatisch konvertiert 401591
# | nur in neuer Rechtschreibung 4653
# | Schweizer und Großschreibvarianten 8745
# | Wichtung der Trennstellen fehlt 6426
# | noch offen 9612
#
#
# Die große Mehrzahl der Wörter der Trennliste wurde nach den Regeln des
# Dudens in die Schreibung mit langem s (ſ) konvertiert.
#
# Der größte Teil der noch offenen Fälle kann durch Unterscheidung in Haupt-
# und Nebentrennstellen (z.B. mit dem SiSiSi_-Algorithmus) gelöst werden.
#
# Für eine beschränke Anzahl offener Fälle wurden Ausnahmeregeln und Ausnahmen
# implementiert.
#
# Das Resultat muß noch auf nicht erfaßte Ausnahmen und Sonderfälle geprüft
# werden. Fehlentscheidungen sind nicht auszuschließen.
#
#
# Problemfälle
# ------------
#
# Wörter mit ſ am Wort oder Silbenende:
#
# * sp, ss, st und sz wird zu ſp, ſſ, ſt und ſz, auch wenn das ſ vor einer
# Nebentrennstelle steht (z.B. Weſ-pe, eſ-ſen, abbürſ-ten (Reformtrennung)
# und Faſ-zination)
#
# **Aber** rundes s am Wortende und nach Vorsilben, z.B.
# dis=putieren, Aus=ſage, aus-tragen, aus-zeichnen.
#
# Wie sollen Trennungen nach Vorsilben (schwache Hautptrennstellen) markiert
# werden?
#
# Wörter mit identischer Schreibung ohne lang-s:
#
# * Wach[s/ſ]tube, As/Aſ, ...?
#
# Ausgabe
# =======
#
# Wortliste mit automatisch bestimmter S-Schreibung, ohne Trennstellen::
completed.append(u'') # für das letzte Zeilenendezeichen
completed_file = file('wortliste-lang-s', 'w')
completed_file.write(u'\n'.join(completed).encode('utf8'))
# Wortlisten mit noch offenen Fällen::
for todo in ['ungewichtet', 'offen']:
todo_file = file('wortliste-lang-s-'+todo, 'w')
todo = globals()[todo] # get variable from string
todo = [u';'.join(fields) for fields in todo]
todo.append(u'') # für das letzte Zeilenendezeichen
todo_file.write(u'\n'.join(todo).encode('utf8'))
# Quellen
# =======
#
#
#
# .. [Duden] `Der Große Duden` 16. Auflage, VEB Bibliographisches Institut
# Leipzig, 1971
#
# Kennzeichnet im Stichwortteil rundes s durch Unterstreichen.
#
# .. [wikipedia]
# http://de.wikipedia.org/wiki/Langes_s
#
# .. [en.wiktionary.org]
# http://en.wiktionary.org/wiki/%C5%BF
#
# .. _SiSiSi: https://www.ads.tuwien.ac.at/research/SiSiSi.html
#
# .. _Wortliste der deutschsprachigen Trennmustermannschaft:
# http://mirrors.ctan.org/language/hyphenation/dehyph-exptl/projektbeschreibung.pdf
#
# .. Fragen, Spezialfälle, Beispiele
#
# http://www.e-welt.net/bfds_2003/bund/fragen/13_Langes%20oder%20rundes%20S.pdf
# http://www.e-welt.net/bfds_2003/bund/fragen/12_hs%20und%20scharfes%20s_1.pdf
Mehr Informationen über die Mailingliste Trennmuster