Detectando sílabas em uma palavra
-
03-07-2019 - |
Pergunta
Eu preciso encontrar uma maneira bastante eficiente para detectar sílabas em uma palavra. Por exemplo.,
Invisible -> in-vi-sib-le
Existem algumas regras silabificação que poderiam ser usados:
V cv VC CVC CCV CC CV CVCC
* em que V é uma vogal e C é uma consoante. Por exemplo,
Pronunciada (5 Pro-nun-ci-a-ção; CV-CVC-CV-V-CVC)
Eu tentei alguns métodos, entre os quais estavam usando regex (que ajuda só se você quiser contar sílabas) ou definição de regras codificado duro (a abordagem de força bruta que prova ser muito ineficiente) e, finalmente, usando um autômato de estados finitos (que não resultou com qualquer coisa útil).
O objetivo da minha candidatura é criar um dicionário de todas as sílabas em um determinado idioma. Este dicionário será posteriormente utilizado para aplicações de verificação ortográfica (usando classificadores Bayesianos) e texto para síntese de voz.
Eu apreciaria se alguém poderia me dar dicas sobre uma maneira alternativa para resolver este problema além das minhas abordagens anteriores.
Eu trabalho em Java, mas qualquer ponta em C / C ++, C #, Python, Perl ... iria trabalhar para mim.
Solução
Leia sobre a abordagem TeX para este problema, para efeitos de hifenização. Especialmente ver tese de dissertação Word Hy-phen-a-ção de Frank Liang pela Comis- colocá-er . Seu algoritmo é muito preciso, e, em seguida, inclui um pequeno exceções dicionário para casos em que o algoritmo não funciona.
Outras dicas
eu tropecei através desta página procurando a mesma coisa, e encontrou algumas implementações do papel Liang aqui: https://github.com/mnater/hyphenator
Isso é menos que você é do tipo que gosta de ler uma tese de 60 páginas, em vez de adaptar o código livremente disponível para problema não exclusivo. :)
Aqui está uma solução 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()]]
Eu estou tentando resolver este problema para um programa que irá calcular a pontuação Flesch-Kincaid e Flesch leitura de um bloco de texto. Meu algoritmo usa o que eu encontrei neste site: http://www.howmanysyllables.com/howtocountsyllables.html e torna-se razoavelmente perto. Ele ainda tem dificuldade em palavras complicadas como invisível e hifenização, mas eu descobri que fica no estádio para os meus propósitos.
Ele tem a vantagem de ser fácil de implementar. Eu encontrei os "es" pode ser silábico ou não. É uma aposta, mas eu decidi remover os es no meu 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;
}
Este é um problema particularmente difícil que não está completamente resolvido pelo algoritmo LaTeX hifenização. Um resumo bem de alguns métodos disponíveis e os desafios envolvidos podem ser encontrados no jornal avaliando automática silabificação Algoritmos para Inglês (Marchand, Adsett e Damper 2007).
Graças Joe Basirico, por compartilhar sua implementação rápida e suja em C #. Eu usei as grandes bibliotecas, e eles trabalham, mas eles são geralmente um pouco lento, e para projetos rápidos, o seu método funciona bem.
Aqui está seu código em Java, juntamente com casos de teste:
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));
}
O resultado foi como esperado (ele funciona bem o suficiente 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
Bumping @Tihamer e @ joe-basirico. função muito útil, não perfeito , mas bom para a maioria dos projetos de pequeno a médio porte. Joe, eu re-escrito uma implementação de seu código em 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
Hope alguém acha isso! Útil
Perl tem Lingua :: Fonologia :: Syllable módulo. Você pode tentar isso, ou tente olhar em seu algoritmo. Eu vi alguns outros módulos mais velhos lá, também.
Eu não entendo por que uma expressão regular dá-lhe apenas uma contagem de sílabas. Você deve ser capaz de obter as sílabas-se usando parênteses de captura. Assumindo que você pode construir uma expressão regular que funciona, o que é.
Hoje eu encontrei este implementação Java de algorithmn hifenização de Frank Liang com padrão de Inglês ou Alemão, que obras muito bem e está disponível no Maven Central.
Cave:. É importante para remover as últimas linhas dos arquivos de padrões .tex
, porque senão esses arquivos não podem ser carregados com a versão atual no Maven Central
Para carregar e usar o hyphenator
, você pode usar o seguinte trecho de código Java. texTable
é o nome dos arquivos .tex
contendo os padrões necessários. Esses arquivos estão disponíveis no site github do projeto.
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;
}
Depois, o Hyphenator
está pronto para usar. Para detectar sílabas, a idéia básica é dividir o prazo para os hífens prestados.
String hyphenedTerm = hyphenator.hyphenate(term);
String hyphens[] = hyphenedTerm.split("\u00AD");
int syllables = hyphens.length;
Você precisa dividir em "\u00AD
", já que a API não retorna um "-"
normal.
Esta abordagem supera a resposta de Joe Basirico, uma vez que suporta várias línguas diferentes e detecta hifenização alemão mais preciso.
Por que calculá-lo? Cada dicionário on-line tem esta informação. http://dictionary.reference.com/browse/invisible no · vis · i · ble
Obrigado @ joe-basirico e @tihamer. Eu tenho o código portado @ de Tihamér para Lua 5.1, 5.2 e luajit 2 ( provavelmente irá rodar em outras versões de lua assim ):
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
E alguns testes divertidos para confirmar que funciona (, tanto quanto é suposto ):
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.")
Eu não poderia encontrar uma forma adequada de contar sílabas, assim que eu projetei um método mim.
Você pode ver o meu método aqui: https://stackoverflow.com/a/32784041/2734752
Eu uso uma combinação de um método de dicionário e algoritmo para contar sílabas.
Você pode ver a minha biblioteca aqui: https://github.com/troywatson/Lawrence- style-Checker
Eu só testei meu algoritmo e tinha uma taxa de greve de 99,4%!
Lawrence lawrence = new Lawrence();
System.out.println(lawrence.getSyllable("hyphenation"));
System.out.println(lawrence.getSyllable("computer"));
Output:
4
3
Eu corri para exata esse mesmo problema há pouco tempo.
Acabei usando a CMU Dicionário de Pronúncia para uma rápida e pesquisas precisas da maioria das palavras. Para palavras não está no dicionário, eu caí de volta para um modelo de aprendizagem máquina que de ~ 98% precisos na previsão de contagem de sílabas.
Eu passei a coisa toda em um módulo python easy-to-use aqui: https: // github.com/repp/big-phoney
Instale:
pip install big-phoney
Conde sílabas:
from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops') # --> 4
Se você não está usando Python e você quiser tentar a abordagem baseada em ML-modelo, fiz um muito detalhado escrever-se sobre a forma como a sílaba obras modelo contando com Kaggle .
Depois de fazer um monte de testes e experimentar pacotes de hifenização bem, eu escrevi o meu próprio com base em uma série de exemplos. Eu também tentei os pacotes pyhyphen
e pyphen
que interfaces com dicionários de hifenização, mas eles produzem o número errado de sílabas em muitos casos. O pacote nltk
era simplesmente demasiado lento para este caso de uso.
Meu implementação em Python é parte de uma classe que eu escrevi, e a rotina de contagem sílaba é colado abaixo. É over-estima o número de sílabas um pouco como eu ainda não encontrei uma boa maneira de conta para terminações de palavras silenciosas.
A função retorna a razão de sílabas por palavra, uma vez que é usado para uma pontuação de legibilidade Flesch-Kincaid. O número não tem que ser exato, apenas perto o suficiente para uma estimativa.
No meu CPU i7 7ª geração, esta função tomou 1.1-1.2 milissegundos para um texto de exemplo 759 palavra.
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)
Eu costumava jsoup fazer isso uma vez. Aqui está um analisador sílaba amostra:
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;
}