Domanda

Devo trovare un modo abbastanza efficace per rilevare sillabe in una parola. Per es.,

Invisibile - > in-vi-sib-le

Esistono alcune regole di sillabazione che potrebbero essere utilizzate:

V CV VC CVC CCV CCCV CVCC

* dove V è una vocale e C è una consonante. Ad esempio,

Pronuncia (5 Pro-nun-ci-a-zione; CV-CVC-CV-V-CVC)

Ho provato alcuni metodi, tra cui l'utilizzo di regex (che aiuta solo se si desidera contare le sillabe) o la definizione di una regola codificata (un approccio a forza bruta che si rivela molto inefficiente) e infine l'utilizzo di automi a stati finiti (che non ha prodotto risultati utili).

Lo scopo della mia applicazione è quello di creare un dizionario di tutte le sillabe in una determinata lingua. Questo dizionario verrà successivamente utilizzato per le applicazioni di controllo ortografico (utilizzando classificatori bayesiani) e per la sintesi vocale.

Gradirei se uno potesse darmi consigli su un modo alternativo per risolvere questo problema oltre ai miei precedenti approcci.

Lavoro in Java, ma qualsiasi suggerimento in C / C ++, C #, Python, Perl ... funzionerebbe per me.

È stato utile?

Soluzione

Leggi l'approccio TeX a questo problema ai fini della sillabazione. In particolare, vedi la tesi di tesi di Frank Liang Word Hy-phen-a-zione di Com- put-ER . Il suo algoritmo è molto preciso e include quindi un piccolo dizionario delle eccezioni per i casi in cui l'algoritmo non funziona.

Altri suggerimenti

Mi sono imbattuto in questa pagina cercando la stessa cosa, e ho trovato alcune implementazioni del documento Liang qui: https://github.com/mnater/hyphenator

Questo a meno che tu non sia il tipo a cui piace leggere una tesi di 60 pagine invece di adattare il codice liberamente disponibile per problemi non univoci. :)

Ecco una soluzione che utilizza 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()]] 

Sto cercando di affrontare questo problema per un programma che calcolerà il punteggio di lettura di flesch-kincaid e flesch di un blocco di testo. Il mio algoritmo utilizza ciò che ho trovato su questo sito Web: http://www.howmanysyllables.com/howtocountsyllables.html e si avvicina ragionevolmente. Ha ancora problemi con parole complicate come invisibile e sillabazione, ma ho scoperto che entra nel campo da baseball per i miei scopi.

Ha il vantaggio di essere facile da implementare. Ho trovato il "es" può essere sillabico o no. È una scommessa, ma ho deciso di rimuovere gli es nel mio 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;
    }

Questo è un problema particolarmente difficile che non è stato completamente risolto dall'algoritmo di sillabazione LaTeX. Un buon riassunto di alcuni metodi disponibili e delle sfide in questione è disponibile nel documento Valutazione degli algoritmi di sillabazione automatica per l'inglese (Marchand, Adsett e Damper 2007).

Grazie Joe Basirico, per aver condiviso la tua implementazione veloce e sporca in C #. Ho usato le grandi librerie e funzionano, ma di solito sono un po 'lente e per progetti veloci il tuo metodo funziona bene.

Ecco il tuo codice in Java, insieme ai casi di 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));
}

Il risultato è stato come previsto (funziona abbastanza bene per 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. Funzione molto utile, non perfetta , ma buona per la maggior parte dei progetti medio-piccoli. Joe, ho riscritto un'implementazione del tuo codice in 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

Spero che qualcuno lo trovi utile!

Perl ha Lingua :: Phonology :: Syllable modulo. Potresti provarlo o provare a esaminare il suo algoritmo. Ho visto anche alcuni altri moduli più vecchi lì.

Non capisco perché un'espressione regolare ti dia solo un conteggio di sillabe. Dovresti essere in grado di ottenere le sillabe stesse usando le parentesi di cattura. Supponendo che tu possa costruire un'espressione regolare che funzioni, cioè.

Oggi ho trovato questo implementazione Java dell'algoritmo di sillabazione di Frank Liang con pattern per inglese o tedesco, che funziona abbastanza bene ed è disponibile su Maven Central.

Cave: è importante rimuovere le ultime righe dei file di pattern .tex , perché altrimenti questi file non possono essere caricati con la versione corrente su Maven Central.

Per caricare e utilizzare il sillabatore , è possibile utilizzare il seguente frammento di codice Java. texTable è il nome dei file .tex contenenti gli schemi necessari. Tali file sono disponibili sul sito github del progetto.

 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;
    }

Successivamente Hyphenator è pronto per l'uso. Per rilevare sillabe, l'idea di base è quella di dividere il termine nei trattini forniti.

    String hyphenedTerm = hyphenator.hyphenate(term);

    String hyphens[] = hyphenedTerm.split("\u00AD");

    int syllables = hyphens.length;

Devi dividere " \ u00AD " ;, poiché l'API non restituisce un normale " - " .

Questo approccio supera la risposta di Joe Basirico, poiché supporta molte lingue diverse e rileva una sillabazione tedesca più accurata.

Perché calcolarlo? Ogni dizionario online ha queste informazioni. http://dictionary.reference.com/browse/invisible in · vis · I · ble

Grazie @ joe-basirico e @tihamer. Ho portato il codice di @ tihamer su Lua 5.1, 5.2 e luajit 2 ( molto probabilmente verrà eseguito anche su altre versioni di 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

E alcuni test divertenti per confermare che funziona ( tanto quanto dovrebbe ):

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.")

Non sono riuscito a trovare un modo adeguato per contare le sillabe, quindi ho progettato un metodo da solo.

Puoi visualizzare il mio metodo qui: https://stackoverflow.com/a/32784041/2734752

Uso una combinazione di un dizionario e un metodo algoritmico per contare le sillabe.

Puoi visualizzare la mia biblioteca qui: https://github.com/troywatson/Lawrence- Style-Checker

Ho appena testato il mio algoritmo e ho avuto un tasso di strike del 99,4%!

Lawrence lawrence = new Lawrence();

System.out.println(lawrence.getSyllable("hyphenation"));
System.out.println(lawrence.getSyllable("computer"));

Output:

4
3

Ho incontrato questo stesso identico problema poco fa.

Ho finito con il Dizionario di pronuncia CMU per una rapida e ricerche accurate della maggior parte delle parole. Per le parole che non sono nel dizionario, sono tornato a un modello di apprendimento automatico con una precisione del 98% circa nella previsione del conteggio delle sillabe.

Ho racchiuso il tutto in un modulo Python facile da usare qui: https: // github.com/repp/big-phoney

Installare: pip installa big-phoney

Conta sillabe:

from big_phoney import BigPhoney
phoney = BigPhoney()
phoney.count_syllables('triceratops')  # --> 4

Se non stai usando Python e vuoi provare l'approccio basato sul modello ML, ho fatto un < scrivi su come funziona il modello di conteggio delle sillabe su Kaggle .

Dopo aver fatto molti test e provato anche i pacchetti di sillabazione, ho scritto il mio sulla base di numerosi esempi. Ho anche provato i pacchetti pyhyphen e pyphen che si interfacciano con i dizionari di sillabazione, ma in molti casi producono un numero errato di sillabe. Il pacchetto nltk era semplicemente troppo lento per questo caso d'uso.

La mia implementazione in Python fa parte di una classe che ho scritto e la routine di conteggio delle sillabe è incollata di seguito. Sovra-stima il numero di sillabe un po 'perché non ho ancora trovato un buon modo per spiegare la fine delle parole silenziose.

La funzione restituisce il rapporto tra sillabe per parola poiché viene utilizzata per un punteggio di leggibilità di Flesch-Kincaid. Il numero non deve essere esatto, abbastanza vicino per un preventivo.

Sulla mia CPU i7 di settima generazione, questa funzione ha richiesto 1,1-1,2 millisecondi per un testo di esempio di 759 parole.

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)

Ho usato jsoup per farlo una volta. Ecco un parser di sillabe di esempio:

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;
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top