Windows批处理脚本parsingCSV文件并输出文本文件

我已经看到另一个页面上的响应( 帮助编写一个批处理脚本来parsingCSV文件并输出一个文本文件 ) – 辉煌的代码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 

它对于我在格式中所做的示例csv文件非常出色:

 Header,Name,Place one,two,three four,five,six 

然而,我想改变的实际文件包含64个字段 – 所以我将tokens=1-10改为tokens=1-64 ,并将%%a等增加到64个variables(最后被称为%%BL )。 但是,现在,当我在我的“大”csv文件(带有64个标记)上运行批处理时,没有任何反应。 没有错误(好),但没有输出! (坏)。 如果任何人都可以提供帮助,那将会是太棒了…如果我能指出最后一点的话,那么接近整个应用程序的工作! 或者,如果任何人有一些示例代码,将做类似的无限数量的令牌…最终,我想要一个string,将是这样的:

 field7,field12,field15,field18 

重要的更新 – 我不认为Windows批处理是一个很好的select,因为一个FOR / F不能parsing超过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行中分配一些令牌,那么解决scheme稍微容易些,因为你可以避免使用疯狂的字符作为FORvariables。 但是还有一些小心的事情要做。

例如,以下内容可让您访问令牌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 ) ) ) 

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

  • 使用固定数量的列
  • 使用不同数量的列
  • dynamicselect在DO子句中扩展哪些标记

原始答案variables被限制为单个字符,所以你的%% BL策略不能工作。 variables是区分大小写的。 根据微软的说法,你只能在一个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 

会给你你的第七,十二,十五和十八个代币。

附录

2016年4月更新 几个星期前,我了解到,以下规则(6年前编写)是代码页相关的。 以下数据已针对 代码页437和850 进行了validation 更重要的是,扩展ASCII字符128-254的FORvariables序列与字节代码值不匹配,并且由代码页巨大地变化。 事实certificate,FOR / Fvariables映射是基于底层的UTF-(16?)代码点。 所以与FOR / F一起使用时,扩展的ASCII字符的使用是有限的。 有关更多信息,请参阅http://www.dostips.com/forum/viewtopic.php?f=3&t=7703上的主题。;

我进行了一些testing,并可以报告以下内容(更新回应jeb的评论)

大多数字符可以用作FORvariables,包括扩展ASCII 128-254。 但是有些字符不能用于在FOR语句的第一部分定义一个variables,但可以在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 

特殊字符,如^ < > | &必须是逃脱或引用。 例如,以下工作:

 for /f %%^< in ("OK") do echo "%%<" %%^< 

有些字符不能用来定义一个FORvariables。 例如,下面给出了一个语法错误:

 for /f %%^= in ("No can do") do echo anything 

%%=可以通过使用TOKENS选项隐含地定义,以及在DO子句中访问的值如下所示:

 for /f "tokens=1-3" %%^< in ("ABC") do echo %%^< %%^= %%^> 

%很奇怪 – 您可以使用%%%%定义一个FORvariables。 但是,除非使用~修饰符,否则无法访问该值。 这意味着封闭的引号不能被保留。

 for /f "usebackq tokens=1,2" %%%% in ('"A"') do echo %%%% %%~%% 

以上产量%% A

~是一个潜在的危险的variables。 如果试图在行尾使用%%~来访问variables,则可能会得到不可预知的结果,甚至可能导致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: %%~~ 

<SUB> (0x1A)字符是特殊的,因为批处理脚本中embedded的<SUB>文字被读取为换行( <LF> )。 为了使用<SUB>作为FORvariables,该值必须以某种方式存储在环境variables中,然后%%%VAR%将同时用于定义和访问。

如前所述,一个FOR / F可以parsing和分配最多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 _=%%_ 

以上产量A=1 _=31 注 – 令牌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 

您可以parsing和分配多达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 _=32 33 34 35

而现在真是一个坏消息。 一个FOR / F永远不能parsing超过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=%%AB=%%BC=%%C 

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

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

第二部分是对该答案的修改,允许通过放置在文件名后的附加参数来select从csv文件中提取哪些字段。 修改后的代码是大写字母。

 @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 

另外,这个版本不需要按顺序给出所需的字段。

编辑

哎呀! for参数的东西分散了我的注意力,所以我没有意识到你最后的要求:

 "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 

你也可以删除标题数组的创build,因为你不想标题! ;)

使用%% @和%%(这里没有logging)作为启动variables,你可以得到的最大值是71:

 @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 ) ) ) 

当我再次读到这个问题,并提出了解决scheme中提出的解决scheme时,我认为可以开发一个更简单的方法来充分利用一系列嵌套的FOR / F命令。 我开始写这样一个方法,可以使用127个额外的令牌,把它们放在ASCII 128-254字符范围内。 但是,当我的程序完成后,我发现“自然”128..254顺序中的ASCII字符不能用于此目的…

然后,一群人对这个问题感兴趣,他们做了一系列的发现和发展,最终在一系列嵌套的FOR / F命令中使用了许多令牌 (超过43,000!)。 您可以在此DosTips主题中阅读关于此发现涉及的研究和开发的详细说明。

最后,我使用新的方法来修改我的程序,所以它现在允许处理多达4094个同时令牌(来自长行的文本文件),但以一种简单的方式 。 我的应用程序包含一个名为MakeForTokens.bat的batch file,您可以在参数中使用所需令牌的数量运行。 例如:

 MakeForTokens.bat 64 

该程序会生成一个名为ForTokens.bat的batch file,其中包含pipe理如此数量的同时令牌所需的所有代码,包括如何处理文件的示例。 这样,用户只需要插入自己的文件名和想要的令牌就可以得到一个工作程序。

在这种情况下,在删除大多数描述性注释之后,这将是最终解决问题的ForTokens.bat文件:

 @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 ABCDEF" 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 

您可以从这个网站下载MakeForTokens.bat应用程序。