0

我正在尝试匹配包含和弦的行,但我需要确保每个匹配都被空格包围或在不消耗字符的情况下首先出现,因为我不希望它们返回给调用者。

例如

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
    };
}
4

4 回答 4

1

我假设您已经将输入分成几行。该函数将逐行处理。

您只需要在提取它们之前检查该行是否有一个和弦作为第一项:

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?)?)?$/
于 2013-02-03T13:03:27.237 回答
0

在开头和结尾添加\b(单词边界)对我有用。此外,您可以使用A-G而不是 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
于 2013-02-03T13:11:40.687 回答
0

在回答标题中的具体问题时,请使用前瞻:

 (?=\s)

当嵌入到 RE 中时,将确保以下字符是空格而不消耗它。

于 2013-02-03T13:12:09.383 回答
0

尝试以下

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]

于 2013-02-03T15:46:49.180 回答