Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Regex for standard guitar lyric/chord bracketing

Tags:

regex

guitar

I am trying to add square brackets around chords in standard text documents formatted for guitar/lyrics to make them more compatible with the OnSong app. I have the rules but don't understand how to match for all the possible combinations. The rules are:

  • Chords will begin with a single capital A-G
  • if the capital A-G is followed by a space, line break, #, b, m, sus, aug, dim, maj, min, or / I'd like to read until the next whitespace or line break (because of standard guitar formatting, a chord like F#min/E is possible, and rather than bother splitting it all out, I just want to keep reading until space)
  • The regex should NOT match if the capital A-G is followed by another letter not in the list above (for example the name "Ed" should not match)
  • bonus points if you can figure out how to make "A small world" NOT match due to the word FOLLOWING the "A" not being a valid chord.
  • super bonus points if the replacement can remove a space before (when not starting a line) and after the new brackets (to keep alignment in place) - it was pointed out to me that this will fail on close chords... this is completely acceptable.

A couple notes: This is for a helper script... perfection is not needed. I do this by hand right now, so the occasional miss is okay. I'm not trying to parse out the details of the chords, just to wrap them in []. While the standard layout is 1 row of chords, 1 row of lyrics, this can't be counted on, so I'm aware some scenarios will fail occasionally.

Test source (chords are random for testing purposes, in case any musicians were going to chime in on the terrible music):

Db    Dsus4/F#           A            Cbmin/C
A man can't be asked for that much to do
D/F#        G         A           D#/E
And I can't sweep you off of your feet

Should turn into:

[Db]  [Dsus4/F#]         [A]          [Cbmin/C]
A man can't be asked for that much to do
[D/F#]      [G]       [A]         [D#/E]
And I can't sweep you off of your feet

My first attempt got me close with:

([A-G]((?!\s).)*)

but that picked up words that began with those letters as well. I have gone around in circles now and only gotten as far as:

\b([CDEFGAB](#|##|b|bb|sus|maj|min|aug)?\b)

When I've tried to use [^\s+] I get mixed results that pick up more of what I want but also ditch things I need. I think I'm just over my head. Any help would be GREATLY appreciated and any explanation of how it works would be even better. While I'd like a solution, I'd also really love to explain why it works...

like image 868
IglooWhite Avatar asked Mar 19 '15 12:03

IglooWhite


2 Answers

This passes using your sample input and achieves all your "super bonus points" requirements:

String output = input.replaceAll("(?m)(^| )([A-G](##?|bb?)?((sus|maj|min|aug|dim)\\d?)?(/[A-G](##?|bb?)?)?)( (?!\\w)|$)", "[$2]");

This code turns this (as a single String with embedded line fees):

Db    Dsus4/F#           A            Cbmin/C
A man can't be asked for that much to do
D/F#        G         A           D#/E
And I can't sweep you off of your feet

Into this:

[Db]  [Dsus4/F#]         [A]          [Cbmin/C]
A man can't be asked for that much to do
[D/F#]      [G]       [A]         [D#/E]
And I can't sweep you off of your feet
like image 50
Bohemian Avatar answered Nov 15 '22 07:11

Bohemian


I have some working regex for the case you provided, but not sure how it will work for others. The problem is that a line can start with A, or it can be in the song line. I tried to work around it using the negative lookahead checking if the chord is followed by a space and an alphanumeric. If there is a space and an alphanumeric, we do not match this chord. Since the chords can repeat after /, I am doubling the pattern.

\b([CDEFGAB](?:b|bb)*(?:#|##|sus|maj|min|aug)*[\d\/]*(?:[CDEFGAB](?:b|bb)*(?:#|##|sus|maj|min|aug)*[\d\/]*)*)(?=\s|$)(?! \w)

Have a look at the demo.

like image 20
Wiktor Stribiżew Avatar answered Nov 15 '22 09:11

Wiktor Stribiżew