2012-09-23 24 views
5

我必須檢查存儲在變量中的字符串的有效性,我不能使用外部CLI實用程序(grep,awk等),因此我選擇了FINDSTR。 該字符串格式(在正則表達式):在FINDSTR中使用子模式

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

我不知道如何檢查子模式(| 「*」)。 目前我的代碼是:

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

問候。

+2

如果可能的話,您最好使用vbscript或powershell。操縱包含特殊字符的字符串在Windows批處理文件中是非常困難的。 –

+0

@Harry Johnston不幸的是我不能使用除了標準的內部或外部命令cmd.exe以外的任何東西。 – networkcode

+1

VBScript和JScript是可用於CMD.EXE的標準本地實用程序,具有良好的正則表達式支持。 PowerShell本身就是從Vista開始的,同時也有很好的正則表達式支持。 – dbenham

回答

6

Mat M對於FINDSTR的限制是正確的。 FINDSTR正則表達式支持是非常原始的和非標準的。從命令行輸入HELP FINDSTRFINDSTR /?以獲得所支持內容的簡要摘要。有關深入的解釋,請參閱What are the undocumented features and limitations of the Windows FINDSTR command?

我喜歡Harry Johnston的評論 - 使用VBScript或JavaScript創建解決方案將非常輕鬆。我認爲這將是一個更好的選擇。

但是,這裏是一個本地批處理解決方案。我已經將關於OP在評論中陳述的子模式數量的額外規則納入到Mat M的答案中。

該解決方案令人驚訝的棘手。由於管道的工作方式,在將ECHO輸出管道輸送到FINDSTR時,特殊字符可能會導致問題。管道的每一側都在它自己的CMD會話中執行。特殊字符必須被引用,轉義兩次,或只通過延遲擴展暴露。我選擇使用延遲擴展,但!字符必須轉義兩次以確保延遲擴展在正確的時間發生。

解析可變數量子模式的最簡單方法是用換行符替換分隔符,並使用FOR/F來迭代每個子模式。

我的代碼的上半部分是一個易碎的編碼工具,可方便地迭代和測試一組字符串。它將無法正常使用<space>;,=<tab>*?中的任何字符串。此外,報價必須在每個字符串中進行平衡。

但更重要的驗證例程可以處理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 

以下是運行批處理腳本

後的結果
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") 


更新

:validate例程可以通過推遲延遲擴展的啓用,直到CMD /V:ON之後被簡化位管。這意味着我不再需要擔心管道左側的!雙重泄漏。

: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

不錯,雖然我們不知道第六個測試用例是否真的是錯誤的。 –

+0

@MatM - 好點。如果它應該是有效的,那麼解決方案將顯得更加複雜。 – dbenham

+0

@dbenham:解決方案是正確的,謝謝!我想告訴你一個我批量進行的項目,以徵求你的意見,可能會引起共同興趣! – networkcode

2

據我所知,findstr是無法分組regexps,所以(|".*")*是一個否定的。如果你知道你有多少塊有,你複製你這樣的代碼

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

這樣一來,如果你確信塊的數量是恆定的,具有空的""如果需要的話,那麼你可以檢查它。

表達式中的雙引號將被忽略,除非您在它們前加\。
..*結構旨在代替.+:一個或多個字符。

+0

該字符串不能包含常數的子模式,變量號從第一個數字開始,然後是:。也許解決辦法是立即用FINDSTR驗證字符串,然後用FOR/F分析每個子模式的標記,如果內容和數字正確,你認爲如何? – networkcode

+0

@ user1125183 - 這應該可行,但這很棘手。請參閱[我的答案](http://stackoverflow.com/a/12570555/1012053) – dbenham