批量循环,csv解析并向文件发出适当的输出

时间:2013-03-20 17:37:47

标签: csv for-loop batch-file

让我来描述我的问题。 我有一个从excel导出的csv文件,包含大量数据。 该文件在第一行中具有标题,在第二行中具有列标题。 我只需从该文件中提取两列(第2列和第3列), 将它们放到1列并将输出发送到另一个文件。

示例:

Title
colA , colB , colC , colD ,...
abc  , def  , ghi  , jkl  ,...
abc  , def  , ghi  , jkl  ,...
abc  , def  , ghi  , jkl  ,...
abc  , def  , ghi  , jkl  ,...

问题是,csv解析器在遇到行时失败了 包含带 - ()@字符的字符串。 (循环将它们视为分隔符,所以每次都会给我一个超出范围的错误。)

这是我已经拥有的。

@Echo off & setlocal EnableExtensions
setLocal EnableDelayedExpansion

REM creating and clearing files
copy /y NUL C:\list1.csv >NUL
copy /y NUL C:\list1_tmp.csv >NUL
copy /y NUL C:\exportedColumns.csv >NUL
copy /y NUL C:\Result.txt >NUL

set Result=C:\Result.txt
set Source=C:\sourcelist.csv
set list1=C:\list1.csv
set list1_tmp=C:\list1_tmp.csv
set expCol=C:\exportedColumns.csv

REM skip 1st two lines from source file and put to output file list1
for /f "skip=2 delims=*" %%a in (%Source%) do (echo %%a >>%list1%)

REM shorten each line to 500 chars and put it to new file
for /f "tokens=* delims=" %%a in ("%list1%") do (
set s=%%a
set s=%s:~0,500% 
echo.%s% >> "%list1_tmp%"
)
REM ^^^^^^^^^^^ this is not working. It puts only 1 space to the output file

rem Parsing the csv file
rem Process the file:
call :ProcessFile < %list1_tmp%
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 %%i==1 echo %%~e >> %expCol%
        if %%i==2 echo %%~e >> %expCol%
    )
    if %%i==3 goto nextLine
    REM I don't want it to process all the columns
    )
goto nextLine

我想请你看看这个并帮我把2列合二为一 并将输出放到1个文件中。

我非常感激。

3 个答案:

答案 0 :(得分:2)

这个怎么样?

for /f "skip=2 tokens=2,3 delims=, " %i in (input.csv) do echo %i%j >> output.csv

修改

要替换/换行,可以试试这个:

@echo off

for /f "skip=2 tokens=2,3 delims=, " %%i in (test.csv) do call :replace %%i%%%j
goto :eof

:replace
set string=%*
For /f "tokens=1,* delims=/" %%a in ('echo %string%') Do (
echo.%%a
If not "%%b"=="" call :replace %%b)

输入:

title
colA , colB , colC , colD ,...
abc  , def  , g\hi  , jkl  ,...

上面会输出:

defg
hi

答案 1 :(得分:0)

您提到的问题之一是for %%e in (%line%) do ...行,当%line%包含(这样的特殊字符时,自然会弄乱解释器。

您可以通过使用字符串替换来避免这种情况,以便在每列周围加上引号。例如(我正在跳过你的一些代码,只关注有问题的部分):

:ProcessFile
set /P line=
:nextLine
    for %%e in ("%line:,=" "%") do (
        echo %%~e
    )
goto nextLine

注意这一部分:"%line:,=" "%"。这会将所有逗号替换为" ",并在该行的开头和结尾添加"

因此,如果我们正在处理的特定行看起来像这样:

abc, def (foo), ghi

for将扩展为:

for %%e in ("abc" "def (foo)" "ghi") do ...

所有内容都很好地包含在引号中,因此(不会干扰。当然,如果您在特定列中有引号,那么会干扰......

下一行,我使用%%e,我将其设为%%~e以便删除引号。

答案 2 :(得分:0)

恰巧我今天早上一直在玩ADODB记录集来访问CSV文件。我的代码可能对您有用。实际上,脚本循环遍历当前目录中的每个.csv文件,每行显示column = value

JScript应该很容易修改,以根据需要组合列。由于这是批处理/ JScript混合,因此您可以选择是要创建Scripting.FileSystemObject对象还是仅重定向cscript行的输出以生成新的.csv文件

以下是csv.bat的代码。 *耸肩*这不是最终答案,而是建议的替代路径。

@if (@a==@b) @end /*

:: batch portion

@echo off
setlocal

:: force 32-bit environment for ODBC drivers
if exist "%windir%\syswow64\cmd.exe" (set "cmd=%windir%\syswow64\cmd.exe") else set "cmd=cmd.exe"

for /r %%I in (*.csv) do (
    echo Processing %%~nxI:
    echo;
    %cmd% /c cscript /nologo /e:jscript "%~f0" "%%~dpI" "%%~nxI"
    echo;
)

goto :EOF

:: JScript portion */
var conn = new ActiveXObject("ADODB.Connection");
var rs = new ActiveXObject("ADODB.Recordset");

var dsn = "Driver={Microsoft Text Driver (*.txt; *.csv)};"
    + "Dbq=" + WSH.Arguments(0) + ";"
    + "Extensions=asc,csv,tab,txt;";

try { conn.Open(dsn); }
catch(e) {

    // If the Microsoft Text Driver didn't work,
    // try the MS Jet 4.0 provider instead.

    var dsn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
    + WSH.Arguments(0)
    + ";Extended Properties=\"text;HDR=Yes;FMT=Delimited\";";

    try { conn.Open(dsn); }
    catch(e) {

        // If that didn't work either, then give up.

        WSH.Echo("Unable to create ADODB connection.");
        WSH.Quit(1);
    }
}

rs.Open("SELECT * from " + WSH.Arguments(1), conn, 2, 4);

while (!rs.EOF) {
    for (var i=0; i<rs.Fields.Count; i++) {
        WSH.Echo(rs.Fields(i).Name + ' = ' + rs.Fields(i));
    }
    rs.MoveNext;
}

rs.Close();
conn.Close();