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.RegularExpressionsTutoriais Java / Classes essenciais / Expressões regulares -java.util.regex.Pattern

questionAnswers(1)

yourAnswerToTheQuestion