正则表达式:了解音节计数器代码

RegEx: Understanding Syllable Counter Code

我在自己的代码中使用了 Dylan's question on here regarding JavaScript syllable counting, and more specifically artfulhacker's answer,无论我输入哪个单字串或多字串,该函数始终能够正确计算音节数。

我对 RegEx 的经验有限,并且没有足够的先验知识来破译以下代码中到底发生了什么,而无需任何帮助。我不是一个乐于让我从某个地方提取的代码在我不知道 如何 的情况下工作的人。有人能够清楚地说明下面 new_count(word) 函数中发生的事情并帮助我破译 RegEx 的使用以及该函数如何正确计算音节吗?许多

function new_count(word) {
  word = word.toLowerCase();                                     //word.downcase!
  if(word.length <= 3) { return 1; }                             //return 1 if word.length <= 3
  word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');   //word.sub!(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '')
  word = word.replace(/^y/, '');                                 //word.sub!(/^y/, '')
  return word.match(/[aeiouy]{1,2}/g).length;                    //word.scan(/[aeiouy]{1,2}/).size
}
/(?:[^laeiouy]es|ed|[^laeiouy]e)$/

匹配三个可能的子字符串:除 'l' 以外的字母或后跟 'es' 的元音字母(如 "res" 或 "tes"); 'ed';或非元音,非 'l' 后跟一个 'e'。由于模式末尾的 $,这些模式必须出现在单词的末尾才能匹配。分组 (?: ) 只是一个分组;领先的 ?: 做出了区分。模式可能会更短一些:

/(?:[^laeiouy]es?|ed)$/

也会做同样的事情。在任何情况下,如果模式匹配,相关字符将从单词中删除。

然后,

/^y/

匹配单词开头的 'y'。如果找到 'y',则会将其删除。

最后,

/[aeiouy]{1,2}/g

匹配任何一个或两个字符的元音(包括 'y')。 g 后缀使其成为 全局 匹配,因此 return 值是一个由所有此类元音跨度组成的数组。 returned 数组的长度是音节数(根据此技术)。

请注意,单词 "poem" 和 "lion" 将被报告为单音节单词,这对于某些英语变体可能是正确的,但并非全部。

Here is a pretty good reference for JavaScript regular expression operators.

据我所知,我们基本上是想计算元音或元音对,但有一些特殊情况。让我们从最后一行开始,即计算元音和元音对:

return word.match(/[aeiouy]{1,2}/g).length;

这将匹配任何元音或元音对。 [...]表示一个character class, i.e. that if we go through the string character-by-character, we have a match, if the actual character is one of those. {1, 2} is the number of repetitions,也就是说我们应该恰好匹配一个或两个这样的字符。

其他两行用于特殊情况。

word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');

这一行将从单词的末尾删除 'syllables',它们是:

  • Xes(其中 X 不是 'laeiouy' 中的任何一个,例如'zes')
  • ed
  • Xe(其中 X 不是 'laeiouy' 中的任何一个,例如'xe')

(我不太确定这背后的语法含义是什么,但我猜,单词末尾的 'syllables',比如 '-ed','- ded'、'-xed' 等实际上不算在内。) 至于正则表达式部分:(?:...) 是一个 non-capturing group。我想在这种情况下,这个组是 non-capturing 并不重要;这只是意味着我们想对整个表达式进行分组,但是我们不需要回头引用它。但是,我们也可以使用捕获组(即 (...)

[^...]是一个否定字符class。这意味着,匹配任何字符,即此处列出的 none 个字符。 (与上面提到的(非否定)字符-class 相比。) 管道符号,即 |,是 alternation 运算符,这意味着任何表达式都可以匹配。 最后,$ 锚匹配 end of the line 或字符串(取决于上下文)。

word = word.replace(/^y/, '');

这一行从单词的开头删除了 'y'-s(可能开头的 'y' 不算作一个音节——我认为这是有道理的)。 ^是匹配the beginning of the line或者字符串(上面提到的c.f.$)的锚点。

注意:该算法仅在 word 确实包含一个单词时有效。