Regex for matching a music Chord

2019-01-26 09:48发布

问题:

I am trying to write a function to parse the string representation of a musical chord.

Example: C major chord -> Cmaj (this is what I want to parse)

Just to make it clear, a chord is made of three different parts:

  • the note (C, D, E, F, G, A)
  • the accidentals for that note (#, ##, b, bb)
  • the chord name

For those, music savvy, I am not considering slash chords (on purpose).

The below function is almost working. However it still doesn't work for the following case:

  • "C#maj" # matches and should
  • "C#maj7" # matches and should
  • "C#maj2" # mathches and shouldn't

I suppose that if I could make the chords regex part forced to be at the end of the regex, did the trick. I have tried using the $ both before and after this String but it didn't work.

Any idea? Thanks.

public static void regex(String chord) {                
    String notes = "^[CDEFGAB]";
    String accidentals = "[#|##|b|bb]";
    String chords = "[maj7|maj|min7|min|sus2]";
    String regex = notes + accidentals + chords; 
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(chord);
    System.out.println("regex is " + regex);
    if (matcher.find()) {
        int i = matcher.start();
        int j = matcher.end();
        System.out.println("i:" + i + " j:" + j);           
    }
    else {
        System.out.println("no match!");
    }
}

回答1:

Change [ and ] to ( and ) in the following lines:

String accidentals = "(#|##|b|bb)";
String chords = "(maj7|maj|min7|min|sus2)";

Otherwise you're just making character classes, so [maj7|maj|min7|min|sus2] simply matches on the letter m.

I'm guessing you also want to add an ending anchor $? I see you had problems with that before, but that's probably because of the aforementioned issue.


Also, might you want (#|##|b|bb) to be optional (i.e., with ?: (#|##|b|bb)?)?



回答2:

Forgive the JavaScript, but on a purely REGEX point, this pattern seems to work. You didn't stipulate which numbers are allowed after which chord names but I've assumed 2 is allowed only after 'sus' and 7 only after 'min' and 'maj'.

var chords = "C#maj7 C##maj Bbmaj7 Abmin2 Cbmin Dsus";
var valid_chords = chords.match(/\b[CDEFGAB](?:#{1,2}|b{1,2})?(?:maj7?|min7?|sus2?)\b/g);


回答3:

Building on Wiseguy's answer, I improved your regex matching. I had to add # outside of the variable accidentals since \b throws matching # off.

Bonus: It even matches chords like Dsus9, D7 etc.

Forgive the JavaScript, but this is the code I ended up using:

var notes = "[CDEFGAB]",
  accidentals = "(b|bb)?",
  chords = "(m|maj7|maj|min7|min|sus)?",
  suspends = "(1|2|3|4|5|6|7|8|9)?",
  sharp = "(#)?",
  regex = new RegExp("\\b" + notes + accidentals + chords + suspends + "\\b" + sharp, "g");

var matched_chords = "A# is a chord, Bb is a chord. But H isn't".match(regex);

console.log(matched_chords);