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.

Foi útil?

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;
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top