我在另一个页面上看到了回复(Help in writing a batch script to parse CSV file and output a text file) - 精彩的代码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个变量(最后一个被调用)例如%%BL
。但是,现在,当我在我的'大'csv文件(带有64个令牌)上运行批处理时,没有任何反应。没有错误(好)但没有输出! (坏)。如果有人可以提供帮助,那就太棒了......如果我能说到最后一点,我就太接近整个应用了!或者,如果有人有一些示例代码将对无限数量的令牌执行类似的操作...最终我想创建一个类似于以下内容的字符串:
field7,field12,field15,field18
答案 0 :(得分: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
)
)
)
附录提供了有关上述工作原理的重要信息。
如果您只需要在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
)
)
)
2016年4月更新 - 根据DosTips用户Aacini,penpen和aGerman的调查工作,我开发了一种相对简单的方法,可以使用FOR / F同时访问数千个令牌。这项工作是this DosTips thread的一部分。实际代码可以在这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
会给你第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
^
<
>
|
&
等特殊字符必须进行转义或引用。例如,以下工作:
for /f %%^< in ("OK") do echo "%%<" %%^<
某些字符不能用于定义FOR变量。例如,以下语法错误:
for /f %%^= in ("No can do") do echo anything
但是%%=
可以通过使用TOKENS选项隐式定义,并且在DO子句中访问的值如下:
for /f "tokens=1-3" %%^< in ("A B C") do echo %%^< %%^= %%^>
%
很奇怪 - 您可以使用%%%%
定义FOR变量。但除非使用~
修饰符,否则无法访问该值。这意味着无法保留封闭引号。
for /f "usebackq tokens=1,2" %%%% in ('"A"') do echo %%%% %%~%%
上述收益率为%% 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: %%~~
<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 _=%%_
以上收益率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
您可以解析并分配最多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永远不会解析超过31个令牌,正如我在查看Number of tokens limit in a FOR command in a Windows batch script时了解到的那样
@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
非常不幸的输出是A=1 B=31 C=%C
答案 1 :(得分:4)
我的回答由两部分组成。第一个是我在帮助写入 - 批处理 - 脚本 - 解析 - csv - 文件 - 输出 - 文本 - 文件问题中发布的新答案,该问题对字段数没有任何限制
第二部分是对该答案的修改,允许选择通过文件名后面的附加参数从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
您也可以删除标题数组的创建,因为您不需要标题! ;)
答案 2 :(得分:1)
使用%% @和%%`(此处未记录)作为起始变量,您可以获得的最大值为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
)
)
)
答案 3 :(得分:0)
当我再次阅读此问题并在最投票的答案中提出解决方案时,我认为可以开发一种更简单的方式来充分利用一系列嵌套的FOR / F命令。我开始编写这样一种方法,允许使用127 附加标记,将它们放在ASCII 128-254字符范围内。但是,当我的程序完成后,我发现&#34;自然&#34;中的ASCII字符。 128..254订单不能用于此目的......
然后,一群人对这个问题感兴趣,他们做了一系列的发现和发展,最终形成了一个允许在一系列中使用多个令牌(超过43,000!)的方法嵌套的FOR / F命令。您可以在this DosTips topic阅读有关此发现所涉及的研究和开发的详细说明。
最后,我使用新方法修改我的程序,因此它现在允许处理多达4094个同时令牌(来自带有长行的文本文件),但以简单的方式。我的应用程序包含一个名为 MakeForTokens.bat 的批处理文件,您可以使用参数中所需的标记数运行。例如:
MakeForTokens.bat 64
该程序生成一个名为 ForTokens.bat 的批处理文件,其中包含管理此类大量同时令牌所需的所有代码,包括如何处理文件的示例。通过这种方式,用户只需插入自己的文件名和所需的令牌即可获得工作程序。
在这种特殊情况下,这将是最终的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 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
您可以从this site下载MakeForTokens.bat应用程序。