Como essa substituição de regex inverte uma string?
Esta é a quarta parte de uma série de artigos sobre regex educacional. Ele mostra como a combinação de referência aninhada (consulte:Como esse regex encontra números triangulares?) para "contar" dentro de asserções (consulte:Como podemos combinar a ^ n b ^ n com o Java regex?) pode ser usado para reverter uma string. O padrão gerado programaticamente usa abstrações de meta-padrão (consulte:Como esse regex Java detecta palíndromos?) Pela primeira vez na série, essas técnicas são usadas para substituição, em vez de correspondência de cadeia inteira.
São fornecidas implementações completas de Java e C #. Citações inspiradas incluídas.
Reverter uma string usando expressões regulares nunca pareceu uma boa idéia, nem era imediatamente óbvio se era possível e, se sim, como alguém poderia tentar fazê-lo.
Enquanto éainda Não é uma boa ideia, pelo menos agora sabemos que é possível, porque aqui está uma maneira de fazer isso:
C #(também em ideone.com)using System;
using System.Text.RegularExpressions;
public class TwoDollarReversal {
public static void Main() {
string REVERSE =
@"(?sx) . grab$2"
.Replace("grab$2",
ForEachDotBehind(
AssertSuffix(@"((.) \1?)")
)
);
Console.WriteLine(
Regex.Replace(
@"nietsniE treblA --
hguone llew ti dnatsrednu t'nod uoy ,ylpmis ti nialpxe t'nac uoy fI",
REVERSE, "$2"
)
);
// If you can't explain it simply, you don't understand it well enough
// -- Albert Einstein
}
// performs an assertion for each dot behind current position
static string ForEachDotBehind(string assertion) {
return "(?<=(?:.assertion)*)".Replace("assertion", assertion);
}
// asserts that the suffix of the string matches a given pattern
static string AssertSuffix(string pattern) {
return "(?=.*$(?<=pattern))".Replace("pattern", pattern);
}
}
Java(também em ideone.com)class TwoDollarReversal {
public static void main(String[] args) {
String REVERSE =
"(?sx) . grab$2"
.replace("grab$2",
forEachDotBehind(
assertSuffix("((.) \\1?)")
)
);
System.out.println(
"taerG eht rednaxelA --\nyrt lliw ohw mih ot elbissopmi gnihton si erehT"
.replaceAll(REVERSE, "$2")
);
// There is nothing impossible to him who will try
// -- Alexander the Great"
}
static String forEachDotBehind(String assertion) {
return "(?<=^(?:.assertion)*?)".replace("assertion", assertion);
}
static String assertSuffix(String pattern) {
return "(?<=(?=^.*?pattern$).*)".replace("pattern", pattern);
}
}
As versões C # e Java parecem usar o mesmo algoritmo geral, com pequenas variações apenas nos detalhes abstratos da implementação.
Claramente, essa não é a melhor, mais direta e mais eficiente maneira de reverter uma string. Dito isto, no interesse de aprender sobre regex; como conceitualizar padrões; como o mecanismo funciona para combiná-los; como juntar várias partes para construir o que queremos; como fazer isso de maneira legível e sustentável; e apenas pela pura alegria de aprender algo novo, podemos ter uma explicação de como isso funciona?
Apêndice: Folha de dicas!Esta é uma breve descrição das construções básicas de regex usadas:
(?sx)
é a bandeira incorporadamodificadores. s
ativa o modo "linha única", permitindo que oponto combinarQUALQUER personagem (Incluindo novas linhas).x
permite que oespaçamento livre modo, onde os espaços em branco não escapados são ignorados (e#
pode ser usado para comentários).^
e$
são o começo e o fim da linhaâncoras.?
como um especificador de repetição indicaopcional (ou seja, zero ou um de). Como um quantificador de repetição em p..*?
denota que o*
(ou seja, zero ou mais) a repetição érelutante/ não ganancioso.(…)
são usados paraagrupamento. (?:…)
é um grupo que não captura. Um grupo de captura salva a string que corresponde; permite referências de retorno / encaminhamento / aninhado (por exemplo,\1
), substituição de substituição (por exemplo,$2
) etc.(?=…)
é positivoolhe para frente; parece à direita afirmar que há uma correspondência do padrão fornecido.(?<=…)
é positivoolhar para trás; olha para a esquerda.Referências de idioma / recursos adicionaisMSDN - Elementos da linguagem de expressão regular -System.Text.RegularExpressions
Tutoriais Java / Classes essenciais / Expressões regulares -java.util.regex.Pattern