[英]How do I make my regex match whitespaces without consuming them?
我正在尝试匹配包含和弦的行,但是我需要确保每个匹配项都被空格包围或排在第一位,而不消耗字符,因为我不希望它们返回给调用者。
例如
Standard Tuning (Capo on fifth fret)
Time signature: 12/8
Tempo: 1.5 * Quarter note = 68 BPM
Intro: G Em7 G Em7
G Em7
I heard there was a secret chord
G Em7
That David played and it pleased the lord
C D G/B D
But you don't really care for music, do you?
G/B C D
Well it goes like this the fourth, the fifth
Em7 C
The minor fall and the major lift
D B7/D# Em
The baffled king composing hallelujah
Chorus:
G/A G/B C Em C G/B D/A G
Hal - le- lujah, hallelujah, hallelujah, hallelu-u-u-u-jah ....
除了它还与“ 68 BPM”中的“ B”匹配外,几乎所有的作品。 现在如何确保和弦正确匹配? 我不希望它与“之前”中的B或“ SUBSIDE”中的D或E相匹配?
这是我在每行上进行匹配的算法:
function getChordMatches(line) {
var pattern = /[ABCDEFG](?:#|##|b|bb)?(?:min|m)?(?:maj|add|sus|aug|dim)?[0-9]*(?:\/[ABCDEFG](?:#|##|b|bb)?)?/g;
var chords = line.match(pattern);
var positions = [];
while ((match = pattern.exec(line)) != null) {
positions.push(match.index);
}
return {
"chords":chords,
"positions":positions
};
}
那就是我想要数组形式为[“ A”,“ Bm”,“ C#”]而不是[“ A”,“ Bm”,“ C#”]。
编辑
我使用接受的答案使其工作。 我必须进行一些调整以适应主要的空白。 感谢大家抽出宝贵的时间!
function getChordMatches(line) {
var pattern = /(?:^|\s)[A-G](?:##?|bb?)?(?:min|m)?(?:maj|add|sus|aug|dim)?[0-9]*(?:\/[A-G](?:##?|bb?)?)?(?!\S)/g;
var chords = line.match(pattern);
var chordLength = -1;
var positions = [];
while ((match = pattern.exec(line)) != null) {
positions.push(match.index);
}
for (var i = 0; chords && i < chords.length; i++) {
chordLength = chords[i].length;
chords[i] = chords[i].trim();
positions[i] -= chords[i].length - chordLength;
}
return {
"chords":chords,
"positions":positions
};
}
我假设您已经将输入分成几行。 该函数将一一处理行。
您只需在提取前检查一下该行是否有和弦:
if (/^\s*[A-G](?:##?|bb?)?(?:min|m)?(?:maj|add|sus|aug|dim)?[0-9]*(?:\/[A-G](?:##?|bb?)?)?(?!\S)/.test(line)) {
// Match the chords here
}
我在前面添加了^\\s*
,以从行首开始进行检查,并添加了(?!\\S)
以检查在第一个和弦之后是否存在空格字符\\s
或行尾。
请注意,我对您的正则表达式做了一些细微的更改,因为A##
(假设它是有效的和弦)不会与您当前的正则表达式匹配。 正则表达式引擎将按照交替模式的顺序检查匹配,所以#
将首先尝试#|##
。 它将发现A#
匹配并返回匹配而不检查##
。 颠倒顺序##|#
还是使用贪婪量词##?
解决了该问题,因为它首先检查了更长的选择。
如果您确定:“如果第一个项目是和弦,则其余部分为和弦”,则可以用空格分开而不是匹配:
line.split(/\s+/);
更新资料
如果您只想匹配您的模式,无论和弦是否在句子中(您当前拥有的内容都可以):
/(?:^|\s)[A-G](?:##?|bb?)?(?:min|m)?(?:maj|add|sus|aug|dim)?[0-9]*(?:\/[A-G](?:##?|bb?)?)?(?!\S)/
此正则表达式将放置在您的问题代码中。
我检查和弦是否以空格字符开头,或者是(?:^|\\s)
行的开头。 不过,您需要修剪结果中的前导空间。
使用\\b
代替(?:^|\\s)
可以避免出现空格问题,但是含义有所不同。 除非您足够了解输入,否则我建议您不要这样做。
另一种方法是用\\s+
分割字符串,并针对每个令牌测试以下正则表达式(请注意,开头的^
和结尾的$
):
/^[A-G](?:##?|bb?)?(?:min|m)?(?:maj|add|sus|aug|dim)?[0-9]*(?:\/[A-G](?:##?|bb?)?)?$/
在开头和结尾添加\\b
(单词边界)对我来说很有效。 另外,您可以使用AG
代替ABCDEFG
。 从而:
> re = /\b[A-G](?:#|##|b|bb)?(?:min|m)?(?:maj|add|sus|aug|dim)?[0-9]*(?:\/[A-G](?:#|##|b|bb)?)?\b/g
/\b[A-G](?:#|##|b|bb)?(?:min|m)?(?:maj|add|sus|aug|dim)?[0-9]*(?:\/[A-G](?:#|##|b|bb)?)?\b/g
> 'G/A G/B C Em C G/B D/A G'.match(re)
["G/A", "G/B", "C", "Em", "C", "G/B", "D/A", "G"]
> 'Tempo: 1.5 * Quarter note = 68 BPM'.match(re)
null
要回答标题中的特定问题,请使用以下内容:
(?=\s)
如果将其嵌入RE中,则会确保后面的字符是空白而不会占用它。
尝试以下
function getChordMatches( line ) {
var match,
pattern = /(?:^|\s)([A-G](?:##?|bb?)?(?:min|m)?(?:maj|add|sus|aug|dim)?\d*(?:\/[A-G](?:##?|bb?)?)?)(?=$|\s)/g,
chords = [],
positions = [];
while ( match = pattern.exec(line) ) {
chords.push( match[1] );
positions.push( match.index );
}
return {
"chords" : chords,
"positions" : positions
};
}
它使用(?:^|\\s)
确保和弦在行的开头或前面有一个空格,并使用正向预见(?=$|\\s)
确保和弦后面跟一个空格或在行尾。 添加括号以捕获和弦本身,然后可通过match[1]
访问。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.