问题
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.
回答1:
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.
回答2:
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.
回答3:
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!
来源:https://stackoverflow.com/questions/11229597/music-chord-part-splitting-regex