我需要使用批处理文件从CSV中分割一行,但它具有需要保留的空值

时间:2014-06-13 21:32:10

标签: string batch-file dos batch-processing string-split

我正在编写一个批处理文件,该文件从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

有什么建议吗?

2 个答案:

答案 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=