Pregunta

This is a follow-up question to this one: Regex for matching a music Chord, asked by me.

Now that I have a regex to know whether a String representation of a chord is valid or not (previous question), how can I effectively get the three different parts of the chord (the root note, accidentals and chord type) into seperate variables?

I could do simple string manipulation, but I guess that it would be easier to build on the previous code and use regex for that, or am I am wrong?

Here is the updated code from the aforementioned question:

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!");
    }
}

Thanks.

¿Fue útil?

Solución

Enclosing something with parentheses (except in cases with special meaning) creates a capturing group, or subpattern.

You already have accidentals and chords grouped as subpatterns like that, but you need to add parentheses to notes to capture that as a subpattern too.

String notes = "^([CDEFGAB])";
String accidentals = "(#|##|b|bb)?";
String chords = "(maj7|maj|min7|min|sus2)";

By convention, the string that is matched by the entire pattern is group 0, then every subpattern is captured as group 1, group 2, and so on.

I'm not a Java guy, but after reading the docs it looks like you would access your subpattern matches using .group():

String note = matcher.group(1);
String acci = matcher.group(2);
String chor = matcher.group(3);

Edit:

Originally, I suggested String accidentals = "((?:#|##|b|bb)?)";, because I was worried that the second subpattern being optional would have caused a group numbering problem if no match existed for it. However, a little testing suggests that even without wrapping it in a non-capturing grouping (?: ) like that, group 2 is always present but empty if there was no match. (Empty string in group 2 was the desired effect anyway.) So, it seems that ... = "(#|##|b|bb)?"; probably would suffice after all.

Otros consejos

You've already done the work. Just add one more capture group so that your final regex becomes:

    ^([CDEFGAB])(#|##|b|bb)?(maj7|maj|min7|min|sus2)?$

And your note, accidental, and chord will be in the first, second, and third captures, respectively.

I like the accepted answer, but as a guitar player I do encounter chords with an extra bass note added, such as G/D, or A/D, or D/F#. Of course, there are a number of other chord names you might encounter such as : 5 , 6, min6, 9, min9, sus4 ... etc. You might consider adding to the number of possible string chords, and then adding something for the bass accidentals if you have any:

String chords =
"(maj|maj7|maj9|maj11|maj13|maj9#11|maj13#11|6|add9|maj7b5|maj7#5||min|m7|m9|m11|m13|
m6|madd9|m6add9|mmaj7|mmaj9|m7b5|m7#5|7|9|11|13|7sus4|7b5|7#5|7b9|7#9|7b5b9|7b5#9|
7#5b9|9#5|13#11|13b9|11b9|aug|dim|dim7|sus4|sus2|sus2sus4|-5|)";
String bass = "/([CDEFGAB])";

In order to complete the "String chords" definition, you might want to consult a chord dictionary. CHEERS!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top