Détection de syllabes dans un mot

je dois trouver un moyen assez efficace de détecter les syllabes dans un mot. Par exemple,

Invisible -> en-vi-sib-le

il y a des règles de syllabification qui pourraient être utilisées:

V CV VC CVC CCV CCCV CVCC

* où V est une voyelle et C une consonne. Par exemple,

prononciation (5 Pro-nun-ci-a-tion; CV-CVC-CV-V-CVC)

j'ai essayé peu de méthodes, parmi qui utilisaient regex (qui aide seulement si vous voulez compter des syllabes) ou la définition de la règle codée dure (une approche de la force brute qui s'avère être très inefficace) et finalement en utilisant un automata d'état fini (qui n'a pas donné de résultat avec quelque chose 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 plus tard utilisé pour des applications de vérification orthographique (en utilisant des classificateurs bayésiens) et de synthèse textuelle.

j'apprécierais si l'on pouvait me donner des conseils sur une autre façon de résoudre ce problème en plus de mes approches précédentes.

je travaille en Java, mais n'importe quel conseil en C/C++, C#, Python, Perl... pourrait fonctionner pour moi.

121
demandé sur Kevin Brown 2009-01-01 20:08:41

15 réponses

lisez à propos de l'approche TeX à ce problème pour les besoins de la hyphénation. Surtout voir Frank Liang thèse Mot Hy-phen-a-tion par Com-mis-er . Son algorithme est très précis, et comprend ensuite un petit dictionnaire d'exceptions pour les cas où l'algorithme ne fonctionne pas.

104
répondu jason 2009-01-01 17:17:58

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

qui est à moins que vous êtes le type qui aime lire une thèse de 60 pages au lieu d'adapter le code librement disponible pour le problème non-unique. :)

41
répondu Sean 2017-04-19 00:07:37

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()]] 
35
répondu hoju 2010-12-21 23:52:31

j'essaie de résoudre ce problème pour un programme qui calculera le score 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 il devient raisonnablement proche. Il a encore des problèmes sur les mots compliqués comme invisible et le trait d'Union, mais je l'ai trouvé obtient dans le stade pour mes buts.

Il a l'avantage d'être facile mettre. J'ai trouvé que le "es" peut être syllabique ou pas. C'est un pari, mais j'ai décidé de supprimer l'es dans 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;
    }
16
répondu Joe Basirico 2012-02-16 21:53:02

il s'agit d'un problème particulièrement difficile qui n'est pas complètement résolu par l'algorithme D'hyphénation du LaTeX. Un bon résumé de quelques méthodes disponibles et les défis impliqués peut être trouvé dans le document Evaluating Automatic Syllabification Algorithms for English (Marchand, Adsett, and Damper 2007).

6
répondu Chris 2011-02-07 15:40:54

Merci Joe Basirico, pour partager votre mise en œuvre 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 très bien.

Voici votre code en Java, ainsi que des 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
5
répondu Tihamer 2014-09-20 15:22:58

Bumping @Tihamer et @joe-basirico. Fonction très utile, pas parfait , mais bon pour la plupart des projets de petite à moyenne. 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

Espère que quelqu'un trouve cette pratique!

5
répondu Tersosauros 2015-10-14 06:24:02

Perl a Lingua::Phonologie::Syllabe module. Vous pourriez essayer ça, ou chercher dans son algorithme. J'ai vu quelques autres anciens modules.

Je ne comprends pas pourquoi une expression régulière ne donne qu'un nombre de syllabes. Vous devriez être en mesure d'obtenir les syllabes elles-mêmes en utilisant les parenthèses de capture. En supposant que vous pouvez construire une expression régulière qui fonctionne, ce qui est.

4
répondu skiphoppy 2012-06-11 14:53:06

Aujourd'hui j'ai trouvé cette implémentation Java de L'algorithme d'hyphénation de Frank Liang avec 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 motif .tex , car autrement ces fichiers ne peuvent pas être chargés avec la version actuelle sur Maven Central.

Pour charger et utiliser le hyphenator , vous pouvez utiliser le suivant code Java snippet. texTable est le nom des fichiers .tex contenant les motifs nécessaires. Ces fichiers sont disponibles sur le site du projet 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;
    }

après le Hyphenator est prêt à l'emploi. Pour détecter les syllabes, l'idée de base est de diviser le terme par les traits d'Union fournis.

    String hyphenedTerm = hyphenator.hyphenate(term);

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

    int syllables = hyphens.length;

vous devez vous séparer sur "\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 les traits d'Union allemands plus précis.

4
répondu rzo 2016-02-17 14:47:33

pourquoi le calculer? Chaque dictionnaire en ligne a cette information. http://dictionary.reference.com/browse/invisible ·vis·i·ble

3
répondu Cerin 2010-02-20 02:44:51

Merci @joe-basirico et @tihamer. J'ai porté le code de @tihamer sur Lua 5.1, 5.2 et luajit 2 ( sera très probablement exécuté sur d'autres versions de lua ainsi que ):

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 qu'il fonctionne ( autant que c'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.")
2
répondu josefnpat 2015-09-09 22:35:06

Je n'ai pas pu trouver un moyen adéquat pour compter les syllabes, donc j'ai conçu une méthode moi-même.

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 consulter ma bibliothèque ici: https://github.com/troywatson/Lawrence-Style-Checker

I viens de tester mon algorithme et avait une de 99,4% taux de grève!

Lawrence lawrence = new Lawrence();

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

sortie:

4
3
2
répondu troy 2017-05-23 12:02:27

j'ai rencontré ce même numéro il y a peu.

j'ai fini par utiliser le CMU prononciation Dictionary pour la recherche rapide et précise de la plupart des mots. Pour les mots qui ne sont pas dans le dictionnaire, je suis retombé sur un modèle d'apprentissage automatique qui est ~98% précis pour prédire les nombres de syllabes.

j'ai tout emballé dans un module python facile à utiliser ici: https://github.com/repp/big-phoney

installer: pip install big-phoney

Count Syllabes:

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

si vous n'utilisez pas Python et que vous voulez essayer L'approche basée sur le modèle ML, j'ai fait un assez détaillé sur la façon dont le modèle de comptage de syllabes fonctionne sur Kaggle .

0
répondu Ryan Epp 2018-07-02 19:56:12

Après avoir fait beaucoup de tests et d'essayer de césure paquets ainsi, j'ai écrit ma propre fondée sur un certain nombre d'exemples. J'ai aussi essayé les paquets pyhyphen et pyphen qui sont en interface avec les dictionnaires de typographie, mais ils produisent le mauvais nombre de syllabes dans de nombreux cas. Le nltk paquet était tout simplement trop lent pour ce cas d'utilisation.

mon implémentation en Python fait partie d'un cours que j'ai écrit, et la routine de comptage de syllabes est collée dessous. Il surestime un peu le nombre de syllabes, car je n'ai toujours pas trouvé de bonne façon de tenir compte des fins muettes.

la fonction renvoie le rapport de syllabes par mot tel qu'il est utilisé pour un score de lisibilité Flesch-Kincaid. Le nombre n'a pas besoin d'être exact, juste assez proche pour une estimation.

sur mon CPU i7 de 7ème génération, cette fonction a pris 1,1-1,2 millisecondes pour un exemple de texte de 759 mots.

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)
0
répondu Jadzia626 2018-09-23 13:41:03

j'ai utilisé jsoup pour faire ça une fois. Voici un exemple d'analyseur de syllabes:

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;
    }
-1
répondu Itamar Fiorino 2018-01-09 16:09:55