使用具有空值的命令按三列对 csv 文件进行排序



情况是这样的:

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

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

问题是空值会丢失。知道吗?

我的算法是对第一列和第三列进行排序,然后显示为原始位置。但是,如果有任何空值(如第 4 列或第 5 列(,它将丢失。

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

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查找不包含任何内容的行并对其进行编号。因此,每行将变为

[number]originallinedata

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

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

通过提供参数:process line_number原始数据行concatenated_column1_column3过程处理该行

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

concatenated_column1_column3_modified_line_number空间原始数据线

所以发送的行将是

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

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

完成后,对临时文件进行排序,并在第一个空格之后报告部分。

如果

输入文件和输出文件不同,则只需 1 行带有 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

每列都可以保持原始状态。

以下脚本能够满足您的请求(我们称之为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 循环捕获,该循环切断前缀,即_字符的所有内容。

最新更新