用于解析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个帖子中找到:
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