如何用正则表达式和Java计算文本中的音节

我有文本作为String ,需要计算每个单词的音节数。 我试图将所有文本拆分成单词数组,而不是单独处理每个单词。 我使用正则表达式。 但是音节的模式不能正常工作。 请建议如何更改它以计算正确的音节数。 我的初始代码。

 public int getNumSyllables() { String[] words = getText().toLowerCase().split("[a-zA-Z]+"); int count=0; List  tokens = new ArrayList(); for(String word: words){ tokens = Arrays.asList(word.split("[bcdfghjklmnpqrstvwxyz]*[aeiou]+[bcdfghjklmnpqrstvwxyz]*")); count+= tokens.size(); } return count; } 

这个问题来自加州大学圣地亚哥分校的Java课程,对吗?

我认为你应该为这个问题提供足够的信息,这样就不会让想要提供帮助的人感到困惑。 在这里,我有自己的解决方案,已经过本地计划的测试案例,以及UCSD的OJ测试。

你错过了关于这个问题中音节定义的一些重要信息。 实际上我认为这个问题的关键是你应该如何处理e 例如,假设有te的组合。 如果你把te放在一个单词的中间,当然它应该算作一个音节; 然而,如果它在一个单词的最后, e应该被认为是英语中的silent e ,所以它不应该被认为是一个音节。

而已。 我想用一些伪代码写下我的想法:

  if(last character is e) { if(it is silent e at the end of this word) { remove the silent e; count the rest part as regular; } else { count++; } else { count it as regular; } } 

您可能会发现我不仅使用正则表达式来处理此问题。 其实我已经考虑过了:这个问题真的只能用正则表达式来完成吗? 我的回答是:不,我不这么认为。 至少现在,根据UCSD给我们的知识,这样做太难了。 正则表达式是一个function强大的工具,它可以非常快速地映射所需的字符。 然而正则表达式缺少一些function。 再以te为例,当正面对着像teate这样的单词时,正则表达式将无法三思而后teate (我只是举例说明这个词)。 如果我们的正则表达式模式将第一个te计为音节,那么为什么最后一个不是?

与此同时,加州大学圣地亚哥分校实际上已经在作业文件中谈到过:

如果你发现自己正在进行心理体操以提出单个正则表达式直接计算音节,那通常表明有一个更简单的解决方案(提示:考虑循环字符 – 请参阅下面的下一个提示)。 仅仅因为一段代码(例如正则表达式)更短并不意味着它总是更好。

这里的提示是,你应该将这个问题与一些循环结合起来,与正则表达式结合起来。

好的,我现在应该最终显示我的代码:

 protected int countSyllables(String word) { // TODO: Implement this method so that you can call it from the // getNumSyllables method in BasicDocument (module 1) and // EfficientDocument (module 2). int count = 0; word = word.toLowerCase(); if (word.charAt(word.length()-1) == 'e') { if (silente(word)){ String newword = word.substring(0, word.length()-1); count = count + countit(newword); } else { count++; } } else { count = count + countit(word); } return count; } private int countit(String word) { int count = 0; Pattern splitter = Pattern.compile("[^aeiouy]*[aeiouy]+"); Matcher m = splitter.matcher(word); while (m.find()) { count++; } return count; } private boolean silente(String word) { word = word.substring(0, word.length()-1); Pattern yup = Pattern.compile("[aeiouy]"); Matcher m = yup.matcher(word); if (m.find()) { return true; } else return false; } 

您可能会发现除了给定方法countSyllables ,我还创建了另外两个方法countitsilentecountit用于计算单词中的音节, silente试图弄清楚这个单词是以无声e结尾。 而且还应该注意到not silent e的定义。 例如,应该考虑not silent e ,而ate被认为是silent e

以下是我的代码已经通过测试的状态,来自本地测试用例和来自UCSD的OJ:

来自本地测试案例

并从OJ测试结果:

来自Coursera OJ

PS:直接使用像[^ aeiouy]这样的东西应该没问题,因为在我们调用这个方法之前会解析这个单词。 还需要更改为小写,这将节省大量处理大写的工作。 我们想要的只是音节的数量。 谈到数字,一种优雅的方法是将count定义为static,因此private方法可以直接使用count++ inside。 但现在没关系。

如果你仍然没有得到这个问题的方法,请随时与我联系:)

这为您提供了一个单词中的多个音节元音:

 public int getNumVowels(String word) { String regexp = "[bcdfghjklmnpqrstvwxz]*[aeiouy]+[bcdfghjklmnpqrstvwxz]*"; Pattern p = Pattern.compile(regexp); Matcher m = p.matcher(word.toLowerCase()); int count = 0; while (m.find()) { count++; } return count; } 

您可以在字符串数组中的每个单词上调用它:

  String[] words = getText().split("\\s+"); for (String word : words ) { System.out.println("Word: " + word + ", vowels: " + getNumVowels(word)); } 

更新:正如freerunner所说,计算音节数量比计算元音更复杂。 需要考虑ouuioo ,最终的沉默和其他可能的组合。 由于我不是母语为英语的人,我不确定正确的算法是什么。

使用user5500105的概念,我开发了以下方法来计算单词中的音节数。 规则是:

  • 连续元音被计为1个音节。 例如。 “ae”“ou”是1个音节

  • Y被认为是元音

  • 如果e是唯一的元音,则最后的e被计为音节:例如:“the”是一个音节,因为末尾的“e”是唯一的元音,而“there”也是1个音节,因为“e”位于结束,这个词中还有另一个元音。

     public int countSyllables(String word) { ArrayList tokens = new ArrayList(); String regexp = "[bcdfghjklmnpqrstvwxz]*[aeiouy]+[bcdfghjklmnpqrstvwxz]*"; Pattern p = Pattern.compile(regexp); Matcher m = p.matcher(word.toLowerCase()); while (m.find()) { tokens.add(m.group()); } //check if e is at last and e is not the only vowel or not if( tokens.size() > 1 && tokens.get(tokens.size()-1).equals("e") ) return tokens.size()-1; // e is at last and not the only vowel so total syllable -1 return tokens.size(); 

    }

我就是这样做的。 这是我能想出的一个简单的算法。

  public static int syllables(String s) { final Pattern p = Pattern.compile("([ayeiou]+)"); final String lowerCase = s.toLowerCase(); final Matcher m = p.matcher(lowerCase); int count = 0; while (m.find()) count++; if (lowerCase.endsWith("e")) count--; return count < 0 ? 1 : count; } 

我将它与soundex函数结合使用来确定单词是否相似。 音节计数提高了soundexfunction的准确性。

注意:这严格用于计算单词中的音节。 我假设您可以使用java.util.StringTokenizer类的内容解析输入的单词。

你的路线

 String[] words = getText().toLowerCase().split("[a-zA-Z]+"); 

分裂ON单词,只返回单词之间的空格! 您希望拆分单词之间的空格,如下所示:

 String[] words = getText().toLowerCase().split("\\s+"); 

你可以这样做:

 public int getNumSyllables() { return getSyllables(getTokens("[a-zA-Z]+")); } protected List getWordTokens(String word,String pattern) { ArrayList tokens = new ArrayList(); Pattern tokSplitter = Pattern.compile(pattern); Matcher m = tokSplitter.matcher(word); while (m.find()) { tokens.add(m.group()); } return tokens; } private int getSyllables(List tokens) { int count=0; for(String word : tokens) if(word.toLowerCase().endsWith("e") && getWordTokens(word.toLowerCase().substring(0, word.length()-1), "[aeiouy]+").size() > 0) count+=getWordTokens(word.toLowerCase().substring(0, word.length()-1), "[aeiouy]+").size(); else count+=getWordTokens(word.toLowerCase(), "[aeiouy]+").size(); return count; } 

我单独计算,然后根据以e结尾的单词拆分文本。
然后计算音节,这是我的实现:

 int syllables = 0; word = word.toLowerCase(); if(word.contains("the ")){ syllables ++; } String[] split = word.split("e!$|e[?]$|e,|e |e[),]|e$"); ArrayList tokens = new ArrayList(); Pattern tokSplitter = Pattern.compile("[aeiouy]+"); for (int i = 0; i < split.length; i++) { String s = split[i]; Matcher m = tokSplitter.matcher(s); while (m.find()) { tokens.add(m.group()); } } syllables += tokens.size(); 

我已经测试了所有测试用例都通过了。

您正在使用方法拆分错误。 该方法接收分离器。 需要写这样的东西:

 String[] words = getText().toLowerCase().split(" "); 

但是如果你想计算音节的数量,就足以计算元音的数量:

 String input = "text"; Set vowel = new HashSet<>(); vowel.add('a'); vowel.add('e'); vowel.add('i'); vowel.add('o'); vowel.add('u'); int count = 0; for (char c : input.toLowerCase().toCharArray()) { if (vowel.contains(c)){ count++; } } System.out.println("count = " + count);