Обнаружение слогов в слове - PullRequest
126 голосов
/ 01 января 2009

Мне нужно найти достаточно эффективный способ обнаружения слогов в слове. Например.,

Невидимый -> in-vi-sib-le

Существует несколько правил слоговой логики:

V резюме VC CVC ККТ CCCV CVCC

* где V - гласный, а C - согласный. Например,

Произношение (5 Pro-nun-ci-a-aion; CV-CVC-CV-V-CVC)

Я пробовал несколько методов, среди которых использовалось регулярное выражение (которое помогает, только если вы хотите сосчитать слоги) или жестко закодированное определение правила (метод грубой силы, который оказывается очень неэффективным) и, наконец, использование автоматов конечного состояния (что не принесло ничего полезного).

Цель моего приложения - создать словарь всех слогов на данном языке. Этот словарь позже будет использоваться для приложений проверки правописания (с использованием байесовских классификаторов) и синтеза текста в речь.

Я был бы признателен, если бы вы могли дать мне советы по альтернативному способу решения этой проблемы помимо моих предыдущих подходов.

Я работаю в Java, но любой совет в C / C ++, C #, Python, Perl ... будет работать для меня.

Ответы [ 15 ]

112 голосов
/ 01 января 2009

Читайте о подходе TeX к этой проблеме в целях переноса слов. Особенно см. Диссертацию Фрэнка Ляна Слово Hy-phen-a-ation от Com-put-er . Его алгоритм очень точен, а затем содержит небольшой словарь исключений для случаев, когда алгоритм не работает.

44 голосов
/ 02 января 2009

Я наткнулся на эту страницу в поисках того же самого и нашел несколько реализаций статьи Ляна здесь: https://github.com/mnater/hyphenator

Это если вы не из тех, кто любит читать тезисы на 60 страниц вместо адаптации свободно доступного кода для решения неуникальной проблемы. :)

39 голосов
/ 05 ноября 2010

Вот решение, использующее 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()]] 
18 голосов
/ 11 апреля 2011

Я пытаюсь решить эту проблему для программы, которая будет вычислять баллы чтения flesch-kincaid и flesch для блока текста. Мой алгоритм использует то, что я нашел на этом сайте: http://www.howmanysyllables.com/howtocountsyllables.html, и он достаточно близок. У него все еще есть проблемы со сложными словами, такими как «невидимка» и «перенос», но я обнаружил, что он попадает в поле для моих целей.

Преимущество в простоте реализации. Я обнаружил, что «es» может быть либо слоговым, либо нет. Это игра, но я решил убрать es из моего алгоритма.

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;
    }
7 голосов
/ 07 февраля 2011

Это особенно сложная проблема, которая не полностью решается алгоритмом переноса LaTeX. Хорошее резюме некоторых доступных методов и связанных с ними проблем можно найти в документе Оценка алгоритмов автоматической силлабификации для английского языка (Marchand, Adsett, и Damper 2007).

5 голосов
/ 14 октября 2015

натыкаясь @Tihamer и @ joe-basirico. Очень полезная функция, не perfect , но хорошая для большинства проектов от малого до среднего. Джо, я переписал реализацию вашего кода на 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

Надеюсь, кто-нибудь найдет это полезным!

5 голосов
/ 20 сентября 2014

Спасибо, Джо Басирико, за то, что поделился своей быстрой и грязной реализацией в C #. Я использовал большие библиотеки, и они работают, но обычно они немного медленные, и для быстрых проектов ваш метод работает нормально.

Вот ваш код на Java вместе с тестовыми примерами:

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

Результат был ожидаемым (он достаточно хорош для Флеша-Кинкейда):

txt=what countSyllables=1
txt=super countSyllables=2
txt=Maryland countSyllables=3
txt=American countSyllables=3
txt=disenfranchized countSyllables=5
txt=Sophia countSyllables=2
4 голосов
/ 17 февраля 2016

Сегодня я обнаружил эту Java-реализацию алгоритма переноса слов Фрэнка Ляна с шаблоном для английского или немецкого языка, которая работает довольно хорошо и доступна в Maven Central.

Cave: важно удалить последние строки файлов шаблонов .tex, поскольку в противном случае эти файлы не могут быть загружены с текущей версией в Maven Central.

Чтобы загрузить и использовать hyphenator, вы можете использовать следующий фрагмент кода Java. texTable - это имя файла .tex, содержащего необходимые шаблоны. Эти файлы доступны на сайте 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;
    }

После этого Hyphenator готов к использованию. Для обнаружения слогов основная идея состоит в том, чтобы разделить термин на предоставленные дефисы.

    String hyphenedTerm = hyphenator.hyphenate(term);

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

    int syllables = hyphens.length;

Вам нужно разделить на "\u00AD ", поскольку API не возвращает нормальный "-".

Этот подход превосходит ответ Джо Басирико, поскольку он поддерживает множество различных языков и более точно определяет перенос слов в немецком языке.

4 голосов
/ 01 января 2009

Perl имеет модуль Lingua :: Phonology :: Syllable . Вы можете попробовать это или попытаться изучить его алгоритм. Я также видел там несколько других старых модулей.

Я не понимаю, почему регулярное выражение дает вам только количество слогов. Вы должны быть в состоянии получить сами слоги, используя круглые скобки. Предполагая, что вы можете создать регулярное выражение, которое работает, то есть.

3 голосов
/ 20 февраля 2010

Зачем это вычислять? Каждый онлайн-словарь имеет эту информацию. http://dictionary.reference.com/browse/invisible в · визави · я · BLE

...