批处理之家's Archiver

batman 发表于 2008-7-24 00:29

【练习-001】批处理实现两文本同行交替输出

[color=red]
从今天开始,本人将陆续推出文本输出类题目给新手做,并会根据解答思路和解答过程酌情
加分(解题思路为重),希望广大新手放开思路踊跃讨论和解题,本人也会在其中发表自己的
观点,并予以新人以指点的,好了不多说了,下面是第一题:
[/color]
[color=blue]
有两个文本如下(实际中并不知道两文本各有多少行):
文本1.txt[code]aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
ccccccccccccccccccccccccccccccccccccccc
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
ggggggggggggggggggggggggggggggggggggggg
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz[/code]文本2.txt[code]hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn[/code]要求用批处理输出如下(两文本交替输出):[code]aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
ccccccccccccccccccccccccccccccccccccccc
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
ggggggggggggggggggggggggggggggggggggggg
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz[/code]要求:
1 尽量不生成临时文件
2 代码要高效
3 代码尽量简洁
目的:
唯一的目的在于共同提高!
[color=green][/color][color=green]----------------------------------------------------------------------------------------------------------------------------------------------------
[color=#0000ff]到目前为止,已经有了两个较为满意的答案:[/color]
[color=#0000ff]第一种是先判断行数采用变量赋值的方法,[/color][color=#0000ff]见14楼more和15楼本人的代码。[/color]
[color=#0000ff]特点:通用性强,代码简洁且效率较高。[/color]
[color=#0000ff]第二种是不判断行数采用变量控制的方法,见16楼pusofalse版主和18楼本[/color][color=#0000ff]人的代码。[/color]
[color=#0000ff]特点:通用性强,代码简洁且效率最高。[/color][/color][/color]
[color=blue][color=green][color=#0000ff]第三种是生成临时文件的方案,见24楼本人的代码。[/color][/color][/color]
[color=blue][color=green][color=#0000ff]特点:思路较独特[/color]
[color=#0000ff]希望大家充分放开思路,看看还有别的解决方案不,只要是按要求做出来的,一律加分。[/color]

[color=#0000ff][/color][/color][/color]

[[i] 本帖最后由 batman 于 2008-7-27 23:58 编辑 [/i]]

浅默 发表于 2008-7-24 05:56

@echo off
for /f "tokens=1* delims=:" %%i in ('findstr /n .* 1.txt') do (
    echo %%j>>3.txt
    for /f "tokens=1* delims=:" %%a in ('findstr /n .* 2.txt') do (
        if %%i==%%a echo %%b>>3.txt
    )
)   
pasle

batman 发表于 2008-7-24 08:05

首先对二楼做为鼓励予以加分,同时对存在的问题予以指出:
二楼的方法存在以下的问题:
1 首先我们不知道两个文本各有多少行,如果1.txt的行数比2.txt少,而你的代码中for循环到
了1.txt最后一行就终止了,那么2.txt多出的行就全部漏掉了。
2 如果1.txt中的行数比2.txt少,二楼的代码也存在效率问题,在上面的代码中调用for的次数
是1.txt行数乘上2.txt的行数,而理论上最少的调用次数是两文本行数的和。
3 if %%i==%%a改为if "%%i"=="%%a"实为妥当些。
4 最后的pause出现了笔误。

[[i] 本帖最后由 batman 于 2008-7-24 15:25 编辑 [/i]]

随风 发表于 2008-7-24 13:38

纠正一点,2楼并没有产生临时文件,他只是输出到文件,不算违规。
此题,说难不难,说简单也不简单,不过以“浅默”的功力似乎不应该只是如此。。。
大家都来试试。。。看谁最先给出完美答案。。







.

batman 发表于 2008-7-24 15:26

[quote]原帖由 [i]随风[/i] 于 2008-7-24 13:38 发表 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=5868&ptid=1239][img]http://bbs.bathome.net/images/common/back.gif[/img][/url]
纠正一点,2楼并没有产生临时文件,他只是输出到文件,不算违规。
[/quote]
对不起,是我搞错了,已更正。

pusofalse 发表于 2008-7-25 00:01

这题应该是1.txt总是比2.txt多吧?

随风 发表于 2008-7-25 00:21

[quote]原帖由 [i]pusofalse[/i] 于 2008-7-25 00:01 发表 [url=http://bathome.net/redirect.php?goto=findpost&pid=5879&ptid=1239][img]http://bathome.net/images/common/back.gif[/img][/url]
这题应该是1.txt总是比2.txt多吧? [/quote]

未必,也有可能是 2.txt 比 1.txt 的行数多。

batman 发表于 2008-7-25 00:35

给个小提示:
可以先判断两个文本哪个的行数多,再把行多的文本放到第一个for中。

同时也可以不判断文本的行数,但思路就要开放点了。

本人暂时想出了三个解决方案,暂不贴出,希望大家能有更好的办法。

pusofalse 发表于 2008-7-25 00:36

这类的交错输出的文本题比较经典,不确定到底那个多,岂不是要先得读出行数多或行数少的那个?

batman 发表于 2008-7-25 00:44

是不是可以换个思路:判断和输出同时进行呢?

随风 发表于 2008-7-25 00:49

也可以根本不必要判断文本的行数。

pusofalse 发表于 2008-7-25 01:10

的确如此 思维定向了。

youxi01 发表于 2008-7-25 08:23

不过有个疑问就是:效率是不是会非常低呢?特别是遭遇大文件时
根据代码的意思,似乎是查询两个文件行号一样的时候就分别写入到文件里

这样的话,要是第二个文件有1W行岂不是要比对1W次?这样,明显有9999次是没有必要的...

more 发表于 2008-7-25 10:38

想得头都要破了

[code]@echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('type 11.txt') do (
   set /a m+=1
   set "home!m!=%%a"
)
for /f "delims=" %%b in ('type 12.txt') do (
   set /a n+=1
   set "bat!n!=%%b"
)
if %m% gtr %n% (
   for /l %%c in (1 1 %m%) do (
      if not "!home%%c!"=="" echo !home%%c!
      if not "!bat%%c!"=="" echo !bat%%c!
   )
) else (
   for /l %%d in (1 1 %n%) do (
      if not "!home%%d!"=="" echo !home%%d!
      if not "!bat%%d!"=="" echo !bat%%d!
   )
)
pause[/code]

[[i] 本帖最后由 more 于 2008-7-25 10:44 编辑 [/i]]

batman 发表于 2008-7-25 11:40

[quote]原帖由 [i]more[/i] 于 2008-7-25 10:38 发表 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=5888&ptid=1239][img]http://bbs.bathome.net/images/common/back.gif[/img][/url]
@echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('type 11.txt') do (
   set /a m+=1
   set "home!m!=%%a"
)
for /f "delims=" %%b in ('type 12.txt') do (
   set /a n+=1
   set  ... [/quote]
代码可以简化,下面先公布我的第一种方法:[code]@echo off
for /f "delims=" %%i in (1.txt) do set /a n+=1&call,set "_%%n%%=%%i"
for /f "delims=" %%i in (2.txt) do set /a m+=1&call,set ".%%m%%=%%i"
if %n% gtr %m% (set "num=%n%") else (set "num=%m%")
for /l %%i in (1,1,%num%) do (
     if defined _%%i call,echo %%_%%i%%
     if defined .%%i call,echo %%.%%i%%
)
pause>nul[/code]

[[i] 本帖最后由 batman 于 2008-7-25 17:05 编辑 [/i]]

pusofalse 发表于 2008-7-25 16:44

@echo off&setlocal enabledelayedexpansion&set n=-1
for /f "delims=" %%a in (1.txt) do (
set/a n+=1
set flag=
call :lp %%a
if not defined flag echo %%a
)
:lp
if "%1" equ "" set/a n+=1
set m=skip=%n%
if "%m%" equ "skip=0" set "m="
for /f "%m% delims=" %%a in (2.txt) do if "%1" neq "" (echo %1&echo %%a&set flag=a&goto :eof) else echo %%a
if "%1" equ "" pause

[[i] 本帖最后由 pusofalse 于 2008-7-25 16:55 编辑 [/i]]

batman 发表于 2008-7-25 16:46

[quote]原帖由 [i]youxi01[/i] 于 2008-7-25 08:23 发表 [url=http://bbs.bathome.net/redirect.php?goto=findpost&pid=5886&ptid=1239][img]http://bbs.bathome.net/images/common/back.gif[/img][/url]
不过有个疑问就是:效率是不是会非常低呢?特别是遭遇大文件时
根据代码的意思,似乎是查询两个文件行号一样的时候就分别写入到文件里

这样的话,要是第二个文件有1W行岂不是要比对1W次?这样,明显有9999次是没 ... [/quote]
我想在这里应该不有去考虑极端情况,比如特殊字符处理什么的。

batman 发表于 2008-7-25 17:24

既然这种最高效的方法也出来了(只可惜是版主级做出来的),下面就贴出本人的第二种方法:[code]@echo off
set "n=-1"&set "flag=a"
:begin
set /a n+=1
if %n% equ 0 (set "m=") else (set "m=skip=%n%")
for /f "%m% delims=" %%i in (1.txt) do echo %%i&if defined flag goto next
set no=a
:next
for /f "%m% delims=" %%i in (2.txt) do echo %%i&goto begin
if not defined no set "flag="&goto begin
pause>nul[/code]

pusofalse 发表于 2008-7-25 17:34

为了减少不必要的调用,修改如下。[code]
@echo off&set n=-1
for /f "delims=" %%a in (1.txt) do (
set/a n+=1
set flag=
if not defined faith call :lp %%a
if not defined flag echo %%a
)
:lp
if "%1" equ "" set/a n+=1
set m=skip=%n%
if "%m%" equ "skip=0" set "m="
for /f "%m% delims=" %%a in (2.txt) do if "%1" neq "" (echo %1&echo %%a&set flag=a&goto :eof) else echo %%a
set faith=faith
if "%1" equ "" pause[/code]

batman 发表于 2008-7-25 18:16

再次提示:
第三种方法可就本题而言来解(不考虑行数太多的情况)

浅默 发表于 2008-7-25 19:25

@echo off
for /f "tokens=1* delims=:" %%i in ('findstr /n .* 1.txt') do set _%%i=%%j
for /f "tokens=1* delims=:" %%k in ('findstr /n .* 2.txt') do set _%%k.%%k=%%l
for /f "tokens=1* delims==" %%i in ('set _ ') do echo %%j
pause
要是就本题的几行上面的还行

namejm 发表于 2008-7-25 20:56

  把两个文本的相同行交叉合并,需要考虑以下两种情况:
  1、两个文本的行数相同;
  2、两个文本的行数不同。

  如果两个文本行数相同,则可以省略对文本行数的判断,但考虑到效率高低,需要使用临时文件,则可使用如下代码:[code]@echo off
findstr .* 1.txt>_1.txt
setlocal enabledelayedexpansion
for /f "delims="  %%i in (2.txt) do (
    set /a num+=1
    echo !num!:%%i
    findstr /b /i "!num!:" _1.txt
)
del /q _1.txt
pause[/code]  如果两个文本行数不同,则需要判断行数谁多谁少,从而减少对比次数,演示代码如下:[code]@echo off
:: 生成临时文件,主要是为了获得 行数:行内容 格式的文本
findstr /n .* 1.txt>_1.txt
findstr /n .* 2.txt>_2.txt

:: 获取各自的总行数
for /f "delims=:" %%i in (_1.txt) do set num_1=%%i
for /f "delims=:" %%i in (_2.txt) do set num_2=%%i

:: 比较行数谁多谁少
set num=%num_1%
set file_1=_1.txt&set file_2=_2.txt
if %num_1% geq %num_2% (
    set num=%num_2%
    set file_1=_2.txt&set file_2=_1.txt
)

:: 以行数少的文本为标准,交叉输出两文本的同行内容
for /f "tokens=1* delims=:" %%i in (%file_1%) do (
    echo %%i:%%j
    findstr /b /i "%%i:" %file_2%
)

:: 对多出的行内容,一次性输出,减少了对比次数,从而提高效率
more +%num% %file_2%
del /q _1.txt _2.txt
pause[/code]  如果不动用临时文件,要么需要设置大量的变量,严重消耗系统内存;要么需要用 if 语句对行号进行机械对比,严重影响效率;如果使用 set 排序方案,则要求每个文本的总行数不能超过9行,并且不能保证某个文件的行内容总是排在两行中的第一行或第二行。
  注:以上代码都没有考虑特殊字符。

pusofalse 发表于 2008-7-25 21:09

jm厉害,考虑这么全面。

batman 发表于 2008-7-26 20:39

最后给出本人的第三个方案,生成临时文件且通用性不很强,
解题思路和上面的完思路不同:[code]@echo off&setlocal enabledelayedexpansion
set "num=0"
for %%a in (1.txt 2.txt) do (
    for /f "delims=" %%i in (%%a) do (
        set /a n+=1
        if !n! gtr !num! set "num=!n!"
        set /p=%%i <nul>>temp.txt
    )
    echo.>>temp.txt&set "n=0"
)
:lp
set /a n+=1
for /f "tokens=%n%" %%i in (temp.txt) do echo %%i
if %n% neq %num% goto lp
del /q temp.txt&pause>nul[/code]

keen 发表于 2009-4-16 19:56

我的,有点晚:[code]@echo off&setlocal enabledelayedexpansion
set m=-1
for /f %%i in (1.txt) do set /a m+=2&set _!m!=%%i
for /f %%j in (2.txt) do set /a n+=2&set _!n!=%%j
if %m% gtr %n% (set max=%m%) else (set max=%n%)
for /l %%k in (1 1 %max%) do (
    if defined _%%k call,echo %%_%%k%%
)
pause[/code]

keen 发表于 2009-4-16 20:11

batman就是强!
我的跟batman的第一种方法(15楼)基本一样了。
batman的第二种方法(见18楼),更是高手典范。

batman 发表于 2009-4-20 08:10

呵呵,本人最老的一道题也被你们翻出来了,加油,有这股劲就对了!

netbenton 发表于 2009-4-29 23:06

[code]
@echo off&setlocal enabledelayedexpansion
for /f "delims="  %%a in (1.txt) do (set /a n+=1&set #!n!=%%a)
for /f "delims=" %%a in (2.txt) do (
    set/a m+=1
    echo %%a
    if defined #!m! for %%b in (!m!) do echo !#%%b!
)
set/a m+=1
for /l %%a in (!m!,1,!n!) do echo !#%%a!
[/code]

everest79 发表于 2009-4-30 02:15

一百行以内[code]@echo off
for /f "tokens=1* delims=[]" %%a in ('find /v /n "" ?.txt^|findstr /b "\[[0-9]\]"^|sort') do echo.%%b >>out.txt
for /f "tokens=1* delims=[]" %%a in ('find /v /n "" ?.txt^|findstr /b "\[[0-9][0-9]\]"^|sort') do echo.%%b >>out.txt

pause[/code]

netbenton 发表于 2009-4-30 07:38

楼上的不一定会交替的吧?
有可能会这样的
a.txt  行
b.txt  行
b.txt  行
a.txt  行

页: [1] 2

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.