While the regex pattern isn't perfect at finding only the intended answer (there are a number of variations that the matching string will accept), I'll explain the code and build the answer that Ivo Beckers found.
First an explanation of regex calls that will be important. The pattern matcher moves through the input string, "consuming" characters as it matches them.
(?=__)
is a positive lookahead which ensures the next characters match the specification in __
; however, unlike normal matching, a lookahead doesn't consume the characters, so they still need to be matched. Conversely, the negative lookahead (?!__)
looks ahead and ensures that the next characters don't match __
.
When brackets (__)
are used around matching statements like __
(but not as part of a lookahead statement), it forms a "capturing group". This means that whatever __
matches to will be stored as a subpattern that can be referred to later, and will be assigned a reference number based on the order of the opening bracket (
. For example, if I matched the string abc
against the pattern .(.(.))
(.
matches almost any single character), then it would match, and the stored subpatterns would be 1:bc
and 2:c
. Later in the pattern, we can match against these subpatterns \1
and \2
.
(?ix)(?![AD-ZC][a-jn-zjm])
(?ix) sets modifiers, which change the behaviour of the pattern matching. i
means the pattern matching is case-insensitive. We then have a negative lookahead to check that the next two characters are not [AD-ZC][a-jn-zjm]
. This can be a B
followed by any character or any character followed by a K
or L
. They can be non-alphabetical characters, but the next part sorts that out. Let's assume BL
so far.
(
([[:upper:]])
[[:lower:]]
)
This matches the first two letters to characters. Since it's on case-insensitive mode, this just makes sure that they're letters, but otherwise it would check that it's an uppercase and a lowercase letter. It also stores the pair as subpattern 1 bl
and the first character of the two as subpattern 2 b
. Still BL
.
(?=[a-z])
(?!b-z)
(.)
We perform a positive lookahead to check that the next character is a letter, followed by a negative lookahead to check that it's not b-z
(so it's A
). (.)
then consumes that character and stores it as subpattern 3 a
. BLA
ck
This just matches ck
. BLACK
(
(?!\t\r\n\f)
\s
)
The next character is matched to a whitespace character \s
but not any of the special whitespace characters. This leaves us with a space '
' character stored as subpattern 4. BLACK
(
(?=[A-Z])
(?![A-R])
(?![T-Z])
.\3\2\2\3t
(
(h)
)
)
The three lookaheads assert that the next character is a letter, and not A-R
or T-Z
, leaving S
. .
consumes the S, and \3\2\2\3
matches those subpatterns in that order, which is ABBA
. This is followed by t
and h
. The whole group is stored as subpattern 5 SABBATH
, and the h
is stored as both subpatterns 6 and 7 h
. BLACK SABBATH
\4\-\4\5\4\1
This simply matches the numbered subpatterns and the -
character, giving - SABBATH BL
. BLACK SABBATH - SABBATH BL
(o)
(\8)
d
This matches o
and stores it as subpattern 8. It then matches \8
and stores that, too, so subpatterns 8 and 9 are both o
. Then it matches d
. BLACK SABBATH - SABBATH BLOOD
(?![xa-wz])
[[:alpha:]]\4\5*
Another negative lookahead to ensure that the next character isn't xa-wz
, leaving y
. [[:alpha:]]
then matches the next alphabetical character, which is y
. We then add another space with \4
, giving us BLACK SABBATH - SABBATH BLOODY
. *
means to match \5
(SABBATH
) 0 or more times, as many times as possible. If we leave it as 0, we're already finished and have a matching pattern, or BLACK SABBATH - SABBATH BLOODY SABBATHSABBATHSABBATHSABBATH
is also valid. A quick Google search indicates that "Sabbath, Bloody Sabbath" is a real song, so BLACK SABBATH - SABBATH BLOODY SABBATH
seems to be the right answer. Alternatively, he may be listening to zKack saZzatH - SazZATh ZkoOdY
!