Board logo

标题: 如何用批处理按标记分割文件 [打印本页]

作者: yhsean     时间: 2005-12-5 13:02    标题: 如何用批处理按标记分割文件

  有一文件    AA.TXT
内容如下:

FGAFFFFSFF
GGFDGG
R*01
FSFF
HHH
R*02
FSDGF
HDFH
GHGH
JJHGJR
R*03

....

kljljfljfkj
fmlfk
R*n

请问如何将文件按照R*标记分割,名字按*后字符为名字

即:01.txt  内容为
01.txt
FGAFFFFSFF
GGFDGG

      02.txt  内容为
02.txt
FSFF
HHH

      03.txt  内容为
03.txt
FSDGF
HDFH
GHGH
JJHGJR

.....
     n.txt内容为
n.txt
kljljfljfkj
fmlfk


实际情况可能被分割的文件AA.TXT很大,分割的效率是务必考虑的

在此先感谢各位前辈们


[ Last edited by yhsean on 2005-12-8 at 13:33 ]
作者: willsort     时间: 2005-12-5 14:29
Re yhsean:

      使用批处理分割文件的效率不太可能很高,如果在其中使用第三方工具,则就脱离了批处理的意义。下面给出 cmd 下的例句,直接在命令行使用,使用了最简的原型,一些含有特殊符号的行可能不会正确的输出,比如含有*的非文件标记行。

for /f "tokens=1,2 delims=*" %f in (aa.txt) do @if "%f"=="R" (ren temp %g.txt) else (echo %f>>temp)
作者: yhsean     时间: 2005-12-5 18:39
谢版主回复
我也是用上面命令完成分割,但是当文件太大的时候,执行效率非常低,这样在实际工作中用的话,意义就不十分大了

刚来改论坛,感觉版主非常热心,再一次致谢。

贴出的目的在于能否找到一种更简洁有效的办法,不知道用DEBUG有没办法实现
作者: yhsean     时间: 2005-12-5 18:55
外请问版主如果使用第三方工具,如何在批处理中调用工具,(调用过程中,工具实现功能的时候强调完全自动——不能人工参与)

文件除了标记*外无别的*
作者: willsort     时间: 2005-12-5 19:02
Re yhsean:

      DEBUG 应该可以实现,只是会更复杂,效率会更低,而且 debug 本身对大文件的处理性能也不是很理想。

      批处理调用的第三方工具需要有一定的选择性,我们首选可以在命令行参数中进行全部操作的工具,其次才会考虑需要在运行过程中动态交互的工具,此时需要精心设计一个完备的应答文件。

      你的问题应该可以用 sed 简单的实现,只是现在没有时间了;明天中午之前,若无其它达人提供此方案的实现,我会考虑完成一个。
作者: yhsean     时间: 2005-12-6 08:54
天晚上下了个 SSED FOR WIN32 ,网上一番研究用法,介于行道太浅,未找出解决方案。因工作十分需要,期待willsort师之答案,感激不尽

另外众多SED版本中,是否SSED 会更好些
作者: 无奈何     时间: 2005-12-6 11:29
to yhsean
SED 是基于行的流编辑器不适合文本的分割。可以用 csplit 来完成这个分割任务。
下面给你提供一个方案,如下:
1、先执行下面的语句,分割文本。
csplit -z -b %02d.txt -f file AA.txt "/^R.*[0-9]+/+1" {*}
2、生成文件重命名对照文件。
findstr /r "^R.*[0-9]*" file*.txt>rename.txt
3、删除文件最后一行,并重命名文件。
sed -i.bak "$d" file*.txt
sed "s/^\(.*\):R[^0-9]*\([0-9]\+\)/ren \1 \2.txt/" rename.txt>rname.bat
rname.bat
说明:
1、以上提到的第三方工具可以从 GNU utilities for Win32 工具包中找到。
2、SED 过低版本不支持 -i 参数,请使用 GNU SED ,非 GNU SED 与其表达式有部分差异,我不能保证能得到正确结果。
3、你可以将上面语句存为批处理文件执行。
如有问题跟贴提出。
作者: yhsean     时间: 2005-12-6 23:38
谢谢解答,想必无奈何先生是专业程序员,我只是应用者,为完成方案,研究SED好长时间,找不到解,毕竟对于一个应用者来说,学习大量的工具是有难度的,介于SED命令的广泛性(自我感觉而已),所以肯请那位仁兄给出SED之解。

实际应用的时候为一应用软件动态产生该脚本,所以代码的简洁性有助于方便定制应用软件使其生成脚本。再次感谢无奈何先生
作者: yhsean     时间: 2005-12-7 13:17
于找到方案了
willsort师帮忙检查下代码能否在简洁一点
算法是否合理

因为 * 在SED有特殊意义,所以将 * 改为了 |


::net send * ***
echo off
set file=AA.txt
set/a m=1
set/a n=1
set/a L=0

for /f %%a in ('SED -n "/R|/p" ./%file%') do (set/a L+=1)
if %L%==1 (copy %file% %m%.ok&goto end)

SED "1,/R|/!d" ./%file% >%m%.ok
SED "1,/R|/d" ./%file% >%n%.tmp

:loop
    set/a m+=1
    set/a p=-1*%n%
    set/a L=0

    for /f %%a in ('SED -n "/R|/p" ./%n%.tmp') do (set/a L+=1)
    if %L%==1 (ren %n%.tmp %m%.ok&goto end)

    SED "1,/R|/!d" ./%n%.tmp >%m%.ok
    SED "1,/R|/d" ./%n%.tmp >%p%.tmp
    set/a n*=-1
goto loop
:end
DEL *.TMP
for %%a in (*.ok) do (
for /f "tokens=2 delims=|" %%b in ('SED -n "/R|/p" %%a') do (ren %%a %%b)
)
::net send * ***


net send只是利用消息时间差测试处理时间

[ Last edited by yhsean on 2005-12-7 at 14:25 ]
作者: willsort     时间: 2005-12-7 20:27
Re 无奈何:

      csplit 确实是个不错的文本模式分割工具,使用起来要比 sed 简单许多。但是从效率上来考虑,我认为他们相差不会很大,因为二者的核心都是对文本进行以行为单位的模式匹配。而从你的方案来看,除了第一步中对全文进行了扫描之外,查找文件名的findstr语句和删除最后一行的sed语句实际上也相当于扫描全文,三次全文扫描显然会降低效率。

      而理论上这个项目应该是可以通过一次扫描实现的,比如我最先提到的for /f方案,当然我目前尚未完成一次扫描的 sed 方案,主要困难在于如何给文件命名上。现在想来,通过 sed 来完成确实不是太好的方案。就目前得知,awk应该可以完成,只是对它仍很陌生。

Re yhsean:

      你的代码我没有仔细研究,但理解了你的大致思路,是分割出一个文件命名一个文件,然后再继续分割,这样实际上对文本扫描的次数更多了,效率应该更难保证。

      你提到了更改了匹配模式,那么是否意味着 aa.txt 的格式你是可以自由定制的?甚或你对 aa.txt 有着更多的操作自由度?若果然如此,我们可以继续改善这个模式,以更利于工具的匹配。
作者: tigerpower     时间: 2005-12-7 23:44
既然板主对awk很陌生,何以断定它可以完成任务呢
作者: yhsean     时间: 2005-12-8 13:01
谢版主,aa.txt是由一应用软件产生,其中最后行内容可以自己定制

另外被分割的文件有不确定的若干部分(由软件动态产生)

还有个问题就是实际中还要将分割后最后一行搬移到每一个分割后文件前面指定行(比如2行后)

真诚希望得到版主的帮助,SED也是猛肯了两天,别的工具是一点不懂啊

(不过尽管如此,SED多次扫描文件全文,但比起那个FOR /F也快十几倍)

[ Last edited by yhsean on 2005-12-8 at 13:11 ]
作者: 无奈何     时间: 2005-12-8 13:16
to tigerpower
这是个眼界问题,我猜想 willsort 兄所说的陌生只是相对AWK语法来说的,而并非一无所知。

to yhsean
我只是一名命令行爱好者。对于工具的学习与使用,我个人认为多接触一些是有好处的,某些任务用某一种工具很难完成,但换用其他工具可能很容易,更重要的是拓宽了思路顺便掌握了更多的知识与技巧。

=====
一直以来很畏惧 AWK ,几次尝试学习都退缩回来,至今未能入门。这次带着本帖的问题匆匆浏览了一些文章,发觉这东西真的很有意思,也并非如我原先想像中的难以掌握。因为是有针对性的阅读,所以最后学到的东西不多不少刚刚能够完成这个任务,也不知道语句是否合理,毕竟对 AWK 的了解太少了。
在命令行下执行 gawk -f AA.awk AA.txt
AA.awk 如下:
BEGIN { FS="\n"}
{
if ($1~/^R.*/) {
        name = substr($1,length($1)-1)
        temp = substr(temp,2)
        print temp>name".txt"
        temp = ""
        }
else {
        temp = (temp "\n" $1)
        }
}

作者: yhsean     时间: 2005-12-8 21:37
请  无奈何  或斑竹告诉如何用SED将文件的末行移动到前面指定行

经过改进代码如下:

@echo off
set file=aa.txt
set/a n=1
set/a L=0
:loop
sed -n "1,/R|/p" .\%file%|sed -n "$p" >tmp.tmp
for /f "tokens=3 delims=| " %%a in (tmp.tmp) do (set Tim=%%a)
for /f "tokens=3 delims=| " %%a in (tmp.tmp) do (set Nam=%%a.fanuc1)

sed -n "1,/R|/p" .\%file% >%Nam%
::判断已经处理的文件是否只有一部分(或是否为最后一部分)
for /f %%a in ('sed -n "/R|/p" .\%file%') do (set/a L+=1)
if %L%==1 goto end
set/a L=0
::取出第一部分后的部分;输出临时文件
sed -n "1,/R|/!p" .\%file% >%n%.tmp

::改变被处理的文件
set file=%n%.tmp
::反转临时文件以提供下次输出
set/a n*=-1
goto loop
:end
del *.tmp

[ Last edited by yhsean on 2005-12-8 at 21:40 ]
作者: 无奈何     时间: 2005-12-9 00:36
to yhsean
你应该掌握一些正则表达式的知识,关于特殊字符 * 可以这样 \* 脱掉其特殊性。
你提到的将文件的末行移动到前面指定行前,可以这样实现。
假定移至第三行前。
sed "$w temp.txt" aa.txt| sed -e "2rtemp.txt" -e "$d"
还是那个问题,如果你追求执行效率可以用下面的命令。
sed -e "3,${${p;x;D;p};$!H;d}" aa.txt

作者: willsort     时间: 2005-12-9 15:25
Re All:

首先,发布一则启事:

因本人的个人电脑现已搬至别处,与网络隔绝,以作封闭式训练之用,时间至少两周。此段时间内,只有闲暇时间可以在网吧或他人电脑处登录论坛,所以暂时恢复在线收发、离线阅复的上网习惯,因此必然会导致阅读频率降低、回复周期延长以及管理力度减弱,敬请管理层和广大朋友谅解!

tigerpower兄所言之情状,恰有以下三点感受,列述于下:以古语云,知其然而不知其所以然;用如今的观点讲,明白结果未必懂得过程;拿民间的俗话说,听过老虎会吃人的多,见过老虎咋吃人的少。

我于awk,算得上“鼎鼎大名,如雷贯耳;奈何天意弄人,缘悭一识”。这个源出于 Unix 家族的命令行工具,我在学习批处理的过程曾偶有涉猎,却总是未及深入。除却与无奈何兄相似的畏难避险的人之常情外,尚有一份维护批处理血统纯正的崖岸自高在内。但我亦深知,工具的价值,就是在适当的时间、适当的场合被适当的人所使用,一味逃避不过是掩耳盗铃的鸵鸟心态而已。现在,看到无奈何兄比我更早迈出了这一步,欣喜之余不免惭愧。

现已作决定,与其站在门外为看到热闹而喝彩,不如坐于阁内因识出三昧而击节——awk,是时候接触了。
作者: yhsean     时间: 2005-12-9 19:28
得到各位的帮助。太感谢了。请二位推荐 UNIX最 常用的命令

学习之,以解决实际工作诸多问题

向前辈致敬
作者: tigerpower     时间: 2005-12-9 20:34
原来如此,我还以为板主已经见到了偶上次用mawk的解法,呵呵.
本来是想看看各位如何用sed解题,所以就把那张贴给删了
作者: willsort     时间: 2005-12-11 21:58
Re tigerpower:

      我未见到兄的 mawk 的解法,论坛本来就是百花齐放之地,所以还有兄不要藏珍才好。

      今天下午,终于有空看了部分中文的 awk 文档,根据自己的初浅认识,仍然回应楼主最初的主题,编写了我的第一个 awk 脚本,因为与无奈何兄脚本尚有一些区别,所以贴上来,以抛砖引玉。

Re 无奈何:

      注意到兄的 awk 脚本中是采用一个 temp 变量来做中转的,与print>>相比,字符串变量的连接后再整体输出有可能效率更高一些吗?不知道楼主是否还有心思做一下这方面的测试?因为在我这里不太容易找到规模较大的测试文件。
{
    if ($1 !~ /^R\*/) {
        print $0>>"temp"
    } else {
        close("temp")
        system("ren temp "substr($0,3)".txt")
    }
}
[ Last edited by willsort on 2005-12-11 at 22:11 ]
作者: 无奈何     时间: 2005-12-11 23:43
to yhsean
关于你想了解“UNIX常用的命令”,我在 7 楼已经提到一个工具包,你可以找来玩玩。
to willsort
兄的学习速度够快的!中间变量的使用肯定会降低效率的,但那时是现学现用的,还不知道close()函数,而直接调用system命令更改文件名又失败,没有办法下只能投机应对,但那是我的第一个 AWK 程序,不打算修改了。最好兄能将你完整的程序贴出来,给本帖问题作个完美的解答吧。
作者: 无奈何     时间: 2005-12-12 14:19
to tigerpower
mawk 的速度和稳定性是很出众的,可遗憾的是资料太少了。
你的代码 gawk 不能通过,我觉得你的这段代码应该是通用的,结果换了低版本的 gawk 通过了,让我狂晕。
作者: tigerpower     时间: 2005-12-12 21:24


  Quote:
Originally posted by 无奈何 at 2005-12-12 14:19:
...结果换了低版本的 gawk 通过了,让我狂晕

哈哈,gawk我到是没试过。
GNU utilities for Win32这个东东我也用,不过其中的join有bug,兄弟们用起来要当心啊!
作者: 无奈何     时间: 2005-12-12 22:12
较新版本的 GAWK 我再也不敢用了,稳定性太差了。今天练习了一段程序,较新的几版  GAWK 居然得到匪夷所思的结果,而旧版和其它版的 AWK 完全正常。同样的脚本得到不同的结果,这比无法运行通过要严重的多,太难以让人接受了。
作者: tigerpower     时间: 2005-12-13 08:33
GNU的旗舰产品是gcc和emacs, 其他软件的代码质量和运行性能值得商榷.
但是在大部分Linux论坛里把Linux和GNU吹得天花乱坠,主观色彩太浓烈了.
平时我也用Linux, 有很多软件我觉得BSD版本的更为优秀
作者: Climbing     时间: 2005-12-13 09:20
看完这个帖子良久,我的大脑仍然是一片晕。Unix的东西实在搞得太复杂,非我这种简单的大脑可以理解的。
作者: yhsean     时间: 2005-12-21 12:54


  Quote:
Originally posted by 无奈何 at 2005-12-8 13:16:

BEGIN { FS="\n"}
{
if ($1~/^R.*/) {
        name = substr($1,length($1)-1)
        temp = substr(temp,2)
        print temp>name".txt"
        temp = ""
        }
else {
        temp = (temp "\n" $1)
        }
}


再次消化  无奈何兄脚本  发现可以这样使其简洁点

$0~/^R.*/?  { print temp>substr($1,3)".txt"; temp = "" } : temp = (temp $0 )

[ Last edited by yhsean on 2005-12-22 at 13:29 ]
作者: LiveOnLove     时间: 2005-12-21 14:43
看得有点晕。@_@
作者: yhsean     时间: 2005-12-28 19:41
不好意思,技术是严谨的,纠正我上次的一个错误

{
    if ($1 !~ /^R\*/) {
        print $0>>"temp"
    } else {
        close("temp")
        system("ren temp "substr($0,3)".txt")
    }
}

代码输出比用变量中转一次性输出快,我想应该这样去理解

不用中转变量,处理完就会释放内存中的信息,用中转变量的话,如果中转变量的容量过大的话,速度,直线下降,这在测试一个10M文本内容的出的结果

特此更正,以免误导人!
作者: vkill     时间: 2007-2-12 10:29
用sed的,原理上可以的,只是针对这个专门的
@echo off
set life=AA.txt
for /l %%? in (2,1,100) do (
call :test "%%?"
)
exit
:test
set/a var1=%~1-1
set/a var2=%~1-2
type %life%|findstr "^R\*[0]*%var1%$" >nul||(goto end)
if defined a (sed "/^R\*[0]*%var2%$/,/^R\*[0]*%var1%$/!d" %life% |sed "1d;$d"|more>%var1%.txt
) else (
set a=a
sed "1,/^R\*[0]*%var1%$/!d" %life% |sed "$d" |more>%var1%.txt
)
goto :eof
:end
sed "/^R\*[0]*%var2%$/,$!d" %life% |sed "1d"|more>n.txt
exit
goto :eof
[ Last edited by vkill on 2007-2-12 at 10:42 AM ]
作者: jmz573515     时间: 2007-2-12 11:11
发一个VBS的,揍个热闹
on error resume next
set fso=createobject("scripting.filesystemobject")
set d=createobject("scripting.dictionary")
set file=fso.opentextfile("a.txt")
do while file.atendofstream<>true
m=m+1
d.add m,file.readline
loop
file.close
a=d.items
for i=0 to m-1
  if left(a(i),2)<>"R*" then
      s=s&a(i)&vbcrlf
      h=mid(a(i+1),3)
  else
  set file=fso.createtextfile(h & ".txt")
  file.write s
  file.close
  s=""
  end if
next
msgbox "按标记“R*”分割文件成功。",4096,"系统提示"
[ Last edited by jmz573515 on 2007-2-11 at 10:35 PM ]
作者: jmzsyt     时间: 2007-3-2 02:30
思路不错但是bat不是用来做这个的啊