Erkennen von Silben in einem Wort
-
03-07-2019 - |
Frage
Ich brauche eine ziemlich effiziente Art und Weise finden Silben in einem Wort zu erkennen. Z.B.
Unsichtbar -> in-vi-sib-le
Es gibt einige syllabification Regeln, die verwendet werden könnten:
V Lebenslauf VC CVC CCV CC CV CVCC
*, wobei V ein Vokal und C ist ein Konsonant. Zum Beispiel,
Aussprache (5 Pro-Nonne-ci-a-tion; CV-CVC-CV-V-CVC)
Ich habe einige Methoden versucht, unter denen regex wurden mit oder hartcodierte Regeldefinition (ein Brute-Force-Ansatz, der sehr ineffizient erweist) (das nur, wenn Sie wollen Silben zählen hilft) und schließlich einen endlichen Automaten mit (das führte nicht mit irgendetwas nützlich).
Der Zweck meiner Anwendung ist ein Wörterbuch aller Silben in einer bestimmten Sprache zu erstellen. Das Wörterbuch wird später für die Rechtschreibprüfung von Anwendungen (unter Verwendung von Bayes-Klassifizierer) verwendet werden, und Text-zu-Sprache-Synthese.
Ich würde schätzen, wenn man mir Tipps auf einem anderen Weg geben könnte dieses Problem neben meinen bisherigen Ansätzen zu lösen.
Ich arbeite in Java, aber jede Spitze in C / C ++, C #, Python, Perl ... für mich funktionieren würde.
Lösung
Lesen Sie über die TeX Ansatz für dieses Problem für die Zwecke der hyphenation. sehen vor allem Frank Liang Dissertation Dissertation Word-Hy-Phen-a-tion von Com- put-er . Sein Algorithmus ist sehr genau und schließt dann eine kleine Ausnahmen Wörterbuch für Fälle, in denen der Algorithmus nicht funktioniert.
Andere Tipps
ich auf diese Seite gestolpert für die gleiche Sache suchen, und einige Implementierungen des Liang Papier finden Sie hier: https://github.com/mnater/hyphenator
Das ist, wenn Sie der Typ, die eine 60-seitigen Dissertation statt Anpassung frei verfügbaren Code für nicht eindeutiges Problem gerne lesen. :)
Hier ist eine Lösung mit NLTK :
from nltk.corpus import cmudict
d = cmudict.dict()
def nsyl(word):
return [len(list(y for y in x if y[-1].isdigit())) for x in d[word.lower()]]
Ich versuche, dieses Problem für ein Programm zu beseitigen, die die Flesch-Kincaid und flesch Lesen Partitur eines Textblock wird berechnet werden. Mein Algorithmus verwendet, was ich auf dieser Website: http://www.howmanysyllables.com/howtocountsyllables.html und es wird ziemlich nahe. Es hat immer noch Probleme auf komplizierte Wörter wie unsichtbar und Silbentrennung, aber ich habe festgestellt, es für meine Zwecke im Baseballstadion bekommt.
Es hat den Kopf, dass sie einfach zu implementieren. Ich fand das „es“ kann entweder syllabic sein oder nicht. Es ist ein Glücksspiel, aber ich beschlossen, die es in meinem Algorithmus zu entfernen.
private int CountSyllables(string word)
{
char[] vowels = { 'a', 'e', 'i', 'o', 'u', 'y' };
string currentWord = word;
int numVowels = 0;
bool lastWasVowel = false;
foreach (char wc in currentWord)
{
bool foundVowel = false;
foreach (char v in vowels)
{
//don't count diphthongs
if (v == wc && lastWasVowel)
{
foundVowel = true;
lastWasVowel = true;
break;
}
else if (v == wc && !lastWasVowel)
{
numVowels++;
foundVowel = true;
lastWasVowel = true;
break;
}
}
//if full cycle and no vowel found, set lastWasVowel to false;
if (!foundVowel)
lastWasVowel = false;
}
//remove es, it's _usually? silent
if (currentWord.Length > 2 &&
currentWord.Substring(currentWord.Length - 2) == "es")
numVowels--;
// remove silent e
else if (currentWord.Length > 1 &&
currentWord.Substring(currentWord.Length - 1) == "e")
numVowels--;
return numVowels;
}
Dies ist ein besonders schwieriges Problem, das nicht vollständig durch die LaTeX Trennungs-Algorithmen gelöst. Eine gute Zusammenfassung einiger verfügbaren Methoden und die Herausforderungen, die sich im Papier Auswertung Automatische Silbentrennung Algorithmen für Englisch (Marchand, Adsett und Dämpfer 2007).
Danke Joe Basirico, für Ihre schnelle und schmutzige Implementierung in C # zu teilen. Ich habe die großen Bibliotheken verwendet, und sie arbeiten, aber sie sind in der Regel ein bisschen langsam, und für schnelle Projekte, Ihre Methode funktioniert gut.
Hier ist der Code in Java, zusammen mit Testfällen:
public static int countSyllables(String word)
{
char[] vowels = { 'a', 'e', 'i', 'o', 'u', 'y' };
char[] currentWord = word.toCharArray();
int numVowels = 0;
boolean lastWasVowel = false;
for (char wc : currentWord) {
boolean foundVowel = false;
for (char v : vowels)
{
//don't count diphthongs
if ((v == wc) && lastWasVowel)
{
foundVowel = true;
lastWasVowel = true;
break;
}
else if (v == wc && !lastWasVowel)
{
numVowels++;
foundVowel = true;
lastWasVowel = true;
break;
}
}
// If full cycle and no vowel found, set lastWasVowel to false;
if (!foundVowel)
lastWasVowel = false;
}
// Remove es, it's _usually? silent
if (word.length() > 2 &&
word.substring(word.length() - 2) == "es")
numVowels--;
// remove silent e
else if (word.length() > 1 &&
word.substring(word.length() - 1) == "e")
numVowels--;
return numVowels;
}
public static void main(String[] args) {
String txt = "what";
System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
txt = "super";
System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
txt = "Maryland";
System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
txt = "American";
System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
txt = "disenfranchized";
System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
txt = "Sophia";
System.out.println("txt="+txt+" countSyllables="+countSyllables(txt));
}
Das Ergebnis wurde wie erwartet (es funktioniert gut genug für Flesch-Kincaid):
txt=what countSyllables=1
txt=super countSyllables=2
txt=Maryland countSyllables=3
txt=American countSyllables=3
txt=disenfranchized countSyllables=5
txt=Sophia countSyllables=2
Bumping @Tihamer und @ joe-basirico. Sehr nützliche Funktion, nicht perfekt , aber gut für die meisten kleinen bis mittleren Projekten. Joe, ich habe eine Implementierung des Codes in Python neu geschrieben:
def countSyllables(word):
vowels = "aeiouy"
numVowels = 0
lastWasVowel = False
for wc in word:
foundVowel = False
for v in vowels:
if v == wc:
if not lastWasVowel: numVowels+=1 #don't count diphthongs
foundVowel = lastWasVowel = True
break
if not foundVowel: #If full cycle and no vowel found, set lastWasVowel to false
lastWasVowel = False
if len(word) > 2 and word[-2:] == "es": #Remove es - it's "usually" silent (?)
numVowels-=1
elif len(word) > 1 and word[-1:] == "e": #remove silent e
numVowels-=1
return numVowels
Hope jemand findet diese nützlich!
Perl hat Lingua :: Phonology :: Syllable Modul. Sie könnten versuchen, das, oder versuchen, in seinen Algorithmus suchen. Ich sah ein paar andere älteren Module auch da.
Ich verstehe nicht, warum ein regulärer Ausdruck nur eine Anzahl von Silben gibt. Sie sollten die Silben selbst capture Klammern mit bekommen können. Angenommen, Sie können einen regulären Ausdruck konstruieren, das funktioniert, das ist.
Heute habe ich diese Java-Implementierung von Frank Liang hyphenation Algorithmus mit Muster für Englisch oder Deutsch , die funktioniert ganz gut und auf Maven Zentrale zur Verfügung.
Cave:. Es ist wichtig, die letzten Zeilen der .tex
Pattern-Dateien zu entfernen, da sonst die Dateien nicht mit der aktuellen Version auf Maven Zentrale geladen werden
Um die hyphenator
zu laden und verwenden, können Sie den folgenden Java-Code-Snippet verwenden. texTable
ist der Name der .tex
Dateien die benötigten Muster enthält. Diese Dateien sind auf der Projekt Github-Website zur Verfügung.
private Hyphenator createHyphenator(String texTable) {
Hyphenator hyphenator = new Hyphenator();
hyphenator.setErrorHandler(new ErrorHandler() {
public void debug(String guard, String s) {
logger.debug("{},{}", guard, s);
}
public void info(String s) {
logger.info(s);
}
public void warning(String s) {
logger.warn("WARNING: " + s);
}
public void error(String s) {
logger.error("ERROR: " + s);
}
public void exception(String s, Exception e) {
logger.error("EXCEPTION: " + s, e);
}
public boolean isDebugged(String guard) {
return false;
}
});
BufferedReader table = null;
try {
table = new BufferedReader(new InputStreamReader(Thread.currentThread().getContextClassLoader()
.getResourceAsStream((texTable)), Charset.forName("UTF-8")));
hyphenator.loadTable(table);
} catch (Utf8TexParser.TexParserException e) {
logger.error("error loading hyphenation table: {}", e.getLocalizedMessage(), e);
throw new RuntimeException("Failed to load hyphenation table", e);
} finally {
if (table != null) {
try {
table.close();
} catch (IOException e) {
logger.error("Closing hyphenation table failed", e);
}
}
}
return hyphenator;
}
Danach wird die Hyphenator
ist einsatzbereit. Zur Erkennung von Silben, ist die Grundidee, den Begriff an den vorgesehenen Bindestrichen zu trennen.
String hyphenedTerm = hyphenator.hyphenate(term);
String hyphens[] = hyphenedTerm.split("\u00AD");
int syllables = hyphens.length;
Sie müssen sich auf "\u00AD
“spalten, da die API keine normale "-"
zurückgibt.
Dieser Ansatz übertrifft die Antwort von Joe Basirico, da es viele verschiedenen Sprachen und erkennt deutsche hyphenation genauer.
unterstütztWarum es berechnen? Jeder Online-Wörterbuch hat diese Info. http://dictionary.reference.com/browse/invisible in · vis · i · ble
Danke @ joe-basirico und @tihamer. Ich habe @ Tihamer den Code zu Lua portiert 5.1, 5.2 und luajit 2 ( höchstwahrscheinlich auf anderen Versionen von lua laufen auch ):
countsyllables.lua
function CountSyllables(word)
local vowels = { 'a','e','i','o','u','y' }
local numVowels = 0
local lastWasVowel = false
for i = 1, #word do
local wc = string.sub(word,i,i)
local foundVowel = false;
for _,v in pairs(vowels) do
if (v == string.lower(wc) and lastWasVowel) then
foundVowel = true
lastWasVowel = true
elseif (v == string.lower(wc) and not lastWasVowel) then
numVowels = numVowels + 1
foundVowel = true
lastWasVowel = true
end
end
if not foundVowel then
lastWasVowel = false
end
end
if string.len(word) > 2 and
string.sub(word,string.len(word) - 1) == "es" then
numVowels = numVowels - 1
elseif string.len(word) > 1 and
string.sub(word,string.len(word)) == "e" then
numVowels = numVowels - 1
end
return numVowels
end
Und einige Spaß Tests zu bestätigen es funktioniert ( so viel wie es angenommen hat, zu ):
countsyllables.tests.lua
require "countsyllables"
tests = {
{ word = "what", syll = 1 },
{ word = "super", syll = 2 },
{ word = "Maryland", syll = 3},
{ word = "American", syll = 4},
{ word = "disenfranchized", syll = 5},
{ word = "Sophia", syll = 2},
{ word = "End", syll = 1},
{ word = "I", syll = 1},
{ word = "release", syll = 2},
{ word = "same", syll = 1},
}
for _,test in pairs(tests) do
local resultSyll = CountSyllables(test.word)
assert(resultSyll == test.syll,
"Word: "..test.word.."\n"..
"Expected: "..test.syll.."\n"..
"Result: "..resultSyll)
end
print("Tests passed.")
Ich konnte keine angemessene Art und Weise zu zählen Silben finden, so habe ich eine Methode selbst.
https://stackoverflow.com/a/32784041/2734752 Sie können meine Methode hier ansehen >Ich verwende eine Kombination aus einem Wörterbuch und Algorithmus Methode Silben zu zählen.
Sie können meine Bibliothek hier lesen: https://github.com/troywatson/Lawrence- Style-Checker
ich gerade getestet meinen Algorithmus und hatte eine 99,4% Trefferquote!
Lawrence lawrence = new Lawrence();
System.out.println(lawrence.getSyllable("hyphenation"));
System.out.println(lawrence.getSyllable("computer"));
Ausgabe:
4
3
Ich lief in dieser genau die gleiche Frage vor einiger Zeit.
Ich landete mit dem CMU Aussprache-Wörterbuch für die schnellen und genaue Lookups der meisten Wörter. Für Worte nicht im Wörterbuch, fiel ich zurück zu einem maschinellen Lernmodell, das bei der Vorhersage Silbe zählt ~ 98% korrekt ist.
Ich wickelte die ganze Sache in einer einfach zu bedienende Python-Modul hier: https: // github.com/repp/big-phoney
Installieren:
pip install big-phoney
Syllables Count:
from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops') # --> 4
Wenn Sie nicht mit Python und Sie den ML-Modell-basierten Ansatz, um zu versuchen, ich habe ein ziemlich detaillierten schreiben auf, wie die Silbe Zählung Modell funktioniert auf Kaggle .
Nachdem eine Menge Tests zu tun und auch hyphenation Pakete auszuprobieren, schrieb ich meine eigenen basierend auf einer Reihe von Beispielen. Ich habe auch versucht die pyhyphen
und pyphen
Pakete, die mit hyphenation Wörterbücher Schnittstellen, aber sie produzieren die falsche Anzahl von Silben in vielen Fällen. Das nltk
Paket war einfach zu langsam für diesen Anwendungsfall.
Meine Implementierung in Python ist Teil einer Klasse i geschrieben, und die Silbe Routine zu zählen ist unten eingefügt. Es überschätzt die Anzahl der Silben ein bisschen wie ich habe noch nicht eine gute Art und Weise zu machen stille Wortendungen.
gefundenDie Funktion gibt das Verhältnis von Silben pro Wort, wie es für eine Flesch-Kincaid Lesbarkeit Score verwendet wird. Die Zahl muss nicht genau sein, gerade genug, um in der Nähe für eine Schätzung.
Auf meinem 7. Generation i7 CPU, nahm diese Funktion 1,1-1,2 Millisekunden für eine 759 Wort Beispieltext.
def _countSyllablesEN(self, theText):
cleanText = ""
for ch in theText:
if ch in "abcdefghijklmnopqrstuvwxyz'’":
cleanText += ch
else:
cleanText += " "
asVow = "aeiouy'’"
dExep = ("ei","ie","ua","ia","eo")
theWords = cleanText.lower().split()
allSylls = 0
for inWord in theWords:
nChar = len(inWord)
nSyll = 0
wasVow = False
wasY = False
if nChar == 0:
continue
if inWord[0] in asVow:
nSyll += 1
wasVow = True
wasY = inWord[0] == "y"
for c in range(1,nChar):
isVow = False
if inWord[c] in asVow:
nSyll += 1
isVow = True
if isVow and wasVow:
nSyll -= 1
if isVow and wasY:
nSyll -= 1
if inWord[c:c+2] in dExep:
nSyll += 1
wasVow = isVow
wasY = inWord[c] == "y"
if inWord.endswith(("e")):
nSyll -= 1
if inWord.endswith(("le","ea","io")):
nSyll += 1
if nSyll < 1:
nSyll = 1
# print("%-15s: %d" % (inWord,nSyll))
allSylls += nSyll
return allSylls/len(theWords)
habe ich jsoup einmal zu machen. Hier ist ein Beispiel Silbenanalysierer:
public String[] syllables(String text){
String url = "https://www.merriam-webster.com/dictionary/" + text;
String relHref;
try{
Document doc = Jsoup.connect(url).get();
Element link = doc.getElementsByClass("word-syllables").first();
if(link == null){return new String[]{text};}
relHref = link.html();
}catch(IOException e){
relHref = text;
}
String[] syl = relHref.split("·");
return syl;
}