使用null值命令将csv文件按三列排序?

时间:2016-04-11 07:46:57

标签: windows sorting csv batch-file command-line

案件是这样的:

我有一个包含6列但没有标题的csv文件,如下所示:

5002200,25081,0000002797,6,,2014/06/05
5001111,25081,0000002790,,,2014/06/05
5004901,00081,0000002799,5,,2014/06/05 
5004901,00081,0000002796,5,,2014/06/05


我想要的输出是在排序后显示的,如下所示:

5001111,25081,0000002790,,,2014/06/05
5002200,25081,0000002797,6,,2014/06/05  
5004901,00081,0000002796,5,,2014/06/05 
5004901,00081,0000002799,5,,2014/06/05 


@echo off
if not exist %1 goto :EOF
setlocal
for /F "tokens=1-6 delims=," %%a in (%1) do set "a[%%b,%%c,%%a,%%d,%%e,%%f]=[]"
break > %1
for /F "tokens=2-7 delims=[,]=" %%a in ('set a[') do echo %%c,%%a,%%b,%%d,%%e,%%f>> %1
endlocal

问题是缺少空值。有什么想法吗?

我的算法排序第1列,第3列然后显示为原始位置。但如果有任何空值(如第4或第5列),它将错过。

第一列总共包含7个长度。
只有第4或第5列包含空。

3 个答案:

答案 0 :(得分:2)

sort /+8 infilename >outfilename

似乎会做你想要的。也许如果你要清楚地解释你的排序算法是什么,我们就能够构建一个更合适的系统。

@ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q36542742.txt"
SET "outfile=%destdir%\outfile.txt"
SET "tempfile=%destdir%\tempfile.txt"
DEL "%tempfile%" >NUL 2>NUL  
(
:: first step - number each line, number to %%a, line to %%b
FOR /f "skip=1tokens=1*delims=[]" %%a IN ('find /n /v "" "%filename1%"') DO (
 REM tokenise line - required parts to  %%p, %%q
 FOR /f "tokens=1,3delims=," %%p IN ("%%b") DO (
  REM construct sort-record
  CALL :process %%p%%q %%a "%%b"
 )
)
FOR /f "tokens=1*delims= " %%a IN ('sort "%tempfile%"') DO ECHO(%%b
)>"%outfile%"

DEL "%tempfile%" >NUL 2>NUL  

GOTO :EOF

:: First parameter: primary sort-criterion (fixed-length)
:: Second : secondary sort-criterion (leadin-zero-suppressed numeric)
:: Third : quoted data
:process
SET /a $line=1000000000+%2
>>"%tempfile%" ECHO(%1%$line% %~3
GOTO :EOF

您需要更改sourcedirdestdir的设置以适合您的具体情况。

我使用了一个名为q36542742.txt的文件,其中包含我的测试数据。

生成定义为%outfile%

的文件

tempfile可以设置为您喜欢的任何内容。

首先,通过find发送文件,查找不包含 nothing 的行并对其进行编号。因此,每一行都将成为

[number]originallinedata

并使用每个行以数字开头的事实[]进行标记,%%a将被设置为行号,%%b将被设置为行数据。

重新处理行数据,使用,进行标记并选择标记1和3.两个字段都是固定长度,第二个标记可能不为空。

通过提供参数 concatenated_column1_column3 line_number originaldataline

的过程:process处理该行。

:process内,向%2中的行号添加1000000000,然后发送

concatenated_column1_column3_modified_line_number 空间 originaldataline

所以发送的行将是

500220000000027971000000001 5002200,25081,0000002797,6,,2014/06/05

空格前的线部分是固定长度的。

完成后,对tempfile进行排序并在第一个空格后报告该部分。

答案 1 :(得分:0)

如果输入文件和输出文件不同,只需要一行Unxutil命令,

gawk -F"," "{print $1,$2,$3,$4,$5,$6}" input.csv|sort -gk1,3|sed "s/ /,/g";"s/$/\r/">output.csv

如果输出是直接输入文件,例如,输入.csv文件可以通过将自身拖到批处理文件来获得结果,

sed -i "s/,/ /g" "%~1"
sort -gk1,3 "%~1" -o"%~1"
sed -i "s/ /,/g";"s/$/\r/" "%~1"
exit /b

每列可以保持原始状态。

答案 2 :(得分:0)

以下脚本能够满足您的要求(我们称之为sort-csv.bat):

@echo off
setlocal EnableExtensions EnableDelayedExpansion

rem Define constants:
set "INFILE=%~1"
set "OUTFILE=%~2"
set "TEMPFILE=%TEMP%\%~n1_interim_to_sort%~x1"
set /A MAXWIDTH=10

if not exist "!INFILE!" exit /B 1
if not defined OUTFILE set "OUTFILE=%~dpn1_sorted%~x1"
set "PADZEROS="
for /L %%$ in (1,1,%MAXWIDTH%) do set "PADZEROS=!PADZEROS!0"
> "!TEMPFILE!" (
    for /F "delims=" %%# in ('findstr /N /R "^^" "!INFILE!"') do (
        set "LINE=%%#" & set "LINE=!LINE:*:=!"
        for /F "delims=:" %%a in ("%%#") do set "LNUM=!PADZEROS!%%a"
        for /F "tokens=1,3 delims=," %%A in (""!LINE:^,^=","!"") do (
            set "ITEM1=!PADZEROS!%%~A" & set "ITEM1=!ITEM1:~-%MAXWIDTH%!"
            set "ITEM2=!PADZEROS!%%~B" & set "ITEM2=!ITEM2:~-%MAXWIDTH%!"
            echo(!ITEM1!;!ITEM2!;!LNUM:~-%MAXWIDTH%!_!LINE!
        )
    )
)
> "!OUTFILE!" (
    for /F "tokens=1,* delims=_" %%I in ('sort "!TEMPFILE!"') do (
        echo(%%J
    )
)
> nul 2>&1 del "!TEMPFILE!"

endlocal
exit /B

要使用此批处理文件,请提供输入和输出路径/文件作为命令行参数:

sort-csv.bat "input-file.csv" "output-file.csv"

这背后的主要思想是将每个分隔符,替换为","并将""内的每一行括起来,以便每个项目都包含在""中;例如,1,2,,4之类的行变为"1","2","","4"。这样可以避免相邻的分隔符,,,因此,for /F循环可以使用,作为分隔符来获取项目; ~变量的for /F修饰符用于删除周围的""

对于排序,使用临时文件,其中包含前缀为(分号分隔)列的原始行,用于排序,原始行号以前导零填充方式。所以你的输入文件变为:

0005002200;0000002797;0000000001_5002200,25081,0000002797,6,,2014/06/05
0005001111;0000002790;0000000002_5001111,25081,0000002790,,,2014/06/05
0005004901;0000002799;0000000003_5004901,00081,0000002799,5,,2014/06/05
0005004901;0000002796;0000000004_5004901,00081,0000002796,5,,2014/06/05

然后将此文件输入sort命令,其输出由另一个for /F循环捕获,该循环切断前缀,即_个字符之前的所有内容。< / p>