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

用于解析CSV文件并输出文本文件的Windows批处理脚本,windows,csv,batch-file,cmd,Windows,Csv,Batch File,Cmd,我在另一页上看到了回应()-精彩代码顺便说一句: @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 :

我在另一页上看到了回应()-精彩代码顺便说一句:

@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
对于我以以下格式制作的示例csv文件,它非常有效:

Header,Name,Place
one,two,three
four,five,six
但是,我要更改的实际文件包含64个字段-因此我将
标记=1-10
更改为
标记=1-64
,并将
%%a
等增加到64个变量(例如,最后一个变量被称为
%%BL
)。然而,现在,当我在我的“大”csv文件(带有64个令牌)上运行批处理时,什么也没发生。没有错误(良好),但没有输出!(坏)。如果有人能帮忙那就太好了。。。如果我能抓住这最后一点,我很快就能让整个应用程序正常运行了!或者,如果有人有一些示例代码,可以对不确定数量的令牌执行类似操作。。。最后,我想制作一个字符串,类似于:

field7,field12,field15,field18

重要更新-我不认为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
    )
  )
)
附录提供了有关上述工作原理的重要信息

如果您只需要在行中的64个标记之间分散一些标记,那么解决方案会稍微简单一些,因为您可以避免使用疯狂的字符作为变量。但仍需谨慎记账

例如,以下内容将允许您访问令牌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
    )
  )
)
2016年4月更新-基于DosTips用户Aacini、penpen和aGerman的调查工作,我开发了一种相对简单的方法,可以同时使用FOR/F访问数千个令牌。这项工作是。实际代码可在以下3个帖子中找到:

原始答案 因为变量仅限于一个字符,所以%%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
将给您第7、12、15和18枚代币

附录

2016年4月更新几周前,我了解到以下规则(6年前编写)依赖于代码页。以下数据已针对代码页437和850进行验证。更重要的是,扩展ASCII字符128-254的for变量序列与字节码值不匹配,并且因代码页而异。事实证明FOR/F变量映射是基于底层UTF-(16?)代码点的。因此,当与FOR/F一起使用时,扩展ASCII字符的使用有限。有关更多信息,请参阅处的线程

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

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

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

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
某些字符不能用于定义FOR变量。例如,下面给出了一个语法错误:

for /f %%^= in ("No can do") do echo anything
但是
%%=
可以通过使用TOKENS选项隐式定义,在DO子句中访问的值如下:

for /f "tokens=1-3" %%^< in ("A B C") do echo %%^< %%^= %%^>
上述结果产生
%%A

~
是一个潜在的危险变量。如果试图在行尾使用
%%~
访问变量,可能会得到不可预知的结果,甚至可能导致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: %%~~
(0x1A)字符是特殊的,因为批处理脚本中嵌入的
文本被读取为换行符(
)。为了将
用作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 _=%%_
EXTRACTCSVFIELDS THEFILE.CSV 7 12 15 18
MakeForTokens.bat 64
上面的结果是
A=1=31
Note-令牌2-30工作得很好,我只是想要一个小例子

任何解析和分配超过31个令牌的尝试都将在未设置ERRORLEVEL的情况下以静默方式失败

@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
您可以分析和分配多达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  ^^=%%^^  _=%%_
上述结果产生了
@=1^=31_323335

现在来看一个真正的坏消息。一个for/F永远不能解析超过31个标记,正如我在查看


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

我的答案由两部分组成。第一个是我在help-in-writing-a-batch-script-to-parse-csv-file-and-output-a-text-file问题中发布的一个新答案,该问题的字段数量没有任何限制

第二部分是对答案的修改,允许通过文件名后的附加参数选择将从csv文件中提取哪些字段。修饰语
@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
@echo off
setlocal EnableDelayedExpansion

rem Create heading array:
set /P headingRow=< %1
set i=0
for %%h in (%headingRow%) do (
    set /A i+=1
    set heading[!i!]=%%~h
)


REM SAVE FILE NAME AND CREATE TARGET ELEMENTS ARRAY:
SET FILENAME=%1
IF "%2" == "" (FOR /L %%J IN (1,1,%i%) DO SET TARGET[%%J]=%%J) & GOTO CONTINUE
SET J=0
:NEXTTARGET
    SHIFT
    IF "%1" == "" GOTO CONTINUE
    SET /A J+=1
    SET TARGET[%J%]=%1
GOTO NEXTTARGET
:CONTINUE


rem Process the file:
call :ProcessFile < %FILENAME%
exit /B

:ProcessFile
set /P line=
:nextLine
    set line=:EOF
    set /P line=
    if "!line!" == ":EOF" goto :EOF
    set i=0
    SET J=1
    for %%e in (%line%) do (
        set /A i+=1
        FOR %%J IN (!J!) DO SET TARGET=!TARGET[%%J]!
        IF !i! == !TARGET! (
            for %%i in (!i!) do echo !heading[%%i]!%%~e
            SET /A J+=1
        )
    )
goto nextLine
exit /B
EXTRACTCSVFIELDS THEFILE.CSV 7 12 15 18
@echo off
setlocal EnableDelayedExpansion

rem Create heading array:
set /P headingRow=< %1
set i=0
for %%h in (%headingRow%) do (
    set /A i+=1
    set heading[!i!]=%%~h
)

REM CREATE TARGET ELEMENTS LIST:
IF "%2" == "" (
    SET TARGETLIST=
    FOR /L %%J IN (1,1,%i%) DO SET TARGETLIST=!TARGETLIST! %%J
) ELSE (
    SET TARGETLIST=%*
    SET TARGETLIST=!TARGETLIST:* =!
)

rem Process the file:
call :ProcessFile < %1
exit /B

:ProcessFile
set /P line=
:nextLine
    set line=:EOF
    set /P line=
    if "!line!" == ":EOF" goto :EOF
    set i=0
    for %%e in (%line%) do (
        set /A i+=1
        for %%i IN (!i!) DO (
            IF "!TARGETLIST:%%i=!" NEQ "!TARGETLIST!" (
                echo !heading[%%i]!%%~e
            )
        )
    )
goto nextLine
exit /B
"Ultimately I want to make a string which will be something like:

field7,field12,field15,field18"
:ProcessFile
set /P line=
:nextLine
    set line=:EOF
    set /P line=
    if "!line!" == ":EOF" goto :EOF
    set i=0
    set resultString=
    for %%e in (%line%) do (
        set /A i+=1
        for %%i IN (!i!) DO (
            IF "!TARGETLIST:%%i=!" NEQ "!TARGETLIST!" (
                set resultString=!resultString!%%~e,
            )
        )
    )
    set resultString=%resultString:~0,-1%
    echo Process here the "%resultString%"
goto nextLine
exit /B
@echo off
for /f "tokens=1-31* delims=," %%@ in ("%filename%") do (
    echo:
    echo  1=%%@
    echo  2=%%A
    echo  3=%%B
    echo  4=%%C
    echo  5=%%D
    echo  6=%%E
    echo  7=%%F
    echo  8=%%G
    echo  9=%%H
    echo 10=%%I
    echo 11=%%J
    echo 12=%%K
    echo 13=%%L
    echo 14=%%M
    echo 15=%%N
    echo 16=%%O
    echo 17=%%P
    echo 18=%%Q
    echo 19=%%R
    echo 20=%%S
    echo 21=%%T
    echo 22=%%U
    echo 23=%%V
    echo 24=%%W
    echo 25=%%X
    echo 26=%%Y
    echo 27=%%Z
    echo 28=%%[
    echo 29=%%\
    echo 30=%%]
    echo 31=%%^^
    for /F "tokens=1-30* delims=," %%` in ("%%_") do (
        echo 32=%%`
        echo 33=%%a
        echo 34=%%b
        echo 35=%%c
        echo 36=%%d
        echo 37=%%e
        echo 38=%%f
        echo 39=%%g
        echo 40=%%h
        echo 41=%%i
        echo 42=%%j
        echo 43=%%k
        echo 44=%%l
        echo 45=%%m
        echo 46=%%n
        echo 47=%%o
        echo 48=%%p
        echo 49=%%q
        echo 50=%%r
        echo 51=%%s
        echo 52=%%t
        echo 53=%%u
        echo 54=%%v
        echo 55=%%w
        echo 56=%%x
        echo 57=%%y
        echo 58=%%z
        echo 59=%%{
        echo 60=%%^|
        echo 61=%%}
        for /F "tokens=1-9* delims=," %%0 in ("%%~") do (
            echo 62=%%0
            echo 63=%%1
            echo 64=%%2
            echo 65=%%3
            echo 66=%%4
            echo 67=%%5
            echo 68=%%6
            echo 69=%%7
            echo 70=%%8
            echo 71=%%9
        )
    )
)
MakeForTokens.bat 64
@echo off & setlocal EnableDelayedExpansion & set "$numTokens=65"

Rem/For  Step 1: Define the series of auxiliary variables that will be used as FOR tokens.
call :DefineForTokens

Rem/For  Step 2:  Define an auxiliary variable that will contain the desired tokens when it is %expanded%.
call :ExpandTokensString "tokens=7,12,15,18"

Rem/For  Step 3:  Define the variable with the "delims" value that will be used in the nested FOR's.
set "delims=delims=,"

Rem/For  Step 4:  Create the macro that contain the nested FOR's.
call :CreateNestedFors

Rem/For  Step 5:  This is the main FOR /F command that process the file.
for /F "usebackq tokens=1-31* %delims%" %%%$1% in ("filename.txt") do %NestedFors% (

   Rem/For  Step 6: Process the tokens.

   Rem/For  To just show they, use the "tokens" variable defined above:
   echo %tokens%

   Rem/For  You may also process individual tokens via another FOR /F command:
   for /F "tokens=1-%tokens.len%" %%a in ("%tokens%") do (
      echo Field  #7: %%a
      echo Field #12: %%b
      echo Field #15: %%c
      echo Field #18: %%d
   )

)

goto :EOF


Support subroutines. You must not modify any code below this line.


:DefineForTokens

for /F "tokens=2 delims=:." %%p in ('chcp') do set /A "_cp=%%p, _pages=($numTokens/256+1)*2"
set "_hex= 0 1 2 3 4 5 6 7 8 9 A B C D E F"
call set "_pages=%%_hex:~0,%_pages%%%"
if %$numTokens% gtr 2048 echo Creating FOR tokens variables, please wait . . .
(
   echo FF FE
   for %%P in (%_pages%) do for %%A in (%_hex%) do for %%B in (%_hex%) do echo %%A%%B 3%%P 0D 00 0A 00
) > "%temp%\forTokens.hex.txt"
certutil.exe -decodehex -f "%temp%\forTokens.hex.txt" "%temp%\forTokens.utf-16le.bom.txt" >NUL
chcp 65001 >NUL
type "%temp%\forTokens.utf-16le.bom.txt" > "%temp%\forTokens.utf8.txt"
(for /L %%N in (0,1,%$numTokens%) do set /P "$%%N=")  < "%temp%\forTokens.utf8.txt" 
chcp %_cp% >NUL
del "%temp%\forTokens.*.txt"
for %%v in (_cp _hex _pages) do set "%%v="
exit /B


:CreateNestedFors

setlocal EnableDelayedExpansion
set /A "numTokens=$numTokens-1, mod=numTokens%%31, i=numTokens/31, lim=31"
if %mod% equ 0 set "mod=31"
set "NestedFors="
for /L %%i in (32,31,%numTokens%) do (
   if !i! equ 1 set "lim=!mod!"
   set "NestedFors=!NestedFors! for /F "tokens=1-!lim!* %delims%" %%!$%%i! in ("%%!$%%i!") do"
   set /A "i-=1"
)
for /F "delims=" %%a in ("!NestedFors!") do endlocal & set "NestedFors=%%a"
exit /B


:ExpandTokensString variable=tokens definitions ...

setlocal EnableDelayedExpansion
set "var=" & set "tokens=" & set "len=0"
if "%~2" equ "" (set "params=%~1") else set "params=%*"
for %%a in (!params!) do (
   if not defined var (
      set "var=%%a"
   ) else for /F "tokens=1-3 delims=-+" %%i in ("%%a") do (
      if "%%j" equ "" (
         if %%i lss %$numTokens% set "tokens=!tokens! %%!$%%i!" & set /A len+=1
      ) else (
         if "%%k" equ "" (set "k=1") else set "k=%%k"
         if %%i leq %%j (
            for /L %%n in (%%i,!k!,%%j) do if %%n lss %$numTokens% set "tokens=!tokens! %%!$%%n!" & set /A len+=1
         ) else (
            for /L %%n in (%%i,-!k!,%%j) do if %%n lss %$numTokens% set "tokens=!tokens! %%!$%%n!" & set /A len+=1
         )
      )
   )
)
endlocal & set "%var%=%tokens%" & set "%var%.len=%len%"
exit /B