Board logo

标题: [文本处理] [已解决]学习批处理插入行操作遭遇感叹号,与添加行冲突 [打印本页]

作者: stance    时间: 2011-1-14 22:11     标题: [已解决]学习批处理插入行操作遭遇感叹号,与添加行冲突

学习插入行操作遭遇!号,与添加行冲突

上次发帖得到各位老师指导,近两天一直在学习插入行等文本操作。(原帖:http://www.bathome.net/thread-10708-1-1.html

实际处理txtsetup.sif文本时,本意是要在[sourcedisksfiles]节添加三行内容:
  1. sun86.ime = 1,,,,,,,,2,0,0
  2. sun86.mb = 1,,,,,,,,2,0,0
  3. sun86.chm = 1,,,,,,,,21,0,0
复制代码
遇到几个问题:

一、套用寒夜版主的代码

1、文本中[sourcedisksfiles]节共有3个,只要在第一节中添加,因此套用寒夜版主上次给的批处理,使用了变量c、d作为开关,可以实现添加,但却丢失了!号:
  1. set pth=txtsetup
  2. attrib -r %pth%.sif
  3. @echo off&setlocal enabledelayedexpansion
  4. set c==
  5. (for /f "tokens=1* delims=:" %%a in ('findstr /n .* "%pth%.sif"') do if "%%b"=="" (echo/) else (
  6.         set "b=%%b"
  7.         if "!c!!d!"=="==" if "!b:~0,1!!b:~-1!"=="[]" (
  8.                 echo sun86.ime = 1,,,,,,,,2,0,0
  9.                 echo sun86.mb = 1,,,,,,,,2,0,0
  10.                 echo sun86.chm = 1,,,,,,,,21,0,0
  11.                 set c=&set d=
  12.                         )
  13.         if /i "!b!"=="[sourcedisksfiles]" set d==
  14.         echo !b!
  15. ))>"%pth%.tmp"
复制代码
2、将开启延迟变量放到SET之后,并且不得不使用了ENDLOCAL,!号保住了,但要添加的3行却没进去。批处理如下:
  1. (for /f "tokens=1* delims=:" %%a in ('findstr /n .* "%pth%.sif"') do (
  2.         set "b=%%b"
  3.         set c==
  4.         if "%%b"=="" (echo/) else (
  5.                 if "!c!!d!"=="==" if "!b:~0,1!!b:~-1!"=="[]" (
  6.                         echo sun86.ime = 1,,,,,,,,2,0,0
  7.                         echo sun86.mb = 1,,,,,,,,2,0,0
  8.                         echo sun86.chm = 1,,,,,,,,21,0,0
  9.                         set c=&set d=
  10.                         )
  11.         @echo off&setlocal enabledelayedexpansion
  12.         set b=!b:*:=!
  13.         if /i "!b!"=="[sourcedisksfiles]" set d==
  14.         echo !b!
  15.         )
  16.         endlocal
  17. ))>"%pth%.tmp"
复制代码
折腾这个批处理时进一步理解了开关标识符:开关变量c、d并非无值,=号就是它们的值。当它们的=值并存时,开关打开;当其中一个无值时,开关关闭。c开关已经提前开启,第一次遇到目标节名的时候,开启d开关,c、d两个开关同时处于开启状态,于是运行添加行动作,添加后关闭c、d两个开关。遇到后面的重复目标节名时,d开关仍然会再度打开,但c开关已经关闭了,不会再添加行——佩服这精妙的构思!


不妙的是,这么好的东西到了我手上就不听使唤了。环境一变,就应付不来了。

二、套用随风版主的代码
  1. set pth=txtsetup
  2. @echo off
  3. (for /f "delims=" %%i in ('findstr /in .* %pth%.sif') do (
  4.    set "str=%%i"
  5.    if defined h set "h="
  6.    if not defined v (
  7.       for /f "tokens=1* delims=:" %%j in ("%%i") do (
  8.          if /i "%%k"=="[sourcedisksfiles]" set s=1
  9.          if defined s if /i "%%k"=="[SourceDisksFiles.ia64]" set h=a&set v=a&set "s="
  10.        )
  11.     )
  12.    setlocal enabledelayedexpansion
  13.    set str=!str:*:=!
  14.    if not defined str (echo;) else (
  15.       if defined h (
  16.           echo sun86.ime = 1,,,,,,,,2,0,0
  17.           echo sun86.mb = 1,,,,,,,,2,0,0
  18.           echo sun86.chm = 1,,,,,,,,21,0,0
  19.          set s=3
  20.        )
  21.       echo;!str!
  22.    )
  23.    endlocal
  24. ))>%pth%.tmp
复制代码
添加行成功了!

这段代码运用了3个开关:h、v、s。h、s用来检测[sourcedisksfiles]节名的发现,当遇到下一节名[SourceDisksFiles.ia64]时,s开关关闭,同时开启h、v两个开关。当发现h开关开启时,做添加行操作。操作后,v开关一直处于开启状态,再遇到后面重复的[sourcedisksfiles]节名时,无法满足if not defined v以及if defined h两个当中的任何一个了,于是就不会再添加行。
按照这个理解,我试着取消了v开关,改为if not defined h,果然成功了。这样,随风版主的开关也减至2个:h、s。
修改后的代码如下:
  1. set pth=txtsetup
  2. @echo off
  3. (for /f "delims=" %%i in ('findstr /in .* %pth%.sif') do (
  4.    set "str=%%i"
  5.    if defined h set "h="
  6.    if not defined h (
  7.       for /f "tokens=1* delims=:" %%j in ("%%i") do (
  8.          if /i "%%k"=="[sourcedisksfiles]" set s=1
  9.          if defined s if /i "%%k"=="[SourceDisksFiles.ia64]" set h=a&set "s="
  10.        )
  11.     )
  12.    setlocal enabledelayedexpansion
  13.    set str=!str:*:=!
  14.    if not defined str (echo;) else (
  15.       if defined h (
  16.           echo sun86.ime = 1,,,,,,,,2,0,0
  17.           echo sun86.mb = 1,,,,,,,,2,0,0
  18.           echo sun86.chm = 1,,,,,,,,21,0,0
  19.           set s=3
  20.        )
  21.       echo;!str!
  22.    )
  23.    endlocal
  24. ))>%pth%.tmp
复制代码
修改之后,重新研究新代码。发现[SourceDisksFiles]节同时被h、s两个开关控制着,是否可以再减少一个呢?试验结论是s开关不能取消,如果取消,会造成添加混乱或者不能添加。


但是,set s=3可以取消。之所以能够在检测到下一节[SourceDisksFiles.ia64]名称的时候添加新行,是因为此时set h=a&set "s=",重新给它赋值没有意义了。当遇到后面重复的[sourcedisksfiles]节名时,会重新触发set s=1,但由于set h=a的状态没有取消,无法满足if not defined h的条件,所以新的重复节出现不会触发添加行的动作。

按照这个理解,取消set s=3后,试验成功。代码变成这样:
  1. set pth=txtsetup
  2. @echo off
  3. (for /f "delims=" %%i in ('findstr /in .* %pth%.sif') do (
  4.    set "str=%%i"
  5.    if defined h set "h="
  6.    if not defined h (
  7.       for /f "tokens=1* delims=:" %%j in ("%%i") do (
  8.          if /i "%%k"=="[sourcedisksfiles]" set s=1
  9.          if defined s if /i "%%k"=="[SourceDisksFiles.ia64]" set h=a&set "s="
  10.        )
  11.     )
  12.    setlocal enabledelayedexpansion
  13.    set str=!str:*:=!
  14.    if not defined str (echo;) else (
  15.       if defined h (
  16.           echo sun86.ime = 1,,,,,,,,2,0,0
  17.           echo sun86.mb = 1,,,,,,,,2,0,0
  18.           echo sun86.chm = 1,,,,,,,,21,0,0
  19.        )
  20.       echo;!str!
  21.    )
  22.    endlocal
  23. ))>%pth%.tmp
复制代码
[SourceDisksFiles.ia64]节名称不可以用[]号来代替的,把if /i "%%k"=="[SourceDisksFiles.ia64]"改成"!str:~0,1!!str:~-1!"=="[]",试验,新行未插入。


问题:
1、很奇怪为什么寒夜版主的代码改了之后就无法添加行了呢?
2、对随风版主代码  if defined h set "h="和 set str=!str:*:=!这两句所起的作用还不太理解。改是改了,知其然不知其所以然。

tmplinshi版主给的代码认真做了几遍,因为太长,就不帖了。里面有很多基础知识性的东西,还得学习。

附件:txtsetup.sif压缩件RAR格式,文本超过上传限制,分两卷(ANSI编码)
作者: 随风    时间: 2011-1-15 00:44

如果按我原帖中的代码,v开关是不能取消的,因为我说过是按普通文本来处理,即不考虑inf文件的特性,而你的要求是只在第一个[sourcedisksfiles]后[Locales]前添加新内容,如果取消v开关,那么如果文本有两个[sourcedisksfiles][Locales]段,则都会添加新内容.

你修改寒夜版主的代码
第一个,变量延迟开启的状态自然会丢失!号,而第二个,你把setlocal enabledelayedexpansion放在了for内部并启用了endlocal,也就是说你在setlocal enabledelayedexpansion后的变量在endlocal后又都归零了,自然这些变量也就白设置了.

set str=!str:*:=! 目的是为了最完整的取到整行,因为第一个for得到的值是行号加冒号加整行内容,该语句是删除行号及冒号,若是用第二个for的方法删除行号和冒号,遇到以冒号开头的行就会丢失内容.

if defined h set "h=" 当h第一次被定义后,就已经完成了他的使命,若不清空,那么每次运行到if defined h 时都会执行他括弧里的命令.
因为v开关的作用,他也不会再次被定义
作者: hanyeguxing    时间: 2011-1-15 00:59

  1. @echo off
  2. set "r=txtsetup.sif"
  3. set c==
  4. (for /f "tokens=1* delims=:" %%a in ('findstr /n .* "%r%"') do if "%%b"=="" (echo/) else (
  5.         for /f "tokens=1* delims=[]" %%c in ("%%b") do if "%%d"=="" if not "%%b"=="%%c" if defined c if defined d (
  6.                 set c=&set d=
  7.                 echo sun86.ime = 1,,,,,,,,2,0,0
  8.                 echo sun86.mb = 1,,,,,,,,2,0,0
  9.                 echo sun86.chm = 1,,,,,,,,21,0,0
  10.                 )
  11.         set "b=%%b"
  12.         setlocal enabledelayedexpansion
  13.         echo !b!
  14.         endlocal
  15.         if /i "%%b"=="[sourcedisksfiles]" set d==
  16. ))>temp.txt
  17. move temp.txt "%r%"
复制代码
1,套用的该是思路,而不该是代码
2,注意:本代码修改源文件
3,使用 if defined c if defined d 替代 if "!c!!d!"=="=="
4,使用 for /f "tokens=1* delims=[]" %%c in ("%%b") do if "%%d"=="" if not "%%b"=="%%c" 替代 if "!b:~0,1!!b:~-1!"=="[]"
非注释行存在 [] 只有节名行,所以 if not "%%b"=="%%c" 可以判断是否为节名行
如果在注释行存在 [] ,则必定以分号 ; 开头,而  [] 中间不太可能为空并位于最后,所以此时就必然存在 %%d ,以 if "%%d"=="" 判断是否为注释行。

[ 本帖最后由 hanyeguxing 于 2011-1-15 01:27 编辑 ]
作者: stance    时间: 2011-1-15 01:00     标题: 回复 2楼 的帖子

哦,原来是这样。有了v,即使有了两个重复的相邻段也不怕,这是适应任何情况的处理方法。应该保留!
对h的理解也不对。貌似在个例中也能通过,但不具有普遍性。


endlocal问题比较麻烦,按照现在改完的代码,如果不设endlocal就会收到错误提示,系统到了最大递归层。

[ 本帖最后由 stance 于 2011-1-15 01:18 编辑 ]
作者: stance    时间: 2011-1-15 01:08     标题: 回复 3楼 的帖子

寒夜老师太好啦!我正为它发愁呢,已经搞了两天了。
我现在的想法不仅仅是找到一段能解决眼前问题的代码,而是要把它搞懂,会用。这个目标着实有些难度,好在最近有点开窍了。尤其最近这两个帖子,可以在各种不同的解决方案之间进行对比学习,让我觉得一下子懂了不少新东西。


学习是从模仿开始的—感谢你们r的讲解!我会慢慢学会套用思路而不单纯模仿代码。

[ 本帖最后由 stance 于 2011-1-15 01:21 编辑 ]




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2