Windows批处理脚本用于解析CSV文件并输出文本文件

Jef*_*ebb 6 windows csv cmd batch-file

我在另一个页面上看到了一个响应(帮助编写一个批处理脚本来解析CSV文件并输出一个文本文件) - 精彩的代码BTW:

@ECHO OFF
IF "%~1"=="" GOTO :EOF
SET "filename=%~1"
SET fcount=0
SET linenum=0
FOR /F "usebackq tokens=1-10 delims=," %%a IN ("%filename%") DO ^
CALL :process "%%a" "%%b" "%%c" "%%d" "%%e" "%%f" "%%g" "%%h" "%%i" "%%j"
GOTO :EOF

:trim
SET "tmp=%~1"
:trimlead
IF NOT "%tmp:~0,1%"==" " GOTO :EOF
SET "tmp=%tmp:~1%"
GOTO trimlead

:process
SET /A linenum+=1
IF "%linenum%"=="1" GOTO picknames

SET ind=0
:display
IF "%fcount%"=="%ind%" (ECHO.&GOTO :EOF)
SET /A ind+=1
CALL :trim %1
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO !f%ind%!!tmp!
ENDLOCAL
SHIFT
GOTO display

:picknames
IF %1=="" GOTO :EOF
CALL :trim %1
SET /a fcount+=1
SET "f%fcount%=%tmp%"
SHIFT
GOTO picknames
Run Code Online (Sandbox Code Playgroud)

它非常出色地用于我以下格式制作的示例csv文件:

Header,Name,Place
one,two,three
four,five,six
Run Code Online (Sandbox Code Playgroud)

但是,我想要更改的实际文件包含64个字段 - 因此我更改了tokens=1-10to tokens=1-64并将%%a等增加到最多64个变量(%%BL例如,最后一个被调用).但是,现在,当我在我的'大'csv文件(带有64个令牌)上运行批处理时,没有任何反应.没有错误(好)但没有输出!(坏).如果有人可以提供帮助,那就太棒了......如果我可以指出这最后一点,那就太接近整个应用了!或者,如果有人有一些示例代码将对无限数量的令牌执行类似的操作...最终我想创建一个类似于以下内容的字符串:

field7,field12,field15,field18
Run Code Online (Sandbox Code Playgroud)

dbe*_*ham 16

重要更新 - 我不认为Windows批处理是一个很好的选择,因为单个FOR/F无法解析超过31个令牌.请参阅下面的附录底部以获得解释.

但是,可以通过批量执行您想要的操作.这个丑陋的代码将允许您访问所有64个令牌.

for /f "usebackq tokens=1-29* delims=," %%A in ("%filename%") do (
  for /f "tokens=1-26* delims=," %%a in ("%%^") do (
    for /f "tokens=1-9 delims=," %%1 in ("%%{") do (
      rem Tokens 1-26 are in variables %%A - %%Z
      rem Token  27 is in %%[
      rem Token  28 is in %%\
      rem Token  29 is in %%]
      rem Tokens 30-55 are in %%a - %%z
      rem Tokens 56-64 are in %%1 - %%9
    )
  )
)
Run Code Online (Sandbox Code Playgroud)

该附录提供了有关上述工作原理的重要信息.

如果您只需要在64行中分配一些令牌,那么解决方案可能会稍微容易一些,因为您可以避免将疯狂字符用作FOR变量.但仍有细心的簿记要做.

例如,以下内容将允许您访问令牌5,27,46和64

for /f "usebackq tokens=5,27,30* delims=," %%A in ("%filename%") do (
  for /f "tokens=16,30* delims=," %%E in ("%%D") do (
    for /f "tokens=4 delims=," %%H in ("%%G") do (
      rem Token  5 is in %%A
      rem Token 27 is in %%B
      rem Token 46 is in %%E
      rem Token 64 is in %%H
    )
  )
)
Run Code Online (Sandbox Code Playgroud)

20164月更新 - 基于DosTips用户Aacini,penpen和aGerman的调查工作,我开发了一种相对简单的方法,可以使用FOR/F同时访问数千个令牌.这项工作是这个DosTips线程的一部分.实际代码可以在这3个帖子中找到:

原始答案 FOR变量仅限于单个字符,因此您的%% BL策略无法正常工作.变量区分大小写.根据微软的说法,你只能在一个FOR语句中捕获26个令牌,但如果你使用的不仅仅是alpha,那么它可能会获得更多.这很痛苦,因为你需要一个ASCII表来确定哪些字符去哪里.FOR不允许任何字符,并且单个FOR/F可以分配的最大令牌数是31 + 1.正如您所发现的那样,任何解析和分配超过31的尝试都会悄然失败.

谢天谢地,我认为你不需要那么多令牌.您只需使用TOKENS选项指定所需的标记即可.

for /f "usebackq tokens=7,12,15,18 delims=," %%A in ("%filename%") do echo %%A,%%B,%%C,%%D
Run Code Online (Sandbox Code Playgroud)

会给你7号,12号,15号和18号代币.

附录

2016年4月更新 几周前,我了解到以下规则(6年前编写)依赖于代码页.下面的数据已经针对 代码页437和850 进行了验证.更重要的是,扩展ASCII字符128-254的FOR变量序列与字节代码值不匹配,并且在代码页中变化很大.事实证明,FOR/F变量映射基于底层的UTF-(16?)代码点.因此,与FOR/F一起使用时,扩展的ASCII字符用途有限.有关详细信息,请参阅http://www.dostips.com/forum/viewtopic.php?f=3&t=7703中的主题.

我进行了一些测试,并可以报告以下内容(根据jeb的评论更新):

大多数字符可以用作FOR变量,包括扩展的ASCII 128-254.但是某些字符不能用于在FOR语句的第一部分中定义变量,但可以在DO子句中使用.一些也不能用于.有些没有限制,但需要特殊的语法.

以下是具有限制或需要特殊语法的字符的摘要.请注意,尖括号内的文本<space>表示单个字符.

Dec  Hex   Character   Define     Access
  0  0x00  <nul>       No         No
 09  0x09  <tab>       No         %%^<tab>  or  "%%<tab>"
 10  0x0A  <LF>        No         %%^<CR><LF><CR><LF>  or  %%^<LF><LF>
 11  0x0B  <VT>        No         %%<VT>
 12  0x0C  <FF>        No         %%<FF>
 13  0x0D  <CR>        No         No
 26  0x1A  <SUB>       %%%VAR%    %%%VAR% (%VAR% must be defined as <SUB>)
 32  0x20  <space>     No         %%^<space>  or  "%%<space>"
 34  0x22  "           %%^"       %%"  or  %%^"
 36  0x24  $           %%$        %%$ works, but %%~$ does not
 37  0x25  %           %%%%       %%~%%
 38  0x26  &           %%^&       %%^&  or  "%%&"
 41  0x29  )           %%^)       %%^)  or  "%%)"
 44  0x2C  ,           No         %%^,  or  "%%,"
 59  0x3B  ;           No         %%^;  or  "%%;"
 60  0x3C  <           %%^<       %%^<  or  "%%<"
 61  0x3D  =           No         %%^=  or  "%%="
 62  0x3E  >           %%^>       %%^>  or  "%%>"
 94  0x5E  ^           %%^^       %%^^  or  "%%^"
124  0x7C  |           %%^|       %%^|  or  "%%|"
126  0x7E  ~           %%~        %%~~ (%%~ may crash CMD.EXE if at end of line)
255  0xFF  <NB space>  No         No
Run Code Online (Sandbox Code Playgroud)

特殊字符^ < > | &必须是转义或引用.例如,以下工作:

for /f %%^< in ("OK") do echo "%%<" %%^<
Run Code Online (Sandbox Code Playgroud)

某些字符不能用于定义FOR变量.例如,以下内容给出了语法错误:

for /f %%^= in ("No can do") do echo anything
Run Code Online (Sandbox Code Playgroud)

但是%%=可以通过使用TOKENS选项隐式定义,并且在DO子句中访问的值如下:

for /f "tokens=1-3" %%^< in ("A B C") do echo %%^< %%^= %%^>
Run Code Online (Sandbox Code Playgroud)

%是奇数-您可以定义一个FOR变量使用%%%%.但除非使用~修饰符,否则无法访问该值.这意味着无法保留封闭引号.

for /f "usebackq tokens=1,2" %%%% in ('"A"') do echo %%%% %%~%%
Run Code Online (Sandbox Code Playgroud)

以上收益率 %% A

~是一个潜在危险的FOR变量.如果您尝试%%~在一行的末尾使用变量,您可能会得到不可预测的结果,甚至可能会崩溃CMD.EXE!唯一可靠的访问方式是使用%%~~,当然会删除任何封闭的引号.

for /f %%~ in ("A") do echo This can crash because its the end of line: %%~

for /f %%~ in ("A") do echo But this (%%~) should be safe

for /f %%~ in ("A") do echo This works even at end of line: %%~~
Run Code Online (Sandbox Code Playgroud)

<SUB>因为(0x1A的)字符是特殊的<SUB>嵌入批处理脚本中的文字被解读为换行符(<LF>).为了<SUB>用作FOR变量,该值必须以某种方式存储在环境变量中,然后%%%VAR%才能用于定义和访问.

如前所述,单个FOR/F可以解析并分配最多31个令牌.例如:

@echo off
setlocal enableDelayedExpansion
set "str="
for /l %%n in (1 1 35) do set "str=!str! %%n"
for /f "tokens=1-31" %%A in ("!str!") do echo A=%%A _=%%_
Run Code Online (Sandbox Code Playgroud)

以上产量A=1 _=31 注意 - 令牌2-30工作得很好,我只想要一个小例子

如果不设置ERRORLEVEL,任何解析和分配超过31个令牌的尝试都将无声地失败.

@echo off
setlocal enableDelayedExpansion
set "str="
for /l %%n in (1 1 35) do set "str=!str! %%n"
for /f "tokens=1-32" %%A in ("!str!") do echo this example fails entirely
Run Code Online (Sandbox Code Playgroud)

您可以解析并分配最多31个令牌,并将余数分配给另一个令牌,如下所示:

@echo off
setlocal enableDelayedExpansion
set "str="
for /l %%0 in (1 1 35) do set "str=!str! %%n"
for /f "tokens=1-31*" %%@ in ("!str!") do echo @=%%A  ^^=%%^^  _=%%_
Run Code Online (Sandbox Code Playgroud)

以上收益率 @=1 ^=31 _=32 33 34 35

现在是真正的坏消息.单个FOR/F永远不会解析超过31个令牌,正如我在Windows批处理脚本中查看FOR命令中的令牌数量限制时所学到的

@echo off
setlocal enableDelayedExpansion
set "str="
for /l %%n in (1 1 35) do set "str=!str! %%n"
for /f "tokens=1,31,32" %%A in ("!str!") do echo A=%%A  B=%%B  C=%%C
Run Code Online (Sandbox Code Playgroud)

非常不幸的输出是 A=1 B=31 C=%C