用一个词来检测音节
我需要find一个相当有效的方法来检测一个字的音节。 例如,
不可见 – > in-vi-sib-le
有一些可以使用的音节规则:
V CV VC CVC CCV CCCV CVCC
* V是元音,C是辅音。 例如,
发音(5 Pro-nun-ci-tion; CV-CVC-CV-V-CVC)
我已经尝试了几种方法,其中使用正则expression式(这有助于只计算音节)或硬编码的规则定义(一种被certificate是非常低效的蛮力方法),最后使用有限状态自动机不带任何有用的结果)。
我的应用程序的目的是创build一个给定语言的所有音节字典。 此字典稍后将用于拼写检查应用程序(使用贝叶斯分类器)和文本到语音合成。
我很感激,如果能给我一个替代的方法来解决这个问题,除了我以前的做法。
我在Java中工作,但是C / C ++,C#,Python,Perl中的任何提示都适用于我。
为了连字的目的,阅读关于TeX的这个问题的方法。 尤其是参见Frank Liang的论文“ Com-put-er”的“Hy-phen-a-tion” 。 他的algorithm非常准确,然后在algorithm不起作用的情况下包含一个小例外字典。
我偶然发现了这个页面,寻找相同的东西,并在这里find了一些Liang论文的实现: https : //github.com/mnater/hyphenator
那就是除非你喜欢阅读60页的论文,而不是为了解决非唯一性问题而自由使用代码。 🙂
这是一个使用NLTK的解决scheme:
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()]]
我试图解决这个问题,将计算一块文本的flesch-kincaid和flesch阅读分数的程序。 我的algorithm使用我在这个网站上find的: http : //www.howmanysyllables.com/howtocountsyllables.html ,它相当接近。 它在复杂的词汇中仍然有麻烦,比如看不见的和连字符,但是我发现它是为了我的目的而进入的。
它具有容易实施的好处。 我发现“es”可以是音节或不是。 这是一场赌博,但我决定删除algorithm中的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; }
这是LaTeX连字algorithm没有完全解决的一个特别难的问题。 有关可用的一些方法和所面临的挑战的一个很好的总结可以在评估英语的自动音节化algorithm (Marchand,Adsett和Damper 2007)中find。
Perl有Lingua :: Phonology :: Syllable模块。 你可以试试,或者试着研究它的algorithm。 我也看到了其他一些老的模块。
我不明白为什么一个正则expression式只给你一个音节数。 您应该能够使用捕捉括号自己得到音节。 假设你可以构造一个正则expression式,也就是说。
感谢Joe Basirico,分享你在C#中快速和肮脏的实现。 我已经使用了大型图书馆,他们工作,但他们通常有点慢,而且对于快速项目,你的方法工作正常。
以下是Java中的代码以及testing用例:
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)); }
结果如预期的那样(对于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
今天,我发现这个 Java Frank Liang的连字algorithm的实现与英文或德文的模式,工作得很好,可在Maven中心。
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不会返回一个正常的"-"
。
这种方法胜过了Joe Basirico的答案,因为它支持许多不同的语言,并且更精确地检测德语连字符号。
为什么要计算它? 每个在线字典都有这个信息。 http://dictionary.reference.com/browse/invisible in vis·i·ble
颠簸@Tihamer和@ joe-basirico。 非常有用的function,不完美 ,但对大多数中小型项目很好。 Joe,我用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
希望有人认为这有用!
我找不到一个合适的方法来计算音节,所以我自己devise了一个方法。
你可以在这里查看我的方法: https : //stackoverflow.com/a/32784041/2734752
我使用字典和algorithm方法的组合计算音节。
您可以在这里查看我的图书馆: https : //github.com/troywatson/Lawrence-Style-Checker
我刚刚testing了我的algorithm,并有99.4%的打击率!
Lawrence lawrence = new Lawrence(); System.out.println(lawrence.getSyllable("hyphenation")); System.out.println(lawrence.getSyllable("computer"));
输出:
4 3
谢谢你@ 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
一些有趣的testing,以确认它的工作原理( 尽可能多 ):
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.")