Board logo

标题: 请求高手解答 我的 难题(文本拆分) [打印本页]

作者: wusongccb     时间: 2008-10-22 10:36    标题: 请求高手解答 我的 难题(文本拆分)

按照版主要求,现将详细需求整理在此:

现有16G的文本文件,有130个字段,需要拆分成若干文件。

拆分规则: 按照 第117个字段的前三位命名并拆分。

我已研究出一个半成品代码,代码如下:

@echo off
sed.exe -f sed.txt aaa.txt > temp.txt
for /f "tokens=1,2* delims=|" %%a in (temp.txt) do (
for /f "tokens=25* delims=|" %%d in ("%%c") do (
for /f "tokens=25* delims=|" %%f in ("%%e") do (
for /f "tokens=25* delims=|" %%h in ("%%g") do (
for /f "tokens=25* delims=|" %%j in ("%%i") do (
for /f "tokens=15 delims=|" %%l in ("%%k") do (
  set "str=%%l"
  setlocal enabledelayedexpansion
  for %%z in (110 210 310) do (
    if "!str:~,3!"=="%%z" (
       >>%%z.txt echo %%a^|%%b^|%%c
    )
  )
  endlocal
)
)
)
)
)
)

但是该代码有个缺陷,%%z 并不止上述“110 210 310”三个,因此不能如此枚举出来,而是能否直接用第117个字段的前三位来命名。

同时目前还发现另外一个问题:
    文本文件中,有字段值为空,即两个管道符在一起(如:||),如此,FOR会认为所指定的字段并不在117位,程序默认空字段值不能赋值给一个变量,导致锁定第117个字段不准,因此我使用了个蠢办法,利用SED.EXE做一个全文本替换,将管道符“|”替换成空格+管道符(如:“ |”),如此就能锁定第117个字段,但是这样就会多一次I/O读写,效率大大降低。

论坛高手如云,且不吝赐教,感激不尽!!

[ Last edited by wusongccb on 2008-10-27 at 10:55 ]
作者: moniuming     时间: 2008-10-22 11:02

@echo off
setlocal enabledelayedexpansion
for /f "tokens=1,2* delims=|" %%a in ('type aaa.txt') do (
   set "str=%%b"
   for %%i in (110 210 310) do (
      if "!str:~,3!"=="%%i" (
         >>%%i.txt echo %%a^|%%b^|%%c
      )
   )
)

作者: tireless     时间: 2008-10-22 13:29

@echo off & setlocal EnableDelayedExpansion
for /f "delims=| tokens=1,2*" %%a in (aaa.txt) do (
  set str=%%b & set filename=!str:~,3!
  echo %%a^|%%b^|%%c>>!filename!.txt
)
有个疑问是,为什么:

  set str=%%b & set filename=!str:~,3!
  echo %%a^|%%b^|%%c>>!filename!.txt

不能换成:

  set str=%%b
  echo %%a^|%%b^|%%c>>!str:~,3!.txt
作者: HAT     时间: 2008-10-22 14:00    标题: Re 2楼

这样是不是要好些?
@echo off
for /f "tokens=1,2* delims=|" %%a in (aaa.txt) do (
  set "str=%%b"
  setlocal enabledelayedexpansion
  for %%i in (110 210 310) do (
    if "!str:~,3!"=="%%i" (
      >>%%i.txt echo %%a^|%%b^|%%c
    )
  )
  endlocal
)

作者: moniuming     时间: 2008-10-22 14:12    标题: 回3楼

经测试,确实不行...

[ Last edited by moniuming on 2008-10-22 at 14:23 ]
作者: 26933062     时间: 2008-10-22 14:37
这样是否更好
:
@echo off&setlocal enabledelayedexpansion
for /f "tokens=1,2* delims=|" %%a in ('type aaa.txt') do (
   set "str=%%b"
   >>!str:~0,3!.txt echo %%a^|%%b^|%%c
)

作者: tireless     时间: 2008-10-22 14:46    标题: Re 4 楼



  Quote:
@echo off
for /f "tokens=1,2* delims=|" %%a in (aaa.txt) do (
  set "str=%%b"
  setlocal enabledelayedexpansion
  for %%i in (110 210 310) do (
    if "!str:~,3!"=="%%i" (
      >>%%i.txt echo %%a^|%%b^|%%c
    )
  )
  endlocal
)

蓝色部分是不是放在 for 循环的外面好些?放在 for 循环里面的话,每套一次 for 循环,都进行 打开/关闭变量延迟扩展...
作者: HAT     时间: 2008-10-22 14:54    标题: Re 3楼

看完这篇文章,不知是否有所启发:
预处理究竟是如何进行的
http://bbs.verybat.org/viewthread.php?tid=9292

预处理这个问题为我大脑的新陈代谢做出过极大的贡献,然而始终无法窥其全貌。下面仅仅是我个人的一些点滴看法,在这里与各位分享,希望能通过讨论使我们对预处理的理解更进一步。

一、预处理究竟要做什么?

根据我的经验,预处理要做的是变量值的替换和特殊符号的处理。究竟先执行哪个操作呢,我认为要先进行变量值的替换。理由有二:

(一)

set var=2&echo %var%
类似于这样的语句,如果说先进行特殊符号处理的话,势必要先处理符号"&",而"&"是用来连接两条命令的,这样一来该行就理所应当的被理解为两句,那么我们还要变量延迟干嘛。这里应该是先对变量var赋值,然后处理特殊符号"&"。

(二)

@echo off
set var=^^^>
echo %var%
pause
这句"set var=^^^>"首先也会被预处理,预处理之后var的值为"^>"。(如果对脱字字符的处理过程还不了解的话,请参考相关帖子)
本例的输出结果是">",因此可以证明系统先将变量的值替换为"^>"然后再处理特殊符号"^"。

预处理特殊符号的目的是明确该行语句究竟要干什么。比如"echo hero >>hero.txt",预处理后就明确了这句的目的是向文件中写东西。再比如"echo hero &echo oreh",预处理之后就知道这其实是两条语句。我的结论就是--只需进行一次预处理,语句的功能(比如重定向输出)和结构(比如究竟是一整句还是两个分句)就得到明确。

二、启动了变量延迟之后预处理又是如何进行呢?

我的看法是这样的:如果语句中存在英文叹号"!"则会被预处理两次,其它情况仍然是预处理一次。举几个例子看看。

(一)

@echo off
echo !^^^^^>
setlocal enabledelayedexpansion
echo !^^^^^>
pause
两个echo语句的结果不同。下面做一下分析:
对于第一个echo语句,变量延迟没有开启,进行预处理的时候该句就被预处理为"echo !^^>",这也就是输出的结果。由此可见预处理只进行了一次。

对于第二个echo语句,此时变量延迟开启,由于有"!"存在,首先进行一次预处理得到"echo !^^>",再进行一次得到"echo ^>",结果也是如此。
之所以没有输出叹号,是因为开启了变量延迟,叹号就变为了特殊符号。

(二)

@echo off
setlocal enabledelayedexpansion
set var=hero
echo !var!
pause
像这里的"echo !var!"不是没有被预处理,而是被预处理了两次。看下面的这段代码就可以理解了。

@echo off
setlocal enabledelayedexpansion
set var=hero
echo !var!^^^^^>
pause
运行的结果为:"hero^>"。我们来分析一下,进行第一次预处理时,由于"!var!",因此先不替换变量值而进行特殊符号的处理,处理完后就成了"echo !var!^^>";之后再进行一次预处理,此时就要替换"!var!"了,处理完后就成了"echo hero^>"。

(三)
我们再来看看当变量延迟开启时语句中不存在英文叹号的情况。

@echo off
echo ^^^^^>
setlocal enabledelayedexpansion
echo ^^^^^>
pause

@echo off
set var=hero
echo %var%^^^^^>
setlocal enabledelayedexpansion
echo %var%^^^^^>
pause
怎么样,是不是验证了前面的话?

注意:第一次是对整行语句进行预处理,而第二次是对每个分句逐句的进行预处理。

三、变量延迟开启的情况下,究竟是"!var!"的替换先进行呢,还是先处理符号?

我的看法是先处理符号,再进行变量值的替换。理由如下

@echo off
set var=^^^^!
setlocal enabledelayedexpansion
echo !var!
pause
分析一下,这句"set var=^^^^!"是在变量延迟未开启的情况下运行的,因此只进行一次预处理,那么var的值就是"^^!"。程序运行的结果也正是"^^!",这就说明在"echo !var!"替换变量值之后系统并没有进行符号的处理,也就是说符号的处理是在替换变量值之前完成的。

从优先级的角度看,就是"%%">符号>"!!"。

以上三个部分,只是我个人的看法,由于没有官方文档的支持,因此仅供参考。

作者: HAT     时间: 2008-10-22 14:55    标题: Re 6楼

1. 这个方法3楼已经对提出疑问了。
2. 为何要坚持用type呢?
作者: moniuming     时间: 2008-10-22 14:57    标题: 回6楼

不行,你试过了吗?

  Quote:
'3.txt' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
'3.txt' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
'3.txt' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
'3.txt' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
'3.txt' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
'3.txt' 不是内部或外部命令,也不是可运行的程序
或批处理文件。


作者: HAT     时间: 2008-10-22 14:59    标题: Re 7楼

放在哪里更好呢?嗯,还是各取所需吧^_^
处理特殊字符的几种方法
http://bbs.bathome.cn/thread-1224-1-3.html

特殊字符的处理一直是困扰新手的一大难题,就算是老手,也不敢保证能对各种场合中的特殊字符应付自如,现在给出处理各种特殊字符的方法,供各位参考。

何谓特殊字符?一般而言,我们把已经在cmd中赋予了各种功能的符号称为特殊字符,比如>、>>、<、<<、|、||、&、&&、^、%,另外,在处理文件路径的时候,空格也可以算作特殊字符。

很多人都知道,要正确处理路径中的空格,需要用引号把路径括起来,比如:md "c:\test abc\123"。实际上,只要文件路径中含有特殊字符,则引号对是必须的。

可能还有些人知道,要输出百分号本身,需要在它之前再添加一个百分号,比如,要显示 %abc% 这个字符串,应该写成 echo %%abc%%。

但是,当我们要输出>、<、|之类的特殊符号的时候,大多数人就开始绞尽脑汁了。

首先,我们要请出转义字符^,它能帮我们应付绝大多数的特殊字符。例如,我们要显示>,echo ^>即可;要显示|,echo ^|即可,也就是说,在每个特殊字符前加上转义字符^,就可以对特殊字符进行原样输出了。这次,你会原样输出 aueou>|&a^ueo 之类的字符串了吗?

但是,转义字符^也不能包打天下:当需要输出的是已知的一串或多行字符串的时候,确实可以在每个特殊字符前添加^来原样输出,可是,如果特殊字符的位置不确定,或是我们需要从某个文本中原样读出一串或多行可能含有特殊字符的未知字符串的时候,我们还能用插入转义符号^的办法吗?这显然是行不通的。

或许你已经意识到,就算是对含有特殊符号的路径用引号对括起来以保证处理它的时候不至出错,但是,当要用echo语句显示这个路径的时候,引号对还是会显示出来,这个多余的引号对并不是我们需要的,有时候甚至是我们无法容忍的。

转义字符^无用武之地,引号对也行不通,难道我们就这样彻底绝望了吗?

这次,该for语句隆重出场了。

先看示例:

@echo off
for /f "delims=" %%i in (test.txt) do (
  set "str=%%i"
  setlocal enabledelayedexpansion
  echo.!str!
  endlocal
)
pause

test.txt内容如下(已经囊括了特殊字符):
aoueoa & aeo"83
aeouoaeA3ua^|!:>
><||a6^$2oueo
%ae!aoue

在这里,我动用了各种手段以保证特殊字符能够原样显示:
① 用 "delims=" 避免取到的字符串从空格处被截断;
② 用 set "str=%%i" 这样的引号对写法保证把字符串原样赋予变量 str ;
③ 在适当的位置开启变量延迟并在合适的地方终止变量延迟,以保证能原样显示变量 str 的值。在这一点上,很多人,包括很多熟练使用 setlocal enabledelayedexpansion 语句的人,都会忽略一些技术细节,从而不能100%原样显示变量 str 的值:我发现,绝大多数的批处理老鸟,都喜欢在@echo off和for语句之间使用 setlocal enabledelayedexpansion 语句,这是个非常不好的习惯——如果变量的值中含有半角感叹号的话,感叹号将被直接抛弃!正确的做法是:在for语句内部而不是外部开启变量延迟,并在合适的地方用endlocal语句终止延迟——endlocal一定要添加上,这是个好习惯,否则,当 setlocal enabledelayedexpansion 被执行32次之后,将会报错,从而引发后续处理错误。

当然,就算我动用了这么多手段,也不能保证能处理文本中的所有特殊情况:当行首为分号的时候、当存在空行的时候……

如果要处理所有的特殊情况,请使用如下代码:

@echo off
cd.>output.txt
for /f "delims=" %%i in ('findstr /n .* test.txt') do (
  set "var=%%i"
  setlocal enabledelayedexpansion
  set var=!var:*:=!
  (echo.!var!)>>output.txt
  endlocal
)
start output.txt

作者: wusongccb     时间: 2008-10-22 18:29    标题: 非常感谢各位高手

输入的文本是追加式的,能改成替换方式的吗
作者: wusongccb     时间: 2008-10-22 18:32
还有我处理的 aaa.txt有 16个G这么大,用2楼的脚本可以吗?
作者: wusongccb     时间: 2008-10-22 18:34
高手如云,真是个好论坛
作者: wusongccb     时间: 2008-10-22 18:51
还有个巨大的困难,各位高手

实际情况是 分隔符的 第 117个字段(例子是第2个字段),发掘26个字段后不知道用什么字母来代替
作者: 26933062     时间: 2008-10-22 19:49
tokens= 最多只能是 32 段。
1、你的文本每行都有117段吗?
2、文本内容有特殊字符吗?
作者: longge0000     时间: 2008-10-25 15:05    标题: Re 3楼



  Quote:
Originally posted by tireless at 2008-10-22 13:29:
[code]@echo off & setlocal EnableDelayedExpansion
for /f "delims=| tokens=1,2*" %%a in (aaa.txt) do (
  set str=%%b & set filename=!str:~,3!
  echo %%a^|%%b^|%%c>>!filena ...

3楼的问题:
有个疑问是,为什么:

  set str=%%b & set filename=!str:~,3!
  echo %%a^|%%b^|%%c>>!filename!.txt

不能换成:

  set str=%%b
  echo %%a^|%%b^|%%c>>!str:~,3!.txt

因为>>后面的逗号会被看成空格,这样把文件名成了!str:~文件名不能有:
改成下面可通过!  
set str=%%b
echo %%a^|%%b^|%%c>>!str:~^,3!.txt

[ Last edited by longge0000 on 2008-10-26 at 14:00 ]
作者: HAT     时间: 2008-10-25 18:43
请把13楼、15楼等关于这个问题的具体信息全部整理到1楼,并描述清楚,最好能够给出一部分真实数据,这样会有助于别人帮你写代码。
作者: ABC123321     时间: 2009-3-8 13:01
这么多,看的眼花,细讲下Delims!有分哦
作者: ABC123321     时间: 2009-3-8 13:02
鼓励一下自己,一定会赶上你们
作者: ABC123321     时间: 2009-3-8 13:05
Delims默认分割符是什么?
作者: ABC123321     时间: 2009-3-8 13:07
HAT老大回答一下?,解围!不甚感谢
作者: ABC123321     时间: 2009-3-8 13:09
哎呀!咋没有反映?高手帮助菜鸟一下求你了
作者: HAT     时间: 2009-3-11 08:16    标题: Re 21楼

帮助里面写的很清楚:
for /?
作者: yixlin     时间: 2009-12-11 21:51
学习了,支持支持,多谢