Обнаружение слогов словом

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

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

Существуют правила силлабификации, которые можно использовать:

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

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

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

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

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

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

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

Ответ 1

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

Ответ 2

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

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

Ответ 3

Вот решение, использующее 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()]] 

Ответ 4

Я пытаюсь решить эту проблему для программы, которая будет вычислять оценку чтения 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 _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;
    }

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Вот ваш код на 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 _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));
}

Результат был таким, как ожидалось (он работает достаточно хорошо для 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

Ответ 8

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

Пещера: важно удалить последние строки файлов шаблонов .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 не возвращает нормальный "-".

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

Ответ 10

Bumping @Tihamer и @joe-basirico. Очень полезная функция, а не совершенная, но хороша для большинства малых и средних проектов. Джо, я переписал реализацию вашего кода в 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 "usually" silent (?)
        numVowels-=1
    elif len(word) > 1 and word[-1:] == "e":    #remove silent e
        numVowels-=1
    return numVowels

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

Ответ 11

Я не мог найти адекватный способ подсчета слогов, поэтому сам разработал метод.

Вы можете просмотреть мой метод здесь: fooobar.com/questions/54315/...

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

Вы можете просмотреть мою библиотеку здесь: https://github.com/troywatson/Lawrence-Style-Checker

Я только что протестировал свой алгоритм и имел показатель забастовки 99,4%!

Lawrence lawrence = new Lawrence();

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

Вывод:

4
3

Ответ 12

Спасибо @joe-basirico и @tihamer. Я портировал код @tihamer в Lua 5.1, 5.2 и luajit 2 (скорее всего, будет работать и на других версиях 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

И некоторые забавные тесты, чтобы подтвердить, что это работает (насколько это возможно):

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