Je dois trouver un moyen assez efficace de détecter les syllabes dans un mot. Par exemple.,
Invisible -> in-vi-sib-le
Certaines règles de syllabification peuvent être utilisées:
V CV VC CVC CCV CCCV CVCC
* où V est une voyelle et C est une consonne .
Prononciation (5 voix contre; CV-CVC-CV-V-CVC)
J'ai essayé peu de méthodes, parmi lesquelles on utilisait regex (ce qui n'est utile que si l'on veut compter les syllabes) ou la définition de règle codée en dur (une approche de force brute qui s'avère très peu efficace) et enfin l'utilisation d'un automate à états finis (qui ne donne rien d’utile).
Le but de mon application est de créer un dictionnaire de toutes les syllabes dans une langue donnée. Ce dictionnaire sera utilisé par la suite pour des applications de vérification orthographique (utilisant des classificateurs bayésiens) et de synthèse de texte à parole.
Je vous serais reconnaissant de bien vouloir me donner des conseils sur un autre moyen de résoudre ce problème en plus de mes approches précédentes.
Je travaille en Java, mais toute astuce en C/C++, C #, Python, Perl ... fonctionnerait pour moi.
Lisez à propos de l'approche TeX de ce problème aux fins de la césure. Voir en particulier Frank Liang mémoire de thèse Parole Hy-phen-a-tion de Com-put-er . Son algorithme est très précis et inclut ensuite un dictionnaire de petites exceptions pour les cas où l’algorithme ne fonctionne pas.
Je suis tombé sur cette page à la recherche de la même chose et j'ai trouvé quelques implémentations du papier Liang ici: https://github.com/mnater/hyphenator
C’est à moins que vous ne soyez du genre à apprécier la lecture d’une thèse de 60 pages au lieu d’adapter du code librement disponible à un problème non unique. :)
Voici une solution utilisant 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()]]
J'essaie de résoudre ce problème pour un programme qui calcule les scores de lecture flesch-kincaid et flesch d'un bloc de texte. Mon algorithme utilise ce que j'ai trouvé sur ce site: http://www.howmanysyllables.com/howtocountsyllables.html et cela devient assez proche. Il a toujours du mal à utiliser des mots compliqués comme invisible et la césure, mais je trouve que ça entre dans le stade pour mes besoins.
Il a l'avantage d'être facile à mettre en œuvre. J'ai trouvé le "es" peut être syllabique ou non. C'est un pari mais j'ai décidé de supprimer l'es de mon algorithme.
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;
}
C'est un problème particulièrement difficile qui n'est pas complètement résolu par l'algorithme de césure de LaTeX. Vous trouverez un bon résumé de certaines des méthodes disponibles et des problèmes rencontrés dans le document Evaluation des algorithmes de syllabification automatique en anglais (Marchand, Adsett et Damper 2007).
Merci Joe Basirico pour le partage de votre implémentation rapide et sale en C #. J'ai utilisé les grandes bibliothèques et elles fonctionnent, mais elles sont généralement un peu lentes et, pour les projets rapides, votre méthode fonctionne bien.
Voici votre code en Java, avec les cas de test:
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));
}
Le résultat était comme prévu (cela fonctionne assez bien pour 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 et @ joe-basirico. Fonction très utile, pas parfaite, mais bonne pour la plupart des projets de petite à moyenne taille. Joe, j'ai réécrit une implémentation de votre code en Python:
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
J'espère que quelqu'un trouve cela utile!
Aujourd'hui, j'ai trouvé ceci une implémentation Java de l'algorithme de césure de Frank Liang avec un motif pour l'anglais ou l'allemand, qui fonctionne assez bien et est disponible sur Maven Central.
Cave: Il est important de supprimer les dernières lignes des fichiers de modèle .tex
, sinon ces fichiers ne peuvent pas être chargés avec la version actuelle sur Maven Central.
Pour charger et utiliser la hyphenator
, vous pouvez utiliser l'extrait de code Java suivant. texTable
est le nom des fichiers .tex
contenant les modèles nécessaires. Ces fichiers sont disponibles sur le site github du projet.
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;
}
Ensuite, la Hyphenator
est prête à être utilisée. Pour détecter les syllabes, l’idée de base est de scinder le terme au niveau des tirets fournis.
String hyphenedTerm = hyphenator.hyphenate(term);
String hyphens[] = hyphenedTerm.split("\u00AD");
int syllables = hyphens.length;
Vous devez diviser en "\u00AD
", car l’API ne renvoie pas un "-"
normal.
Cette approche surpasse la réponse de Joe Basirico, car elle prend en charge de nombreuses langues différentes et détecte la césure allemande plus précisément.
Perl a Lingua :: Phonology :: Syllable module. Vous pouvez essayer cela, ou essayer de regarder dans son algorithme. J'ai aussi vu quelques modules plus anciens.
Je ne comprends pas pourquoi une expression régulière ne donne qu'un nombre de syllabes. Vous devriez pouvoir obtenir les syllabes elles-mêmes en utilisant des parenthèses de capture. En supposant que vous puissiez construire une expression régulière qui fonctionne, c'est-à-dire.
Pourquoi le calculer? Chaque dictionnaire en ligne a cette information. http://dictionary.reference.com/browse/invisible in · vis · i · ble
Je ne pouvais pas trouver un moyen adéquat pour compter les syllabes, alors j'ai conçu moi-même une méthode.
Vous pouvez voir ma méthode ici: https://stackoverflow.com/a/32784041/2734752
J'utilise une combinaison d'un dictionnaire et d'une méthode d'algorithme pour compter les syllabes.
Vous pouvez voir ma bibliothèque ici: https://github.com/troywatson/Lawrence-Style-Checker
Je viens de tester mon algorithme et j'avais un taux de collision de 99,4%!
Lawrence lawrence = new Lawrence();
System.out.println(lawrence.getSyllable("hyphenation"));
System.out.println(lawrence.getSyllable("computer"));
Sortie:
4
3
Merci @ joe-basirico et @tihamer. J'ai porté le code de @ tihamer sur Lua 5.1, 5.2 et luajit 2 ( fonctionnera probablement sur d'autres versions de lua )
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
Et quelques tests amusants pour confirmer que cela fonctionne ( autant que cela est supposé ):
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.")
J'ai rencontré exactement le même problème il y a un moment.
J'ai fini par utiliser le Dictionnaire de prononciation CMU pour une recherche rapide et précise de la plupart des mots. Pour les mots ne figurant pas dans le dictionnaire, je suis revenu à un modèle d’apprentissage automatique précis à environ 98% pour prédire le nombre de syllabes.
J'ai résumé le tout dans un module python facile à utiliser ici: https://github.com/repp/big-phoney
Installer: pip install big-phoney
Comptez les syllabes:
from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops') # --> 4
Si vous n'utilisez pas Python et que vous souhaitez essayer l'approche basée sur un modèle ML, j'ai écrit de manière assez détaillée sur le fonctionnement du modèle de comptage de syllabes sous Kaggle .
Après avoir beaucoup testé et essayé plusieurs paquets de césure, j'ai écrit le mien à partir d'un certain nombre d'exemples. J'ai également essayé les packages pyhyphen
et pyphen
qui s'interfacent avec les dictionnaires de césure, mais ils produisent souvent un nombre incorrect de syllabes. Le package nltk
était tout simplement trop lent pour ce cas d'utilisation.
Mon implémentation en Python fait partie d'une classe que j'ai écrite et la routine de comptage des syllabes est collée ci-dessous. Il surestime un peu le nombre de syllabes, car je n’ai toujours pas trouvé le moyen de rendre compte de la fin silencieuse de Word.
La fonction renvoie le rapport des syllabes par mot utilisé pour un score de lisibilité de Flesch-Kincaid. Le nombre n'a pas besoin d'être exact, mais assez proche pour une estimation.
Sur mon processeur i7 de 7e génération, cette fonction prenait 1,1 à 1,2 millisecondes pour un exemple de texte de 759 Word.
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)