情况是这样的:
我有一个 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
您需要更改sourcedir
和destdir
的设置以适合您的情况。
我使用了一个名为 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
循环捕获,该循环切断前缀,即_
字符的所有内容。