Board logo

标题: [讨论]for的tokens细节或者缺陷 [打印本页]

作者: qzwqzw     时间: 2008-1-19 01:08    标题: [讨论]for的tokens细节或者缺陷

http://www.cn-dos.net/forum/viewthread.php?tid=24127&fpage=1

刚才在上面链接里看到讨论for的tokens
虽然有一两篇精贴
但被众多口水淹没
让人很没有讨论的心情
索性换到正式场合另开新帖讨论
----------------------------------------
上文几位老兄讲得大致不错
只是在某些细节上还点得不够到位
tokens中有几个重要的细节也是缺陷
是所有喜欢玩脚本的老兄都应该注意的

为讨论方便首先建立一个测试文件
echo 1 2 3 4 5 6 7 8 9 10 11 12 > test

另外以下所有代码都是在命令行中测试
在bat中测试请自行修改
--------------------------------------------------------------------------------
细节或缺陷之一:order

猜猜下句的执行结果
for /f "tokens=9,8,7" %i in (test) do echo %i-%j-%k
是9-8-7吗?
错!是7-8-9
也就是说令牌是按照它在字符串中的排序值赋给变量的
跟代码中的书写顺序毫不相关
所以不管你怎么设置令牌顺序
它总是按照从小到大的顺序赋值给迭代变量
也就是说你可以随意变换tokens的顺序
而不必担心代码执行结果会有什么变化
只有一个例外(下面会有提到)

这对你是一个好消息吗?
至少对于我是一个坏消息!

因为某些时候我就想获得9-8-7的顺序
但我必须自己通过对变量进行重组才能得到这样的顺序
--------------------------------------------------------------------------------
细节或缺陷之二:*

其意义不言自明
获取剩余的所有令牌到一个变量

注意它必须位于所有令牌的尾部
否则会报错
这就是细节之一提到的唯一的例外
它的顺序不能随意变化

*的另外一个特性就是它隐含一个,
也就是说*可以不像其它令牌那样必须前缀一个逗号分隔符

所以以下两句的执行结果都是一样的
for /f "tokens=1,2*" %i in (test) do echo %i-%j-%k
for /f "tokens=1,2,*" %i in (test) do echo %i-%j-%k

因为在cmd的令牌串中间没有空令牌的存在

--------------------------------------------------------------------------------
细节或缺陷之三:m-n

最初写cmd脚本总以为m-n获取第m到n个令牌到一个变量
后来碰壁了才知道根本不是这么回事

再猜猜这句的结果
for /f "tokens=1-7,8,9" %i in (test) do echo %i-%j-%k

它的结果是是1-2-3,而不是1 2 3 4 5 6 7-8-9
也就是说1-7是指将第1-7个令牌分别取到了7个变量中
其中%i %j %k分别接收到了第1,2,3个令牌
第4-9个令牌因为没有变量接收所以被丢弃了

这个细节可以说我很不欣赏
因为逐个获取多个令牌到多个变量是完全可以通过笨办法来实现的
而完整获取多个令牌到一个变量是没有现成的好办法的
比如我想获取3 4 5 6 7到一个变量中该怎么办?

Q 可以用多个单令牌变量拼凑一个多令牌变量吗?
A 请考虑考虑delims的多样性

Q 可以用不带分割符的令牌符号(如1,2,34567,8,9)吗?
A cmd会执着的将34567认做一个超远的令牌(当然找不到)

Q 可以用*解决多令牌变量问题吗
A 那会多取8 9 10 11 12等令牌到变量

所以之后我见到代码里有tokens=1,2,3,4,5,6,7,8等类似代码
总是忍不住吁叹——
真是身在福中不知福啊!
--------------------------------------------------------------------------------
细节或缺陷之四:shift

在我们遇到一个超长令牌串时
会发现使用完26个字母也无法取到足够的令牌

或者遇到一个不知或不等长度的令牌串时
我们根本无法获知第n个令牌究竟是空还是不空

这是我就十分盼望有一个类似shift的处理令牌串移位的指令
能够让我对这个令牌串得心应手的操作
当然在ms没有满足我的要求之前
只好自立更生艰苦创业了

“没有枪没有炮敌人给我们造——”

这个方法也已经有很多人再用了
for /f "tokens=*" %i in (test) call script.cmd %i
当然script.cmd的脚本名更多时候会被改为一个批处理自身的标签

但如果这中间的delims竟然不是空格、跳格、等号、分号
那就够让人头疼的了!

[ Last edited by qzwqzw on 2008-1-19 at 01:15 AM ]
作者: everest79     时间: 2008-1-19 03:40
for /f 的options有固定的优先顺序
当在tokens的值中出现了*,那么对应的delims值就被忽略了
你们原来的贴子
for /f "tokens=1* delims=*" %a in (abcd*efg*hijk*lmn) do echo %a %b

这样的命令取得的值不是
abcd efg hijk lmn而是abcd efg*hijk*lmn
作者: lxmxn     时间: 2008-1-19 09:24
TO qzwqzw:

for可以取到超过26个变量的值,初步猜想是根据ASCII的大小顺序来取的。

test.txt

  Quote:
a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40

上面的文本是用逗号分隔,而且就一行。
CODE:  [Copy to clipboard]
for /f "delims=, tokens=1-31" %A in (test.txt) do @echo:第1个变量:%A,第27个变量:%[,第29个变量:%],第31个变量:%_
结果是:
第1个变量:a1,第27个变量:a27,第29个变量:a29,第31个变量:a31

而且还发现,tokens的个数只能取到31个,超过31个执行就没结果了。
比如:
CODE:  [Copy to clipboard]
for /f "delims=, tokens=1-32" %A in (test.txt) do @echo:第1个变量:%A,第27个变量:%[,第29个变量:%],第31个变量:%_
这个执行没有任何结果。

当然,for初始变量的可以指定为非字母的字符。
比如:
CODE:  [Copy to clipboard]
for /f "delims=, tokens=1-10" %! in (test.txt) do @echo:%!\%"\%#\%$\%&
结果是:
a1\a2\a3\a4\a6

对于特殊字符命名的变量还是无法处理,比如本例中的%%(%的ASCII位于$和&之间)这个就无法取到。
作者: terse     时间: 2008-1-19 10:06


  Quote:
Originally posted by everest79 at 2008-1-19 03:40:
for /f 的options有固定的优先顺序
当在tokens的值中出现了*,那么对应的delims值就被忽略了
你们原来的贴子
for /f "tokens=1* delims=*" %a in (abcd*efg*hi ...

我是这样理解的  这里的*还是可以作为分隔符的 只是 tokens=1* 定义了第一个*前面于%a 然后的所有字符都是%b 也就是并不是忽略了* 即便把delims=*换作delims=# 然后读取abcd#efg#hijk#lmn 这样也会的到abcd efg#hijk#lmn 把tokens=1*写成tokens=1,2,*    echo %a %b%c  得到 abcd efg hijk*lmn 不知我这样理解对不 学习了everest79兄
对了 () 里少了“”吧
作者: qzwqzw     时间: 2008-1-19 10:14
To everest79:

指出你的三个bug
1、前文我没有参与讨论,所以是“他们”而非“你们”
2、你给的测试代码abcd*efg*hijk*lmn没有加双引号,测试通不过
3、正如terse所说,你给的示例不能证明options的存在优先顺序

当然在某些情况下必须将delims写在最后
就是在同时取非空格字符和空格字符做delims时
比如下面的options都是无效的
-----------------------------------------------
"delims=,.  tokens=1-9"
"delims= ,. tokens=1-9"
-----------------------------------------------
根据楼下的意见改了

[ Last edited by qzwqzw on 2008-1-19 at 10:55 AM ]
作者: lxmxn     时间: 2008-1-19 10:23


  Quote:
Originally posted by qzwqzw at 2008-1-19 10:14:
To everest79:

指出你的三个bug
1、前文我没有参与讨论,所以是“他们”而非“你们”
2、你给的测试代码abcd*efg*hijk*lmn没有加双引号,测试通不过
3、 ...

比如下面的options都是无效的
"tokens=,.  delims=1-9"
"tokens= ,. delims=1-9"

tokens和delims搞反了吧?
作者: ansipeter     时间: 2008-1-19 10:35
http://9527dos.yo2.cn  里面有一篇是关于这个的,希望有用
作者: qzwqzw     时间: 2008-1-19 10:53
TO lxmxn:

迭代变量可以取更多ASCII字符这很早就有谈论了
只要选对好的起始点大约可以取将近90个变量

而且只要想到cmd乃至windows内部是完全unicode的
那么双字节字符作变量也应该不算稀奇了
for /f "tokens=1-7" %⒈ in (test) do echo %⒉ %⒊ %⒋

这样能设置的迭代变量就数量上而言足够使用了

但是本文的讨论重点不是迭代变量的数目
而是令牌符号的细节或陷阱
莫不成我可以改成“for的细节或者陷阱”
这才真的名副其实呢
作者: knoppix7     时间: 2008-1-19 12:40
明白点了。话说令牌是什么?
参数?
作者: qzwqzw     时间: 2008-1-19 12:45
惭愧
令牌就是tokens
tokens就是令牌
这是过去的算法书里的常用词汇
作者: everest79     时间: 2008-1-20 02:25
学习了




欢迎光临 中国DOS联盟论坛 (http://cndos.fam.cx/forum/) Powered by Discuz! 2.5