2012-09-23 16 views
5

Devo controllare la validità di una stringa memorizzata in una variabile, non posso usare utilità CLI esterne (grep, awk, ecc.) Quindi ho scelto FINDSTR. La stringa ha questo formato (in regexp):Usa subpatterns in FINDSTR

([1-9][0-9]*:".*"(|".*")*) 

non so come controllare il sotto-regola (| "*".). Attualmente il mio codice è:

((ECHO.) | (SET /P "=(11:"a"|"b"|"c")") | (FINDSTR /R /C:"^([1-9][0-9]*:".*")$")) 

saluti.

+2

Se possibile, probabilmente stai meglio usando vbscript o powershell. Manipolare le stringhe contenenti caratteri speciali è una difficoltà assurda nei file batch di Windows. –

+0

@Harry Johnston Sfortunatamente non riesco a utilizzare nulla di diverso dai comandi interni o esterni standard per cmd.exe. – networkcode

+1

VBScript e JScript sono utilità native standard disponibili per CMD.EXE, con un buon supporto di espressioni regolari. PowerShell è nativo da Vista in poi, e ha anche un buon supporto per le espressioni regolari. – dbenham

risposta

6

Mat M è corretto sulla limitazione di FINDSTR. Il supporto regex di FINDSTR è molto primitivo e non standard. Digitare HELP FINDSTR o FINDSTR /? dalla riga di comando per ottenere una breve sinossi di ciò che è supportato. Per una spiegazione approfondita, fare riferimento a What are the undocumented features and limitations of the Windows FINDSTR command?

Mi piace il commento di Harry Johnston - Sarebbe abbastanza facile creare una soluzione utilizzando VBScript o JavaScript. Penso che sarebbe una scelta molto migliore.

Ma, ecco una soluzione batch nativo. Ho incorporato la regola aggiuntiva sul numero di sottoprogrammi che l'OP ha dichiarato nel commento alla risposta di Mat M.

La soluzione è sorprendentemente complessa. Caratteri speciali possono causare problemi quando si esegue il piping dell'output ECHO a FINDSTR a causa del modo in cui i tubi funzionano. Ogni lato della pipe viene eseguito nella propria sessione CMD. I caratteri speciali devono essere citati, sfuggiti due volte o esposti solo tramite un'espansione ritardata. Ho scelto di utilizzare l'espansione ritardata, ma i caratteri ! devono essere sfuggiti due volte per assicurarsi che l'espansione ritardata avvenga all'ora corretta.

Il modo più semplice per analizzare un numero variabile di subtertern è sostituire il delimitatore con una nuova riga e utilizzare FOR/F per iterare ogni subpattern.

La metà superiore del mio codice è un'imbracatura di codifica fragile per semplificare l'iterazione e testare un set di stringhe. Non funzionerà correttamente con qualsiasi <space>;,=<tab>* o ? nella stringa. Inoltre, le virgolette devono essere bilanciate in ogni stringa.

Ma la più importante routine di convalida può gestire qualsiasi stringa nella variabile var.

@echo off 
setlocal 
set LF=^ 


::Above 2 blank lines are critical for creating a linefeed variable. Do not remove 

set test=a 

for %%S in (
    "(3:"a"|"c"|"c")" 
    "(11:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k")" 
    "(4:"a"|"b"|"c")" 
    "(10:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k")" 
    "(3:"a"|"b"|"c"" 
    "(3:"a"|"b^|c")" 
    "(3:"a"|"b"|c)" 
    "(3:"a"|"b"||"c")" 
    "(3:"a"|"b"|;|"c")" 
) do (
    set "var=%%~S" 
    call :validate 
) 
exit /b 

:validate 
setlocal enableDelayedExpansion 
cmd /v:on /c echo ^^^!var^^^!|findstr /r /c:"^([1-9][0-9]*:.*)$" >nul || (call :invalid FINDSTR fail& exit /b) 
if "!var:||=!" neq "!var!" (call :invalid double pipe fail& exit /b) 
for /f "delims=(:" %%N in ("!var!") do set "expectedCount=%%N" 
set "str=!var:*:=!" 
set "str=!str:~0,-1!" 
set foundCount=0 
for %%A in ("!LF!") do for /f eol^=^%LF%%LF%^ delims^= %%B in ("!str:|=%%~A!") do (
    if %%B neq "%%~B" (call :invalid sub-pattern fail& exit /b) 
    set /a foundCount+=1 
) 
if %foundCount% neq %expectedCount% (call :invalid count fail& exit /b) 
echo Valid: !var! 
exit /b 
:invalid 
echo Invalid - %*: !var! 
exit /b 

Ecco i risultati dopo l'esecuzione dello script batch di

Valid: (3:"a"|"c"|"c") 
Valid: (11:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k") 
Invalid - count fail: (4:"a"|"b"|"c") 
Invalid - count fail: (10:"a"|"b"|"c"|"d"|"esdf"|"f"|"g"|"h"|"i"|"j"|"k") 
Invalid - FINDSTR fail: (3:"a"|"b"|"c" 
Invalid - sub-pattern fail: (3:"a"|"b|c") 
Invalid - sub-pattern fail: (3:"a"|"b"|c) 
Invalid - double pipe fail: (3:"a"|"b"||"c") 
Invalid - sub-pattern fail: (3:"a"|"b"|;|"c") 


Aggiornamento

Il :validate routine può essere semplificato un po 'posticipando l'abilitazione di espansione ritardata fino a dopo la CMD /V:ON tubo. Ciò significa che non devo più preoccuparmi di eseguire il doppio escape del ! sul lato sinistro del tubo.

:validate 
cmd /v:on /c echo !var!|findstr /r /c:"^([1-9][0-9]*:.*)$" >nul || (call :invalid FINDSTR fail& exit /b) 
setlocal enableDelayedExpansion 
... remainder unchanged 
+0

Bello, anche se non sappiamo se il sesto test case è veramente falso. –

+0

@MatM - buon punto. Se dovrebbe essere valido, la soluzione sarà notevolmente più complicata. – dbenham

+0

@dbenham: la soluzione è corretta, grazie! Vorrei parlarvi di un progetto che ho fatto in gruppo per avere la vostra opinione, potrebbe essere di interesse comune! – networkcode

2

Per quanto ne so, findstr non è in grado di raggruppare espressioni regolari, quindi (|".*")* è un no-no. Se si conosce il numero di blocchi che si ha e si duplicare il vostro codice come questo

FINDSTR /R /C:"^([1-9][0-9]*:\"..*\"|\"..*\"|\"..*\")$" 

In questo modo, se si è sicuri del numero di blocchi è costante, avendo quelle vuote "" se necessario, allora si può verificare la presenza di esso.

Le doppie virgolette all'interno dell'espressione vengono ignorate a meno che non le si preceda con \.
Il costrutto ..* sostituisce .+: uno o più caratteri.

+0

La stringa non può contenere un numero costante di subpattern, il numero variabile viene comunicato dal primo numero seguito da:. Forse una soluzione sarebbe quella di convalidare immediatamente la stringa con un FINDSTR e quindi analizzare il token di ogni subpattern con un FOR/F, se corretto nel contenuto e nel numero, cosa ne pensi? – networkcode

+0

@ user1125183 - Dovrebbe funzionare, ma è difficile. Vedi [la mia risposta] (http://stackoverflow.com/a/12570555/1012053) – dbenham