如何使我的正则表达式匹配空格而不消耗它们?

时间:2013-02-03 12:47:58

标签: javascript regex

我正在尝试匹配包含和弦的行,但我需要确保每个匹配都被空格包围或排在第一行而不会消耗字符,因为我不希望它们返回给调用者。

E.g。

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 个答案:

答案 0 :(得分: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?)?)?$/ ):< / p>

{{1}}

答案 1 :(得分: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

答案 2 :(得分:0)

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

 (?=\s)

当嵌入RE时,将确保后续字符是空白而不消耗它。

答案 3 :(得分: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]访问。