3.2.2 for循環(huán)語(yǔ)句

2022-04-13 15:07 更新

(三) 定點(diǎn)提取:tokens=

  上一節(jié)在講解 delims= 的時(shí)候,我一再?gòu)?qiáng)調(diào) for /f 默認(rèn)只能提取到第一節(jié)的內(nèi)容,現(xiàn)在我們來(lái)思考一個(gè)問(wèn)題
:如果我要提取的內(nèi)容不在第一節(jié)上,那怎么辦?

  這回,就該輪到 tokens= 出馬了。

  tokens= 后面一般跟的是數(shù)字,如 tokens=2,也可以跟多個(gè),但是每個(gè)數(shù)字之間用逗號(hào)分隔,如 tokens=3,5,8
,它們的含義分別是:提取第2節(jié)字符串、提取第3、第5和第8節(jié)字符串。注意,這里所說(shuō)的“節(jié)”,是由 delims=
這一開(kāi)關(guān)劃分的,它的內(nèi)容并不是一成不變的。

  下面來(lái)看一個(gè)例子:
[txt2]

尺有所短,寸有所長(zhǎng),學(xué)好批處理沒(méi)商量,考慮問(wèn)題復(fù)雜化,解決問(wèn)題簡(jiǎn)潔化?! ?/p>

對(duì)[txt2]這段文本,假設(shè)它們保存在文件test.txt中,如果我想提取“學(xué)好批處理沒(méi)商量”這句話,該如何寫代碼呢
?

  我們稍微觀察一下[txt2]就會(huì)發(fā)現(xiàn),如果以逗號(hào)作為切分符號(hào),就正好可以把“學(xué)好批處理沒(méi)商量”化為單獨(dú)的
一“節(jié)”,結(jié)合上一節(jié)的講解,我們知道,"delims=," 這個(gè)開(kāi)關(guān)是不可缺少的,而要提取的內(nèi)容在以逗號(hào)切分的第
3節(jié)上,那么,tokens= 后面的數(shù)字就應(yīng)該是3了,最終的代碼如下:
[code8]

@echo off
for /f "delims=, tokens=3" %%i in (test.txt) do echo %%i
pause  

如果我們現(xiàn)在要提取的不只一個(gè)“節(jié)”,而是多個(gè),那又怎么辦呢?比如,要提取以逗號(hào)切分的第2節(jié)和第5節(jié)字符串
,是寫成這樣嗎?
[code9]

@echo off
for /f "delims=, tokens=2,5" %%i in (test.txt) do echo %%i
pause  

運(yùn)行批處理后發(fā)現(xiàn),執(zhí)行結(jié)果只顯示了第2節(jié)的內(nèi)容。

  原來(lái),echo 后面的 %%i 只接收到了 tokens=2,5 中第一個(gè)數(shù)值2所代表的那個(gè)字符串,而第二個(gè)數(shù)值5所代表的
字符串因?yàn)闆](méi)有變量來(lái)接收,所以就無(wú)法在執(zhí)行結(jié)果中顯示出來(lái)了。

  那么,要如何接收 tokens= 后面多個(gè)數(shù)值所指代的內(nèi)容呢?

  for /f 語(yǔ)句對(duì)這種情況做如下規(guī)定:

  如果 tokens= 后面指定了多個(gè)數(shù)字,如果形式變量為%%i,那么,第一個(gè)數(shù)字指代的內(nèi)容用第一個(gè)形式變量%%i
來(lái)接收,第二個(gè)數(shù)字指代的內(nèi)容用第二個(gè)形式變量%%j來(lái)接收,第三個(gè)數(shù)字指代的內(nèi)容用第三個(gè)形式變量%%k來(lái)接收…
…第N個(gè)數(shù)字指代的內(nèi)容用第N個(gè)形式變量來(lái)接收,其中,形式變量遵循字母的排序,第N個(gè)形式變量具體是什么符號(hào)
,由第一個(gè)形式變量來(lái)決定:如果第一個(gè)形式變量是%%i,那么,第二個(gè)形式變量就是%%j;如果第一個(gè)形式變量用的
是%%x,那么,第二個(gè)形式變量就是%%y。

  現(xiàn)在回頭去看[code9],你應(yīng)該知道如何修改才能滿足題目的要求了吧?修改結(jié)果如下:
[code10]

@echo off
for /f "delims=, tokens=2,5" %%i in (test.txt) do echo %%i %%j
pause  

如果有這樣一個(gè)要求:顯示[txt2]中的內(nèi)容,但是逗號(hào)要替換成空格,如何編寫代碼?

  結(jié)合上面所學(xué)的內(nèi)容,稍加思索,你可能很快就得出了答案:
[code11]

@echo off
for /f "delims=, tokens=1,2,3,4,5" %%i in (test.txt) do echo %%i %%j %%k %%l %%m
pause  

寫完之后,你可能意識(shí)到這樣一個(gè)問(wèn)題:假如要提取的“節(jié)”數(shù)不是5,而是10,或者20,或者更多,難道我也得從1
寫到10、20或者更多嗎?有沒(méi)有更簡(jiǎn)潔的寫法呢?

  答案是有的,那就是:如果要提取的內(nèi)容是連續(xù)的多“節(jié)”的話,那么,連續(xù)的數(shù)字可以只寫最小值和最大值,
中間用短橫連接起來(lái)即可,比如 tokens=1,2,3,4,5 可以簡(jiǎn)寫為 tokens=1-5 。

  還可以把這個(gè)表達(dá)式寫得更復(fù)雜一點(diǎn):tokens=1,2-5,tokens=1-3,4,5,tokens=1-4,5……怎么方便就怎么寫吧
。

  大家可能還看到一種比較怪異的寫法:
[code12]
 
for /f "delims=, tokens=1,*" %%i in (test.txt) do echo %%i %%j
pause  

結(jié)果,第一個(gè)逗號(hào)不見(jiàn)了,取代它的是一個(gè)空格符號(hào),其余部分保持不變。

  其中奧妙就在這個(gè)星號(hào)上面。

  tokens=后面所接的星號(hào)具備這樣的功能:字符串從左往右被切分成緊跟在*之前的數(shù)值所表示的節(jié)數(shù)之后,字符
串的其余部分保持不變,整體被*所表示的一個(gè)變量接收。

  理論講解是比較枯燥的,特別是為了嚴(yán)密起見(jiàn),還使用了很多限定性的修飾詞,導(dǎo)致句子很長(zhǎng),增加了理解的難
度,我們還是結(jié)合[code12]來(lái)講解一下吧。

  [txt2] 的內(nèi)容被切分,切分符號(hào)為逗號(hào),當(dāng)切分完第一節(jié)之后,切分動(dòng)作不再繼續(xù)下去,因?yàn)?tokens=1,* 中
,星號(hào)前面緊跟的是數(shù)字1;第一節(jié)字符串被切分完之后,其余部分字符串不做任何切分,整體作為第二節(jié)字符串,
這樣,[txt2]就被切分成了兩節(jié),分別被變量%%i和變量%%j接收。

  以上幾種切分方式可以結(jié)合在一起使用。不知道下面這段代碼的含義你是否看得懂,如果看不懂的話,那就運(yùn)行
一下代碼,然后反復(fù)揣摩,你一定會(huì)更加深刻地理解本節(jié)所講解的內(nèi)容的:
[code13]
 
@echo off
for /f "delims=, tokens=1,3-4,*" %%i in (test.txt) do echo %%i %%j %%k %%l
pause    
-----------------------------------------------------------------------------------------------------


(四) 跳過(guò)無(wú)關(guān)內(nèi)容,直奔主題:skip=n

  很多時(shí)候,有用的信息并不是貫穿文本內(nèi)容的始終,而是位于第N行之后的行內(nèi),為了提高文本處理的效率,或
者不受多余信息的干擾,for /f 允許你跳過(guò)這些無(wú)用的行,直接從第N 1行開(kāi)始處理,這個(gè)時(shí)候,就需要使用參數(shù)
 skip=n,其中,n是一個(gè)正整數(shù),表示要跳過(guò)的行數(shù)。例如:
[code14]
 
@echo off
for /f "skip=2" %%i in (test.txt) do echo %%i
pause  

這段代碼將跳過(guò)頭兩行內(nèi)容,從第3行起顯示test.txt中的信息。
-----------------------------------------------------------------------------------------------------


(五) 忽略以指定字符打頭的行:eol=

  在cmd窗口中敲入:for /?,相關(guān)的解釋為:
引用:
eol=c           - 指一個(gè)行注釋字符的結(jié)尾(就一個(gè))引用:
FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k
    會(huì)分析 myfile.txt 中的每一行,忽略以分號(hào)打頭的那些行……  第一條解釋狗屁不通,頗為費(fèi)解:行注釋字
符的結(jié)尾是什么意思?“(就一個(gè))”怎么回事?結(jié)合第二條解釋,才知道eol有忽略指定行的功能。但是,這兩條解
釋是互相矛盾的:到底是忽略以指定字符打頭的行,還是忽略以指定字符結(jié)尾的行?

  實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),還是用代碼來(lái)檢驗(yàn)一下eol的作用吧:
[code15]
 
@echo off
for /f "eol=;" %%i in (test.txt) do echo %%i
pause  

結(jié)果,那些以分號(hào)打頭的行沒(méi)有顯示出來(lái)。

  由此可見(jiàn),第二條解釋是正確的,eol= 的準(zhǔn)確含義是:忽略以指定字符打頭的行。而第一條的“結(jié)尾”純屬微
軟在信口開(kāi)河。

  那么,“(就一個(gè))”又作何解釋呢?

  試試這個(gè)代碼:
[code16]
 
@echo off
for /f "eol=,;" %%i in (test.txt) do echo %%i
pause  

此時(shí),屏幕上出現(xiàn) 此時(shí)不應(yīng)有 ;"。 的報(bào)錯(cuò)信息??梢?jiàn),在指定字符的時(shí)候,只能指定1個(gè)——在很多時(shí)候,我對(duì)這
樣的設(shè)計(jì)頗有微詞而又無(wú)可奈何:為什么只能指定1個(gè)而不是多個(gè)?要忽略多個(gè)還得又是if又是findstr加管道來(lái)多次
過(guò)濾,那效率實(shí)在太低下了——能用到的功能基本上都提供,但是卻又做不到更好,批處理,你的功能為什么那么弱?

  不知道大家注意到?jīng)]有,如果test.txt中有以分號(hào)打頭的行,那么,這些行在代碼[code14]的執(zhí)行結(jié)果中將憑空
消失。

  原來(lái),for /f 語(yǔ)句是默認(rèn)忽略以分號(hào)打頭的行內(nèi)容的,正如它默認(rèn)以空格鍵或跳格鍵作為字符串的切分字符一樣。

  很多時(shí)候,我們可以充分利用這個(gè)特點(diǎn),比如,在設(shè)計(jì)即將用for讀取的配置文件的時(shí)候,可以在注釋文字的行
首加上分號(hào),例如在編寫病毒文件查殺代碼的時(shí)候,可以通過(guò)for語(yǔ)句來(lái)讀取病毒文件列表,那么,病毒文件列表.ini
這個(gè)配置文件可以這樣寫:
 
;以下是常見(jiàn)的病毒文件,請(qǐng)見(jiàn)一個(gè)殺一個(gè)^_^
;copyleft:沒(méi)有
qq.exe
msn.exe
iexplore.exe  

如果要取消這個(gè)默認(rèn)設(shè)置,可選擇的辦法是:

  1、為eol=指定另外一個(gè)字符;
  2、使用 for /f "eol=" 語(yǔ)句,也就是說(shuō),強(qiáng)制指定字符為空,就像對(duì)付delims=一樣。
-----------------------------------------------------------------------------------------------------


(六)如何決定該使用 for /f 的哪種句式?(兼談usebackq的使用)

  for /f %%i in (……) do (……) 語(yǔ)句有好幾種變形語(yǔ)句,不同之處在于第一個(gè)括號(hào)里的內(nèi)容:有的是用單引
號(hào)括起來(lái),有的是用雙引號(hào)包住,有的不用任何符號(hào)包裹,具體格式為:
引用:
  1、for /f %%i in (文件名) do (……)
  2、for /f %%i in ('命令語(yǔ)句') do (……)
3、for /f %%i in ("字符串") do (……)  

看到這里,我想很多人可能已經(jīng)開(kāi)始犯了迷糊了:如果要解決一個(gè)具體問(wèn)題,面對(duì)這么多的選擇,如何決定該使用哪
一條呢?

  實(shí)際上,當(dāng)我在上面羅列這些語(yǔ)句的時(shí)候,已經(jīng)有所提示了,不知道你是否注意到了。

  如果你一時(shí)無(wú)法參透其中奧妙,那也無(wú)妨,請(qǐng)聽(tīng)我一一道來(lái)便是。

  1、當(dāng)你希望讀取文本文件中的內(nèi)容的話,第一個(gè)括號(hào)中不用任何符號(hào)包裹,應(yīng)該使用的是第1條語(yǔ)句;例如:你
想顯示test.txt中的內(nèi)容,那么,就使用 for /f %%i in (test.txt) do echo %%i;
  2、當(dāng)你讀取的是命令語(yǔ)句執(zhí)行結(jié)果中的內(nèi)容的話,第一個(gè)括號(hào)中的命令語(yǔ)句必須使用單引號(hào)包裹,應(yīng)該使用的是
第2條語(yǔ)句;例如:你想顯示當(dāng)前目錄下文件名中含有test字符串的文本文件的時(shí)候,應(yīng)該使用
 for /f %%i in ('dir /a-d /b *test*.txt') do echo %%i 這樣的語(yǔ)句;
  3、當(dāng)你要處理的是一個(gè)字符串的時(shí)候,第一個(gè)括號(hào)中的內(nèi)容必須用雙引號(hào)括起來(lái),應(yīng)該是用的是第3條語(yǔ)句;例
如:當(dāng)你想把bbs.bathome.cn這串字符中的點(diǎn)號(hào)換為短橫線并顯示出來(lái)的話,可以使用
for /f "delims=. tokens=1-3" %%i in ("bbs.bathome.cn") do echo %%i-%%j-%%k 這樣的語(yǔ)句。

  很顯然,第一個(gè)括號(hào)里是否需要用符號(hào)包裹起來(lái),以及使用什么樣的符號(hào)包裹,取決于要處理的對(duì)象屬于什么類
型:如果是文件,則無(wú)需包裹;如果是命令語(yǔ)句,則用單引號(hào)包裹;如果是字符串,則使用雙引號(hào)括起來(lái)。

  當(dāng)然,事情并不是絕對(duì)如此,如果細(xì)心的你想到了批處理中難纏的特殊字符,你肯定會(huì)頭大如斗。

  或許你頭腦中靈光一閃,已經(jīng)想到了一個(gè)十分頭痛的問(wèn)題:在第1條語(yǔ)句中,如果文件名中含有空格或&,該怎么
辦?

  照舊嗎?

  拿個(gè)叫 test 1.txt 的文件來(lái)試試。

  你很快寫好了代碼,新建文件-->碼字-->保存為批處理,前后費(fèi)時(shí)不到1分鐘:
[code17]
 
 @echo off
for /f %%i in (test 1.txt) do echo %%i
pause 

你興沖沖地雙擊批處理,運(yùn)行后,屏幕上出現(xiàn)了可恥的報(bào)錯(cuò)信息:系統(tǒng)找不到文件 test 。

  當(dāng)你把 test 1.txt 換成 test&1.txt 后,更怪異的事情發(fā)生了:CMD窗口在你眼前一閃而過(guò),然后,優(yōu)雅地
消失了。

  你可能覺(jué)得自己的代碼寫錯(cuò)了某些符號(hào),你再仔細(xì)的檢查了一次,確認(rèn)沒(méi)有筆誤,然后,你再次雙擊批處理,
結(jié)果問(wèn)題照舊;你開(kāi)始懷疑其他程序?qū)λ赡苡杏绊?,于是關(guān)掉其他窗口,再運(yùn)行了一次,問(wèn)題依舊;你不服氣
地連續(xù)運(yùn)行了好幾次,還是同樣的結(jié)果。

  怪哉!

  你一拍大腿,猛然想起了一件事:當(dāng)路徑中含有特殊字符的時(shí)候,應(yīng)該使用引號(hào)把路徑括起來(lái)。對(duì),就是它了!

  但是,當(dāng)你把代碼寫出來(lái)之后,你很快就焉了:for /f %%i in ("test 1.txt") do echo %%i,這不就是上面
提到的第3條 for /f 命令的格式嗎?批處理會(huì)把 test 1.txt 這個(gè)文件名識(shí)別為字符串??!

  你百無(wú)聊賴地在CMD窗口中輸入 for /? ,并重重地敲下了回車,漫無(wú)目的地在幫助信息中尋找,希望能找到點(diǎn)
什么。

  結(jié)果還真讓你到了點(diǎn)什么。

  你看到了這樣的描述:
引用:
        usebackq        - 指定新語(yǔ)法已在下類情況中使用:
                          在作為命令執(zhí)行一個(gè)后引號(hào)的字符串并且一個(gè)單
                          引號(hào)字符為文字字符串命令并允許在 filenameset
                          中使用雙引號(hào)擴(kuò)起文件名稱?! 〉牵ㄗx一遍之后,你卻如墜五里霧中,不知所
云。

  還好,下面有個(gè)例子,并配有簡(jiǎn)單的說(shuō)明:
引用:

      FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i
    會(huì)枚舉當(dāng)前環(huán)境中的環(huán)境變量名稱?! ∧阕屑?xì)對(duì)比了for /f語(yǔ)句使用usebackq和不使用usebackq時(shí)在寫法上
的差別,很快就找到了答案:當(dāng)使用了usebackq之后,如果第一個(gè)括號(hào)中是一條命令語(yǔ)句,那么,就要把單引號(hào)'改
成后引號(hào)`(鍵盤左上角esc鍵下面的那個(gè)按鍵,與~在同一鍵位上)。

  回過(guò)頭去再看那段關(guān)于usebackq的描述,字斟句酌,反復(fù)揣摩,終于被你破譯了天機(jī):usebackq 是一個(gè)增強(qiáng)
型參數(shù),當(dāng)使用了這個(gè)參數(shù)之后,原來(lái)的for語(yǔ)句中第一個(gè)括號(hào)內(nèi)的寫法要做如下變動(dòng):如果第一個(gè)括號(hào)里的對(duì)象
是一條命令語(yǔ)句的話,原來(lái)的單引號(hào)'要改為后引號(hào)`;如果第一個(gè)括號(hào)里的對(duì)象是字符串的話,原來(lái)的雙引號(hào)"要
改為單引號(hào)';如果第一個(gè)括號(hào)里的對(duì)象是文件名的話,要用雙引號(hào)"括起來(lái)。

  驗(yàn)證一下,把[code17]改寫成如下代碼:
[code18]
 
@echo off
for /f "usebackq" %%i in ("test 1.txt") do echo %%i
pause  

測(cè)試通過(guò)!

  此時(shí),你很可能會(huì)仰天長(zhǎng)嘆:Shit,微軟這該死的機(jī)器翻譯!

  至于把[code17]代碼中的空格換成&后,CMD窗口會(huì)直接退出,那是因?yàn)?amp;是復(fù)合語(yǔ)句的連接符,CMD在預(yù)處理的
時(shí)候,會(huì)優(yōu)先把&前后兩部分作為兩條語(yǔ)句來(lái)解析,而不是大家想象中的一條完整的for語(yǔ)句,從而產(chǎn)生了嚴(yán)重的語(yǔ)
法錯(cuò)誤。因?yàn)闋可娴筋A(yù)處理機(jī)制問(wèn)題,不屬于本節(jié)要討論的內(nèi)容,在此不做詳細(xì)講解。

  這個(gè)時(shí)候,我們會(huì)吃驚地發(fā)現(xiàn),區(qū)區(qū)一條for語(yǔ)句,竟然有多達(dá)6種句型:
 
  1、for /f %%i in (文件名) do (……)
  2、for /f %%i in ('命令語(yǔ)句') do (……)
  3、for /f %%i in ("字符串") do (……)
  4、for /f "usebackq" %%i in ("文件名") do (……)
  5、for /f "usebackq" %%i in (`命令語(yǔ)句`) do (……)
6、for /f "usebackq" %%i in ('字符串') do (……)  

其中,4、5、6由1、2、3發(fā)展而來(lái),他們有這樣的對(duì)應(yīng)關(guān)系:1-->4、2-->5、3-->6。

  好在后3種情形并不常用,所以,牢牢掌握好前三種句型的適用情形就可以了,否則,要在這么多句型中確定
選擇哪一條語(yǔ)句來(lái)使用,還真有點(diǎn)讓人頭腦發(fā)懵。

至于 for /f 為什么要增加usebacq參數(shù),我只為第4條語(yǔ)句找到了合理的解釋:為了兼容文件名中所帶的空格或&。
它在第5、6條語(yǔ)句中為什么還有存在的必要,我也不是很明白,這有待于各位去慢慢發(fā)現(xiàn)。 


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)