[Trennmuster] Entscheidungsmuster für Binnen- und Schluss-S in Frakturschriften

Guenter Milde milde at users.sf.net
So Jan 29 21:34:48 CET 2012


Hallo Werner und alle Trennmustler,

On 27.01.12, Werner LEMBERG wrote:


> > Nach Anwendung eines Python-Skripts mit Konversionsregeln (+ einigen
> > Spezialfällen) auf die Wortliste erhalte ich für die traditionelle
> > Rechtschreibung (de-DE-1901):
> >
> > | Gesamtwortzahl (traditionelle Rechtschreibung) 417629
> > | Automatisch konvertiert 411341
> > | nur in neuer Rechtschreibung 4653
> > | Schweizer und Großschreibvarianten 8745
> > | Wichtung der Trennstellen fehlt 6280
> > | noch offen 8

> Das schaut doch ganz gut aus!

> > Der größte Teil der noch offenen Fälle kann durch Wichtung der
> > Trennstellen (z.B. mit dem SiSiSi-Algorithmus) gelöst werden.

> Das geht nur manuell, denn...

> > Gibt es schon Ansätze, SiSiSi über die Wortliste laufen zu
> > lassen?

> dies ist bereits geschehen und eingebaut.

Eventuell kann die Kombination SiSiSi + Kenntniss der (ungewichteten)
Trennstellen helfen weitere Wörter automatisch zu wichten.

> >     Wie ist mit Trennungen nach übliche Vorsilben (aus-, bis-, ...) zu
> >     verfahren: Haupt- oder Nebentrennstelle?

> Ich betrachte diese Trennstellen als Haupttrennstellen.

Sind dann die Nebentrennstellen nach "aus" von "aus-ba-lan-cier-tes" bis
"Aus-rei-se|wil-li-gen" als Fehler anzusehen?

> Beachte bitte, daß ich die Hauptarbeit in einen Zweig des
> git-Repositoriums investiert habe:

>   > git branch

>   * Keine-Haupttrennstellen-in-zweisilbigen-Wörtern
>     master

> Da sind mehr Haupttrennstellen kontrolliert und ergänzt.

Leider sind die fehlenden Haupttrennstellen in zweisilbigen Wörtern für die
automatische ſ-Umstellung problematisch: Auch in zweisilbigen Wörtern muß
ein s=t st bleiben, aber ein s-t zu ſt werden.

Ich plädiere daher dafür, alle Haupttrennstellen in der Wortliste zu
markieren. Die "Herabstufung" der Trennstellen in zweisilbigen Wörtern kann
leicht mit einem Skript geschehen, die Unterscheidung nach Haupt- und
Nebentrennstellen nicht.


> > * Wörter mit identischer Schreibung ohne lang-s:
> >
> >   - Wach[s/ſ]tube, As/Aſ, ...?

> Die sollten separiert werden.  Derzeit:

>   Wachstube;Wach[·s/s·]tu·be

> (»·« markiert ungewichtete Trennstellen).

> Mit »s« und »ſ«:

>   Wachstube;Wachs=tu-be
>   Wachſtube;Wach=ſtu·be

> Ein Skript, das »ſ« wieder nach »s« konvertiert, müßte solche Fälle
> finden und Trennstellen entsprechend unterbinden.

Ich schlage vor, die "Ur-Liste" in "Rundschreibung" zu belassen, da ich
denke, daß sie dann einfacher zu lesen und zu pflegen ist. (Anwender, die
die Liste in Frakturschrift betrachten/bearbeiten dürften in der
Unterzahl sein. ;-) Mitwirkende die neue Trennstellen beitragen müssen
sich dann nicht auch noch um die korrekte S-Schreibung sorgen.

Auch wenn die Konversion in s->ſ Richtung deutlich komplexer ist, läßt
sie sich auf der Basis von gewichteten Trennstellen weitgehend automatisch
erledigen. Unklare Fälle kann dann ein ſ-Experte über Ausnahmelisten und
verbesserte Ersetzungsregeln lösen.


> > * Probleme der Wortliste:
> >
> >   - Trennung nach ß fehlt (?): ``Abgußsaal;-2-;Ab·gußsaal;-4-``

...

Bitte um Entschuldigung, das war ein Fehler in meiner latin1-utf-8 Konversion.

> >   - Abkürzungen (Ausg, hrsg, insb msec, nsec) (Wirklich ohne Punkt
> >     ins Wörterverzeichnis?)

> Ja, ohne Punkt im Wörterverzeichnis: Erstens hat der Punkt eine
> spezielle Bedeutung in der Liste (er markiert zu unterdrückende
> Trennstellen), zweitens ist es dem Trennalgorithmus egal :-)

Aber es bleibt die Frage, ob untrennbare Abkürzungen und falsch geschriebene
Maßeinheiten (nach SI ms und nicht msec) überhaupt in eine Trennmusterliste
gehören.

Für die s-ſ Liste ist dagegen die Aufnahme von z.B. "uſw" durchaus zu erwägen.

> > * Reformschreibung mit lang-s?

> Wäre durchaus möglich, aber wer braucht das?

> > Bei Interesse kann ich das Skript und die generierte Wortliste ins
> > Netz stellen oder schicken.

> Bitte schick doch das Skript an die Liste!


Gern.


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 und in den Verbindungen sp, st, sch::

    word = re.sub(ur'^s', ur'ſ', word)
    word = re.sub(ur'([·\-=.])s', ur'\1ſ', word)

    word = word.replace(u'st', u'ſt')
    word = word.replace(u'sp', u'ſp')
    word = word.replace(u'sch', u'ſch')

# ſ steht in Digraphen::

    word = word.replace(u'sh', u'ſh')
    word = word.replace(u'ps', u'pſ')  # ψ
    word = word.replace(u'Ps', u'Pſ')  # Ψ
    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)

# ſ steht im Inlaut als stimmhaftes s zwischen Vokalen::

    word = re.sub(ur'([AEIOUYÄÖÜaeiouäöü])s([aeiouyäöü])', ur'\1ſ\2', word)

# ss wird zu ſſ, wenn es nicht an einer Haupttrennstelle oder im Auslaut
# steht::

    word = word.replace(u's-ſ', u'ſ-ſ')
    word = word.replace(u's.ſ', u'ſ.ſ')
    # Ungetrenntes ss zwischen Selbstlauten, z.B. Hausse, Baisse
    word = re.sub(ur'([AEIOUYÄÖÜaeiouäöü])ss([aeiouyäöü])', ur'\1ſſ\2', word)


# Spezialfälle
# ~~~~~~~~~~~~
# 
# ſz im Anlaut trotz Trennung::

    word = re.sub(ur'es[-·]zen', ur'eſ-zen', word) # Rekonvaleszenz, 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, Faszination

# ſ 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)

    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::

    # Spezialfä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)::

    word = re.sub(ur's(=|$)', ur'~\1', word)

# Dasselbe gilt für Doppel-s (aus Fremdwörtern) in der traditionellen
# Rechtschreibung::

    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 ſp/ſt nicht getrennt)::

    # word = re.sub(ur's([·.\-][^ptzſ])', ur'~\1', word) # konservativ
    word = re.sub(ur'ss?([·.\-][^zſ])', ur'~\1', word)   # traditionell

# s steht vor z, nach den Vorsilben (r)aus-, dis-, konfis-,
# ple-bis-, (ergänzen)::

    word = re.sub(ur'(^|[·.\-=])[Rr]?[Aa]us([·.\-=]z)', ur'\1~\2', word)
    word = re.sub(ur'(^|[·.\-=])[Dd]is([·.\-=]z)', 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([cknw])', 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 or word.find(u's·z') != -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 411341
# | nur in neuer Rechtschreibung 4653
# | Schweizer und Großschreibvarianten 8745
# | Wichtung der Trennstellen fehlt 6280
# | noch offen 8
# 
# 
# 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 Wichtung der
# Trennstellen (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.
# 
# Auch wenn der Algorithmus eher konservativ ist, sind Fehlentscheidungen
# insbesondere in Spezialfällen und Fremdwörtern nicht auszuschließen.
# 
# Problemfälle
# ------------
# 
# Wörter mit ſ am Wort oder Silbenende:
# 
# * Tonarten/Töne: Aſ, Deſ, ...
# 
# * sz (und st, sp in Reformschreibung): aber rundes s am Wortende
# 
#   Wie ist mit Trennungen nach übliche Vorsilben (aus-, bis-, ...) zu
#   verfahren: Haupt- oder Nebentrennstelle?
#   Ggf. neues Symbol: Nebentrennstelle, die aber Schluss-s verlangt?
# 
# Wörter mit identischer Schreibung ohne lang-s:
# 
# * Wach[s/ſ]tube, As/Aſ, ...?
# 
# Probleme der Worliste:
# 
# * Abkürzungen (Ausg, hrsg, insb msec, nsec)
# 
# 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
# 
# .. _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