案件是这样的:
我有一个包含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列包含空。
答案 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
您需要更改sourcedir
和destdir
的设置以适合您的具体情况。
我使用了一个名为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>