我正在编写一个批处理文件,该文件从CSV中取出行并将其重新排列为新CSV以导入Excel。
我的困难在于忽略了空元素,但是对我来说保留生成CSV的表中的空元素非常重要,这样我就可以为导入提取正确的值。
以下是我的问题的简化示例:假设我有一个文件input.txt,其中包含以下内容:
1,2,,4
这是我的代码:
@echo off
set filename=input.txt
for /F "tokens=1,2,3,4 delims=," %%a in (%filename%) do (
echo a : %%a
echo b : %%b
echo c : %%c
echo d : %%d
)
我的输出是:
a : 1
b : 2
c : 4
d :
我想要输出:
a : 1
b : 2
c :
d : 4
有什么建议吗?
答案 0 :(得分:3)
@echo off
setlocal EnableDelayedExpansion
set filename=input.txt
for /F "delims=" %%x in (%filename%) do (
set line=%%x
for /F "tokens=1,2,3,4 delims=," %%a in ("!line:,,=,"",!") do (
echo a : %%~a
echo b : %%~b
echo c : %%~c
echo d : %%~d
)
)
答案 1 :(得分:1)
对于初学者,我们假设您有一个简单的案例,其中没有值包含引号,逗号或!
。然后你可以使用:
@echo off
setlocal enableDelayedExpansion
set "filename=input.csv"
for /f usebackq^ delims^=^ eol^= %%a in ("%filename%") do (
set "ln=%%a"
for /f "tokens=1-4 delims=," %%a in (""!ln:^,^=","!"") do (
echo a : %%~a
echo b : %%~b
echo c : %%~c
echo d : %%~d
)
)
如果已经引用了某些列值,则上述操作无法正常工作。 Aacini的代码在某些情况下有效,但如果有连续的空列,或者如果有一个前导空列,则会失败。更多的代码解决了这些缺点:
@echo off
setlocal enableDelayedExpansion
set "filename=input.csv"
for /f usebackq^ delims^=^ eol^= %%a in ("%filename%") do (
set "ln=%%a"
if "!ln:~0,1!" equ "," set "ln=""!ln!"
if "!ln:~-1,1!" equ "," set "ln=!ln!""" %== I don't think this is needed, but it can't hurt ==%
set "ln=!ln:,,=,"",!"
set "ln=!ln:,,=,"",!"
for /f "tokens=1-4 delims=, eol=," %%a in ("!ln!") do (
echo a : %%~a
echo b : %%~b
echo c : %%~c
echo d : %%~d
)
)
但是CSV文件可能很棘手。可以引用任何列值,引用的值可能包含以""
转义的逗号,换行符或引号。此外,如果启用了延迟扩展,则扩展包含!
(或可能^
)的FOR / F变量将破坏该值。使用纯本机批处理命令解决所有这些问题非常困难。它可以做到,但它会是神秘而缓慢的。
我编写了一个hybrid JScript/batch utility called parseCSV.bat, and a batch macro called csvGetCol,使FOR / F能够安全地解析几乎任何CSV文件。代码是纯脚本,可以在XP之后的任何现代Windows机器上运行。完整文档嵌入在脚本中。此外,还发布了多个示例,说明如何使用这些实用程序。
以下是此问题中可用于示例的代码。以下代码允许在列值中使用逗号,引号,换行符,!
和^
。
@echo off
:: Delayed expansion must be disabled during macro definition
setlocal disableDelayedExpansion
call define_csvGetCol
set "filename=input.csv"
:: Delayed expansion must be enabled when using %csvGetCol%
setlocal enableDelayedExpansion
for /f "tokens=1-4 delims=," %%A in ('parseCSV /e /d ^<"%filename%"') do (
%== Load and decode column values ==%
%csvGetCol% A "," %%A
%csvGetCol% B "," %%B
%csvGetCol% C "," %%C
%csvGetCol% D "," %%D
%== Print results ==%
echo a : !A!
echo b : !B!
echo c : !C!
echo d : !D!
)
我建议您按照上面的链接获取更多信息。但下面是两个实用程序的代码。我将根据需要将更新发布到DOSTips站点。我不能保证我会将代码保持在最新状态。
<强> parseCSV.bat 强>
@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScrpt comment
::************ Documentation ***********
::parseCSV.bat version 1.0
:::
:::parseCSV [/option]...
:::
::: Parse stdin as CSV and write it to stdout in a way that can be safely
::: parsed by FOR /F. All columns will be enclosed by quotes so that empty
::: columns may be preserved. It also supports delimiters, newlines, and
::: quotes within quoted values. Two consecutive quotes within a quoted value
::: are converted into one quote.
:::
::: Available options:
:::
::: /I:string = Input delimiter. Default is a comma.
:::
::: /O:string = Output delimiter. Default is a comma.
:::
::: /E = Encode output delimiter in value as \D
::: Encode newline in value as \N
::: Encode backslash in value as \S
:::
::: /D = Escape exclamation point and caret for delayed expansion
::: ! becomes ^!
::: ^ becomes ^^
:::
:::parseCSV /?
:::
::: Display this help
:::
:::parseCSV /V
:::
::: Display the version of parseCSV.bat
:::
:::parseCSV.bat was written by Dave Benham. Updates are available at the original
:::posting site: http://www.dostips.com/forum/viewtopic.php?f=3&t=5702
:::
::************ Batch portion ***********
@echo off
if "%~1" equ "/?" (
setlocal disableDelayedExpansion
for /f "delims=: tokens=*" %%A in ('findstr "^:::" "%~f0"') do echo(%%A
exit /b 0
)
if /i "%~1" equ "/V" (
for /f "delims=:" %%A in ('findstr /bc:"::%~nx0 version " "%~f0"') do echo %%A
exit /b 0
)
cscript //E:JScript //nologo "%~f0" %*
exit /b 0
************ JScript portion ***********/
var args = WScript.Arguments.Named,
stdin = WScript.Stdin,
stdout = WScript.Stdout,
escape = args.Exists("E"),
delayed = args.Exists("D"),
inDelim = args.Exists("I") ? args.Item("I") : ",",
outDelim = args.Exists("O") ? args.Item("O") : ",",
quote = false,
ln, c, n;
while (!stdin.AtEndOfStream) {
ln=stdin.ReadLine();
if (!quote) stdout.Write('"');
for (n=0; n<ln.length; n++ ) {
c=ln.charAt(n);
if (c == '"') {
if (quote && ln.charAt(n+1) == '"') {
n++;
} else {
quote=!quote;
continue;
}
}
if (c == inDelim && !quote) c='"'+outDelim+'"';
if (escape) {
if (c == outDelim) c="\\D";
if (c == "\\") c="\\S";
}
if (delayed) {
if (c == "!") c="^!";
if (c == "^") c="^^";
}
stdout.Write(c);
}
stdout.Write( (quote) ? ((escape) ? "\\N" : "\n") : '"\n' );
}
的 define_csvGetCol.bat 强>
::define_csvGetCol.bat version 1.0
::
:: Defines variable LF and macro csvGetCol to be used with
:: parseCSV.bat to parse nearly any CSV file.
::
:: This script must be called with delayedExpansion disabled.
::
:: The %csvGetCol% macro must be used with delayedExpansion enabled.
::
:: Example usage:
::
:: @echo off
:: setlocal disableDelayedExpansion
:: call define_csvGetCol
:: setlocal enableDelayedExpansion
:: for /f "tokens=1,3 delims=," %%A in ('parseCSV /d /e ^<test.csv') do (
:: %== Load and decode column values ==%
:: %csvGetCol% A "," %%A
:: %csvGetCol% B "," %%B
:: %csvGetCol% C "," %%C
:: %== Display the result ==%
:: echo ----------------------
:: for %%V in (A B C) do echo %%V=!%%V!
:: echo(
:: )
::
:: Written by Dave Benham
::
:: Delayed expansion must be disabled during macro definition
:: Define LF to contain a linefeed (0x0A) character
set ^"LF=^
^" The empty line above is critical - DO NOT REMOVE
:: define a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
:: Define csvGetCol
:: %csvGetCol% envVarName "Delimiter" FORvar
set csvGetCol=for %%# in (1 2) do if %%#==2 (%\n%
setlocal enableDelayedExpansion^&for /f "tokens=1,2*" %%1 in ("!args!") do (%\n%
endlocal^&endlocal%\n%
set "%%1=%%~3"!%\n%
if defined %%1 (%\n%
for %%L in ("!LF!") do set "%%1=!%%1:\N=%%~L!"%\n%
set "%%1=!%%1:\D=%%~2!"%\n%
set "%%1=!%%1:\S=\!"%\n%
)%\n%
)) else setlocal disableDelayedExpansion ^& set args=