Detección de sílabas en una palabra
Necesito encontrar una manera bastante eficiente de detectar sílabas en una palabra. Por ejemplo,
Invisible - > in-vi-sib-le
Hay algunas reglas de silabificación que podrían usarse:
V CV VC CVC CCV CCCV CVCC
*donde V es una vocal y C es una consonante. Por ejemplo,
Pronunciación (5 Pro-nun-ci-a-ción; CV-CVC-CV-V-CVC)
He probado algunos métodos, entre los cuales estaban el uso de expresiones regulares (que ayuda solo si desea contar sílabas) o la definición de reglas codificadas (a enfoque de fuerza bruta que resulta ser muy ineficiente) y, finalmente, el uso de un autómata de estado finito (que no resultó con nada útil).
El propósito de mi aplicación es crear un diccionario de todas las sílabas en un idioma dado. Este diccionario se utilizará más tarde para aplicaciones de corrección ortográfica (utilizando clasificadores bayesianos) y síntesis de texto a voz.
Agradecería si uno pudiera darme consejos sobre una forma alternativa de resolver este problema además de mi anterior enfoque.
Trabajo en Java, pero cualquier consejo en C/C++, C#, Python, Perl... trabajaría para mí.
15 answers
Lea sobre el enfoque de TeX para este problema a los efectos de la partición. Especialmente ver la disertación de tesis de Frank Liang Word Hy-phen-a-tion by Com-put-er . Su algoritmo es muy preciso, y luego incluye un pequeño diccionario de excepciones para los casos en los que el algoritmo no funciona.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-01 17:17:58
Me topé con esta página buscando lo mismo, y encontré algunas implementaciones del documento de Liang aquí: https://github.com/mnater/hyphenator
Eso es a menos que seas del tipo que disfruta leyendo una tesis de 60 páginas en lugar de adaptar código disponible libremente para problemas no únicos. :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-19 00:07:37
Aquí hay una solución usando 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()]]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-21 23:52:31
Estoy tratando de abordar este problema para un programa que calculará la puntuación de lectura flesch-kincaid y flesch de un bloque de texto. Mi algoritmo utiliza lo que encontré en este sitio web: http://www.howmanysyllables.com/howtocountsyllables.html y se acerca razonablemente. Todavía tiene problemas con palabras complicadas como invisible y separación de palabras, pero he encontrado que se pone en el estadio para mis propósitos.
Tiene la ventaja de ser fácil de implementar. Encontré que la " es " puede ser silábico o no. Es una apuesta, pero decidí eliminar las es en mi algoritmo.
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-16 21:53:02
Este es un problema particularmente difícil que no está completamente resuelto por el algoritmo de partición de látex. Un buen resumen de algunos métodos disponibles y los desafíos involucrados se puede encontrar en el documento Evaluating Automatic Syllabification Algorithms for English (Marchand, Adsett, and Damper 2007).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-07 15:40:54
Gracias Joe Basirico, por compartir su implementación rápida y sucia en C#. He usado las bibliotecas grandes, y funcionan, pero por lo general son un poco lentas, y para proyectos rápidos, su método funciona bien.
Aquí está su código en Java, junto con los casos de prueba:
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));
}
El resultado fue el esperado (funciona lo suficientemente bien para 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-20 15:22:58
Bumping @Tihamer y @joe-basirico. Función muy útil, no perfecto , pero bueno para la mayoría de los proyectos pequeños a medianos. Joe, he reescrito una implementación de tu código 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
Espero que alguien encuentre esto útil!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-14 06:24:02
Perl tiene módulo Lingua::Phonology::Syllable. Podrías intentar eso, o intentar buscar en su algoritmo. También vi algunos otros módulos más antiguos.
No entiendo por qué una expresión regular te da solo una cuenta de sílabas. Debería ser capaz de obtener las sílabas mismas usando paréntesis de captura. Suponiendo que usted puede construir una expresión regular que funciona, es decir.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-11 14:53:06
Hoy encontré esta implementación Java del algoritmo de partición de palabras de Frank Liang con patrón para inglés o alemán, que funciona bastante bien y está disponible en Maven Central.
Cueva: Es importante eliminar las últimas líneas de los archivos de patrón .tex
, porque de lo contrario esos archivos no se pueden cargar con la versión actual en Maven Central.
Para cargar y usar el hyphenator
, puede usar el siguiente fragmento de código Java. texTable
es el nombre de los archivos .tex
contiene los patrones necesarios. Esos archivos están disponibles en el sitio del proyecto github.
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;
}
Después, el Hyphenator
está listo para usar. Para detectar sílabas, la idea básica es dividir el término en los guiones proporcionados.
String hyphenedTerm = hyphenator.hyphenate(term);
String hyphens[] = hyphenedTerm.split("\u00AD");
int syllables = hyphens.length;
Necesita dividir en "\u00AD
", ya que la API no devuelve un "-"
normal.
Este enfoque supera la respuesta de Joe Basirico, ya que admite muchos idiomas diferentes y detecta la partición de palabras en alemán con mayor precisión.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-17 14:47:33
¿por Qué calcularlo? Cada diccionario en línea tiene esta información. http://dictionary.reference.com/browse/invisible in * vis·i * ble
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-20 02:44:51
Gracias @joe-basirico y @tihamer. He portado el código de @tihamer a Lua 5.1, 5.2 y luajit 2 ( lo más probable es que también se ejecute en otras versiones 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
Y algunas pruebas divertidas para confirmar que funciona (tanto como se supone que funciona):
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.")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-09 22:35:06
No pude encontrar una manera adecuada de contar sílabas, así que diseñé un método yo mismo.
Puede ver mi método aquí: https://stackoverflow.com/a/32784041/2734752
Uso una combinación de un diccionario y un método de algoritmo para contar sílabas.
Puedes ver mi biblioteca aquí: https://github.com/troywatson/Lawrence-Style-Checker
¡Acabo de probar mi algoritmo y tuve una tasa de ataque del 99.4%!
Lawrence lawrence = new Lawrence();
System.out.println(lawrence.getSyllable("hyphenation"));
System.out.println(lawrence.getSyllable("computer"));
Salida:
4
3
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:27
Me encontré con este mismo problema hace un rato.
Terminé usando el CMU Pronunciation Dictionary para búsquedas rápidas y precisas de la mayoría de las palabras. Para las palabras que no están en el diccionario, volví a un modelo de aprendizaje automático que es ~98% preciso en la predicción de conteos de sílabas.
Lo envolví todo en un módulo python fácil de usar aquí: https://github.com/repp/big-phoney
Instalar:
pip install big-phoney
Contar sílabas:
from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops') # --> 4
Si no estás usando Python y quieres probar el enfoque basado en el modelo ML, hice un bastante detallado escribir sobre cómo funciona el modelo de conteo de sílabas en Kaggle.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-02 19:56:12
Después de hacer muchas pruebas y probar paquetes de guiones también, escribí el mío basado en una serie de ejemplos. También probé los paquetes pyhyphen
y pyphen
que interactúan con diccionarios de guiones, pero producen el número incorrecto de sílabas en muchos casos. El paquete nltk
era simplemente demasiado lento para este caso de uso.
Mi implementación en Python es parte de una clase que escribí, y la rutina de conteo de sílabas se pega a continuación. Sobre-estima el número de sílabas a aunque todavía no he encontrado una buena manera de explicar los finales silenciosos de las palabras.
La función devuelve la proporción de sílabas por palabra tal como se usa para una puntuación de legibilidad de Flesch-Kincaid. El número no tiene que ser exacto, solo lo suficientemente cerca para una estimación.
En mi CPU i7 de 7a generación, esta función tomó 1.1-1.2 milisegundos para un texto de muestra de 759 palabras.
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-23 13:41:03
Usé jsoup para hacer esto una vez. Aquí hay un ejemplo de analizador de sílabas:
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-09 16:09:55