如何使用批处理文件对txt列表进行排序



我正在尝试建立一个非常基本的记分板系统,该系统应该可以在每台Windows机器上运行,而无需安装任何东西(如Python(。我不确定批处理文件是否是去那里的方法,因为它确实过时了,但我只是在这里选择了它。这就是我要做的:

  • 有一个脚本,让操作员输入名称和分数
  • 对所有分数进行排序
  • 将前5名玩家导出为单独的txt文件(例如PointsTopPlayer1.txt、NameTopPlayer1.text、PointsTopPlayer2.txt、NameTopPlayer2.txt等(

这就是我到目前为止所拥有的。一个批处理脚本,让用户输入一个名称和一些点。

:start
@echo off
cls
echo Please enter data
echo.
set /p _player=name:
:inputpoints
set /p _points=points:
echo %_points%| findstr /r "^[1-9][0-9]*$">nul
if not %errorlevel% equ 0 goto invalid
(echo=%_points%,%_player%)>> data.txt
echo Data saved.
timeout 2 > nul
goto start
:invalid
echo Please enter a valid number.
timeout 1 > nul
goto inputpoints

现在,我的下一个猜测是在脚本中添加一行,对data.txt列表进行排序,目前看起来是这样的:

500,Bob
390,Thomas
650,Sam
100,Nick
20,Olivia

尽管看起来除了sort命令之外,没有真正的方法对数字进行排序,但在这种情况下,该命令不起作用,因为它按字母顺序对数字排序,将100放在20之前。我在网上看到了这个剧本,但老实说,我不知道如何使用它。下一步是将所有数据导出到单独的文件中(对于前5名玩家(,这是我不知道如何实现的

任何想法都值得赞赏!

如果你说PowerShell是你的一个选项,你可以这样做:

$scores = while ($true) {
cls
$player = Read-Host "Please enter the name of a player. (an empty input exits)"
if ([string]::IsNullOrWhiteSpace($player)) { 
break  # exit the outer loop
}
while ($true) {
$points = Read-Host "Please enter the points for player $player"
if ($points -match '^d+$') {   # check if input is numeric
break  # exit the inner loop
}
else {
Write-Host "Please enter a valid number" -ForegroundColor Red
Start-Sleep 1
}
}
# output an object with player name and point
[PsCustomObject]@{
Player = $player
Points = [int]$points  # convert to int so the sorting later will be numeric
}
}
# if you like, save this all as structured csv file you can open in Excel
$scores | Export-Csv -Path 'X:Somewherescoredata.csv' -NoTypeInformation -UseCulture
# now sort and select the top 5 players
$top5 = $scores | Sort-Object -Property Points -Descending | Select-Object -First 5
# write the textfiles
for ($i = 0; $i -lt $top5.Count; $i++) {
# file TopPlayerX.txt
$top5[$i].Player | Set-Content -Path ('X:SomewhereTopPlayer{0}.txt' -f ($i + 1))
# file TopPointsX.txt
$top5[$i].Points | Set-Content -Path ('X:SomewhereTopPoints{0}.txt' -f ($i + 1))
}

由于这是一个逗号分隔的列表,只要给它分配标题,就可以使用Import-Csv

@"
500,Bob
390,Thomas
650,Sam
100,Nick
20,Olivia
"@ | ConvertFrom-Csv -Header "Numbers","Name" | Sort-Object -Property {[int]$_.Numbers}

在本例中,我使用ConvertFrom-Csv,但Import-Csv的工作原理相同。下一步是使用Sort-Object中的脚本块将新创建的Numbers列指定为类型int进行排序。

一个想法:在保存_points之前,先向其添加100000000

100000500,Bob
100000390,Thomas

这会很好地分类。如果你在分析数据,你可以使用

for/f "tokens=1*delims=," %%b in (filename) do (
set /a points=%%b-100000000
echo !points! %%c
)

注意,您需要调用delayedexpansion,因为points的值在代码块中是可变的。参见Stephan的DELAYEDEXPANSION链接

把前n个名字放在最高分列表中很简单:

for /f "tokens=1,2,*delims=[]," %%u in ('type yourfile^|sort^|find /n "1" ') do if %%u leq 5 (set /a points=%%v-100000000
echo !points!,%%w
)

同样,您需要delayedexpansion

或者,你可以听从Stephan的建议

(
for/f "tokens=1*delims=," %%b in (filename) do (
set /a points=%%b+100000000
echo !points!,%%c
)
)>tempfile
(
for /f "tokens=1,*delims=," %%b in ('type tempfile^|sort ') do (set /a points=%%b-100000000
echo !points!,%%c
)
)>filename

它将用排序版本取代CCD_ 10

注意,((codeblock))>filename将输出重定向到filename的新版本。

但总的来说,批量解决这一问题的关键是delayedexpansion

一种批处理方法,使用嵌套的for循环和临时数组通过条件评估进行排序:

@Echo off & Cls
For /f "delims=" %%e in ('echo Prompt $E^|Cmd')Do Set "E=%%e"
Setlocal EnableDelayedExpansion
Set "Players[i]=0"
REM substitue %~f0 below with the filepath of the input data
For /f "tokens=1,2 Delims=," %%G in ('%SystemRoot%System32Findstr.exe /RC:"^[0-9][0-9]*,[a-zA-Z][a-zA-Z]*[ ]*[a-zA-Z][a-zA-Z]*$" "%~f0"')Do (
%= increment array index          =% Set /A "Players[i]+=1"

REM index included in variable name to guard against duplicate names
Set "Player[!Players[i]!]=%%H (Player.!Players[i]!)"
%= Assign Score to indexed player =% Set "%%H (Player.!Players[i]!)=%%G"
)
Call:OutputSorted unsorted
Call:SortArray Player Players[i] LEQ
Call:OutputSorted Sorted Low to high 
Call:SortArray Player Players[i] GEQ
Call:OutputSorted Sorted High to Low

Goto:eof
:SortArray ======================= <Element_VarName> <Element_Index_VarName> [GEQ|H2L]
REM default sorting method Low to High
Set "SortCompare=LEQ"
REM overide default to High to Low if either of the below given as 3rd Argument
If /I "%~3" == "GEQ"  Set "SortCompare=GEQ"
If /I "%~3" == "H2L"  Set "SortCompare=GEQ"
Set /a "Max=%2+1"
For /L %%a In (0,1,!Max!)Do (
If !SortCompare! == LEQ (%= Define Sort offset to match Sort Priority =%
Set /A "S_Offset=%%a - 1"
)Else Set /A "S_Offset=%%a"
For /L %%b IN (0,1,%%a)Do (
If not %%b==%%a For %%c in (!S_Offset!)Do (%= Expand nested variable for comparison =%
For /f "tokens=1,2 Delims=;" %%v in ("!%1[%%c]!;!%1[%%b]!")Do IF !%%v! %SortCompare% !%%w! (%= Switch Array Position =%
Set "tmpV=!%1[%%c]!"
Set "%1[%%c]=!%1[%%b]!"
Set "%1[%%b]=!tmpV!"
)   )   )   )
Exit /B 0
:OutputSorted
REM output formatting
Echo(%*
Echo(Name %E%[15G Player %E%[30G Score
For /f "tokens=2 Delims==" %%G in ('Set Player[')Do (
For /f "tokens=1,3 Delims=(.)" %%i in ("%%G")Do (
Echo(%%i %E%[15G %%j %E%[30G = !%%G!
)
)
Echo(
Goto:eof
=== GOTO:EOF ===
[/DATA]
500,Bob
390,Thomas
650,Sam
100,Nick
1020,Nick Sr
20,Olivia
115,Sam
[DATA]

最新更新