2012-08-10 12 views

risposta

3

AFAIK, non è possibile. È possibile acquisire solo un gruppo per parentesi e successivamente controllare la lunghezza dei dati acquisiti da quel gruppo.

+3

Yay! Cinque upvotes per * Non puoi *. Devo provare più forte! – Borodin

+0

È persino possibile eseguire completamente all'interno della regex. Vedi la mia risposta. – ikegami

+0

Una risposta "AFAIK" in negativo è fondamentalmente sempre una scelta errata. Se non riesci a spiegare perché è impossibile, perché dire loro che pensi che sia? – Mark

5

È necessario acquisire la stringa corrispondente ed elaborarla separatamente.

Questo codice dimostra

use strict; 
use warnings; 

my $str = '> plantagenetgoosewagonattributes'; 

if ($str =~ />(.*)[^a]+/) { 
    my $substr = $1; 
    my %counts; 
    $counts{$_}++ for $substr =~ /./g; 
    print "'$_' - $counts{$_}\n" for sort keys %counts; 
} 

uscita

' ' - 1 
'a' - 4 
'b' - 1 
'e' - 4 
'g' - 3 
'i' - 1 
'l' - 1 
'n' - 3 
'o' - 3 
'p' - 1 
'r' - 1 
's' - 1 
't' - 5 
'u' - 1 
'w' - 1 
0

Prima un'osservazione: A causa della golosità del *, l'ultima [^a]+ non potrà mai corrispondere a più di un non un personaggio - vale a dire, si potrebbe anche lasciare il +.

E come dice @mvf, è necessario acquisire la stringa che il carattere jolly corrisponde per poter contare i caratteri al suo interno. Le espressioni regolari Perl non hanno un modo per restituire un conteggio del numero di volte che un gruppo specifico corrisponde - il motore probabilmente mantiene il numero intorno per supportare il meccanismo {,n}, ma non è possibile ottenerlo.

2

V'è sperimentale, non fare-uso-me, (?{ code }) costrutto ...

Da man perlre:

"({codice}?)" ATTENZIONE: Questa estesa espressione regolare la funzionalità è considerata sperimentale e potrebbe essere modificata senza preavviso. Il codice eseguito che ha effetti collaterali potrebbe non funzionare in modo identico da versione a versione a causa dell'effetto di future ottimizzazioni nel motore regex.

Se questo non spaventarvi, ecco un esempio che conta il numero di "p" s

my $p_count; 
">pppppbca" =~ /(?{ $p_count = 0 })>(p(?{$p_count++})|.)*[^a]+/; 
print "$p_count\n"; 
+0

Produce un risultato errato perché non hai tenuto conto del backtracking. (Dovrebbe restituire 4 per 'ppppp' e' pppppa', ma restituisce 5.) – ikegami

+0

Inoltre, usare le variabili 'my' dichiarate al di fuori di' (? {}) 'All'interno di' (? {}) 'Porterà a risultati errati in alcune situazioni. Usa 'local our' invece di' my'. – ikegami

+0

Entrambi i punti positivi. Ammetto di non aver mai usato la funzione finché non ho provato a scrivere questo esempio. Ho visto la nota sull'utilizzo di local per gestire il backtracking. Non sono sicuro del motivo per cui ho postato questa risposta; Non consiglierei di usarlo, ma ho pensato che fosse abbastanza interessante da far notare. – chepner

5

Al di fuori della regex:

my $p_count = map /p/g, />(.*)[^a]/; 

autosufficiente:

local our $p_count; 
/
    (?{ 0 }) 
    > 
    (?: p (?{ $^R + 1 }) 
    | [^p] 
    )* 
    [^a] 
    (?{ $p_count = $^R; }) 
/x; 

In entrambi i casi, è possibile espanderlo facilmente per contare tutte le lettere . Ad esempio,

my %counts; 
if (my ($seq = />(.*)[^a]/) { 
    ++$counts{$_} for split //, $seq; 
} 

my $p_count = $counts{'p'}; 
+0

Cerca di eseguire il tuo codice 'autonomo' al di fuori di Perl :) (sed, awk, bash - vedi tag dell'autore). ;) Non rovinare la possibilità di eseguire qualche codice perl all'interno della sintassi 'estesa' di Regexp con regexp stesso. – mvf

+1

@mvf, È impossibile * scrivere un problema che funziona in tutti quegli interpreti, quindi non ha senso. (* - Potresti scrivere un "quine", ma ciò comporterebbe la scrittura del programma anche in più lingue.) – ikegami

3

Proseguendo lungo le linee di soluzione di Borodin, ecco un bash pura uno:

let count=0 
testarray=(a b c d e f g h i j k l m n o p q r s t u v w x y z) 

string="> plantagenetgoosewagonattributes"     # the string 
pattern=">(.*)[^a]+"         # regex pattern 

limitvar=${#testarray[@]}         #array length 

[[ $string =~ $pattern ]] && 
(while [ $count -lt $limitvar ] ; do sub="${BASH_REMATCH[1]//[^${testarray[$count]}]}" ; echo "${testarray[$count]} = ${#sub}" ; ((count++)) ; done) 

Fissando da bash 3.0, bash ha introdotto i gruppi di cattura ai quali è possibile accedere tramite BASH_REMATCH [n].

La soluzione dichiara i caratteri da contare come array [Partenza declare -a per matrice declaraton nei casi complessi] .a valore del singolo carattere richiederebbe nessuna variabile di conteggio, non mentre costrutto ma una variabile per il carattere invece che una matrice.

Se si includono intervalli come nel codice precedente, questa dichiarazione di array fa esattamente la stessa cosa.

testarray=(`echo {a..z}`) 

Un'introduzione di un loop if rappresenterà la visualizzazione di 0 count caratteri. Volevo mantenere la soluzione il più semplice possibile.