Domanda

Voglio analizzare le 2 cifre nel mezzo da una data in formato dd/mm/yy ma consentendo anche una sola cifra per il giorno e il mese.

Questo è ciò che mi si avvicinò con:

(?<=^[\d]{1,2}\/)[\d]{1,2}

Voglio un numero 1 o 2 cifre [\d]{1,2} con un numero 1 o 2 cifre e Slash ^[\d]{1,2}\/ prima di esso.

Questo non funziona in molte combinazioni, ho provato 10/10/10, 11/12/13, ecc ...

Ma con mia sorpresa (?<=^\d\d\/)[\d]{1,2} funzionato.

Ma la [\d]{1,2} deve anche corrispondere se \d\d ha fatto, o mi sbaglio?

È stato utile?

Soluzione

Il supporto lookbehind

Grandi sapori regex hanno diversi supporti per lookbehind in modo diverso; alcuni è soggetta a vincoli, e alcuni non ha nemmeno supporta affatto.

  • Javascript: non supportato
  • Python: solo lunghezza fissa
  • Java: solo lunghezza finita
  • NET: nessuna restrizione

Bibliografia


Python

In Python, dove è supportato lookbehind lunghezza solo fissa, il vostro modello originale genera un errore perché \d{1,2}, ovviamente, non ha una lunghezza fissa. È possibile "fissare" questo alternato su due diversi lookbehinds a lunghezza fissa, ad esempio qualcosa di simile a questo:

(?<=^\d\/)\d{1,2}|(?<=^\d\d\/)\d{1,2}

O forse si può mettere entrambe le lookbehinds in qualità di sostituti di un gruppo non-catturante:

(?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}

(si noti che si può semplicemente utilizzare \d senza le parentesi).

Detto questo, probabilmente è molto più semplice utilizzare un gruppo di cattura, invece:

^\d{1,2}\/(\d{1,2})

Si noti che findall rendimenti quale gruppo 1 cattura se solo avere un gruppo. gruppo di acquisizione è più ampiamente supportata di lookbehind, e spesso porta ad un modello più leggibile (come in questo caso).

Questo frammento illustra tutti i punti di cui sopra:

p = re.compile(r'(?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}')

print(p.findall("12/34/56"))   # "[34]"
print(p.findall("1/23/45"))    # "[23]"

p = re.compile(r'^\d{1,2}\/(\d{1,2})')

print(p.findall("12/34/56"))   # "[34]"
print(p.findall("1/23/45"))    # "[23]"

p = re.compile(r'(?<=^\d{1,2}\/)\d{1,2}')
# raise error("look-behind requires fixed-width pattern")

Bibliografia


Java

Java supporta solo di lunghezza finita lookbehind, in modo da poter utilizzare \d{1,2} come nel modello originale. Lo dimostra il seguente frammento:

    String text =
        "12/34/56 date\n" +
        "1/23/45 another date\n";

    Pattern p = Pattern.compile("(?m)(?<=^\\d{1,2}/)\\d{1,2}");
    Matcher m = p.matcher(text);
    while (m.find()) {
        System.out.println(m.group());
    } // "34", "23"

Si noti che (?m) è l'incorporato Pattern.MULTILINE in modo che ^ corrisponde anche all'inizio di ogni riga. Si noti inoltre che, dal momento \ è un carattere di escape per stringhe, è necessario scrivere "\\" per ottenere uno backslash in Java.


C-Sharp

C # supporta piena espressione regolare su lookbehind. Quanto segue mostra frammento come è possibile utilizzare + ripetizione su un lookbehind:

var text = @"
1/23/45
12/34/56
123/45/67
1234/56/78
";

Regex r = new Regex(@"(?m)(?<=^\d+/)\d{1,2}");
foreach (Match m in r.Matches(text)) {
  Console.WriteLine(m);
} // "23", "34", "45", "56"

Si noti che a differenza di Java, in C # è possibile utilizzare @ - stringa tra virgolette in modo che non si dispone di fuggire \

.

Per completezza, ecco come ci si utilizza l'opzione gruppo di cattura in C #:

Regex r = new Regex(@"(?m)^\d+/(\d{1,2})");
foreach (Match m in r.Matches(text)) {
  Console.WriteLine("Matched [" + m + "]; month = " + m.Groups[1]);
}

Data la text precedente, questo stampa:

Matched [1/23]; month = 23
Matched [12/34]; month = 34
Matched [123/45]; month = 45
Matched [1234/56]; month = 56

Domande correlate

Altri suggerimenti

A meno che non ci sia una ragione specifica per l'utilizzo del lookbehind che non è indicato nella domanda, come circa semplicemente corrispondenza il tutto e solo catturare il bit che ti interessa, invece?

Esempio JavaScript:

>>> /^\d{1,2}\/(\d{1,2})\/\d{1,2}$/.exec("12/12/12")[1]
"12"

Per citare regular-expressions.info :

  

La cattiva notizia è che la maggior parte regex   sapori non consentono di utilizzare solo   qualsiasi regex all'interno di un lookbehind, perché   essi non possono applicare un'espressione regolare   indietro. Di conseguenza, il regolare   motore espressione deve essere in grado di   capire come molti passi a passo indietro   prima di controllare il lookbehind.

     

Di conseguenza, molti sapori regex,   compresi quelli utilizzati da Perl e   Pitone, consentono solo a lunghezza fissa   stringhe. È possibile utilizzare qualsiasi regex di   che la lunghezza della partita può essere   predeterminata. Ciò significa che è possibile utilizzare   testo e caratteri classi letterali.   Non è possibile utilizzare la ripetizione o facoltativo   elementi. È possibile utilizzare l'alternanza, ma   solo se tutte le opzioni nella alternanza   hanno la stessa lunghezza.

In altre parole la tua espressione regolare non funziona perché si sta utilizzando un'espressione larghezza variabile all'interno di un lookbehind e il motore regex non supporta questo.

Oltre a quelli elencati da @polygenelubricants, ci sono due eccezioni alla "lunghezza fissa solo" regola. In PCRE (il motore regex per PHP, Apache, et al ) e Oniguruma (Ruby 1.9, Textmate), un lookbehind può essere costituito da un'alternanza in cui ogni alternativa può corrispondere un diverso numero di caratteri, come Se la lunghezza di ciascuna alternativa è fisso. Ad esempio:

(?<=\b\d\d/|\b\d/)\d{1,2}(?=/\d{2}\b)

Si noti che l'alternanza deve essere al livello superiore della sottoespressione lookbehind. Si potrebbe, come me, essere tentati di fattore gli elementi comuni, in questo modo:

(?<=\b(?:\d\d/|\d)/)\d{1,2}(?=/\d{2}\b)

... ma non avrebbe funzionato; al livello superiore, l'espressione tra ora consiste di un singolo alternativo con una lunghezza non fissa.

La seconda eccezione è molto più utile: \K, sostenuta da Perl e PCRE. Vuol dire effettivamente "finta partita davvero iniziato qui." Qualunque cosa appare prima che nella regex è trattata come una lookbehind positivo. Come con lookbehinds .NET, non ci sono restrizioni; ciò che può apparire in una regex normale può essere utilizzato prima della \K.

\b\d{1,2}/\K\d{1,2}(?=/\d{2}\b)

Ma la maggior parte del tempo, quando qualcuno ha un problema con lookbehinds, si scopre che non dovrebbero nemmeno essere li utilizzano. Come @insin sottolineato, questo problema può essere risolto molto più facilmente utilizzando un gruppo di cattura.

Modifica Quasi dimenticavo JGSoft, il sapore regex usata da EditPad Pro e PowerGrep; come .NET, ha lookbehinds completamente senza restrizioni, positivi e negativi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top