简体   繁体   English

使用PowerShell对.txt文件的行进行排序

[英]Sort lines of a .txt file with PowerShell

I have a .txt like that below: 我有一个像下面这样的.txt

MasterCard 
R$ 66,12 

Hipercard 
R$ 7.501,98 

MasterCard 
R$ 48,19 

Hipercard 
R$ 7.742,46 

MasterCard 
R$ 48,19 

Hipercard 
R$ 5.846,14 

MasterCard 
R$ 48,19 

Hipercard 
R$ 4.210,65 

Visa
R$ 4.210,65 

MasterCard 
R$ 48,19 

Hipercard 
R$ 2.892,20 

MasterCard 
R$ 48,19 

Visa
R$ 4.210,65

Hipercard 
R$ 1.944,19 

Hipercard 
R$ 584,04 

Visa
R$ 4.210,65

I need a .bat file (could be in PowerShell) that organize the lines by the descrption and value, like below: 我需要一个.bat文件(可能在PowerShell中),该文件按描述和值来组织行,如下所示:

-----------------
MasterCard 
R$ 66,12 

MasterCard 
R$ 48,19 

MasterCard 
R$ 48,19 

MasterCard 
R$ 48,19 

MasterCard 
R$ 48,19 

MasterCard 
R$ 48,19 

----------------      
Hipercard 
R$ 7.501,98 

Hipercard 
R$ 7.742,46 

Hipercard 
R$ 5.846,14 

Hipercard 
R$ 4.210,65

Hipercard 
R$ 2.892,20 

Hipercard 
R$ 1.944,19 

Hipercard 
R$ 584,04 

--------------        
Visa
R$ 4.210,65 

Visa
R$ 4.210,65

Visa
R$ 4.210,65

It's possibile do it with a .bat file? .bat文件有可能吗? I'm stuck in how to start. 我被困在如何开始。 I was trying using a simple command: sort test.txt /o testout.txt 我正在尝试使用一个简单的命令: sort test.txt /o testout.txt

But it's not working. 但这不起作用。


I have another idea. 我有另一个主意。 Running this command i can put all lines in the same line: 运行此命令,我可以将所有行放在同一行中:

for /f "usebackq tokens=* delims=" %%a in ("frt01.txt") do (echo(%%a)>>test.txt
for /f "usebackqdelims=" %%i in ("test.txt") do @<nul set /p"=%%i ">>"test2.txt"
del test.txt /S /Q /

So now, the description and values are all in only one line: 所以现在,描述和值都只在一行中:

MasterCard  R$ 66,12  Hipercard  R$ 7.501,98  MasterCard  R$ 48,19  Hipercard  R$ 7.742,46  MasterCard  R$ 48,19  Hipercard  R$ 5.846,14  MasterCard  R$ 48,19  Hipercard  R$ 4.210,65  MasterCard  R$ 48,19  Hipercard  R$ 2.892,20  MasterCard  R$ 48,19  Hipercard  R$ 1.944,19  Hipercard  R$ 584,04  

How can i do for it be like bellow? 我该如何做才能像波纹管一样? (Type a 'enter' after each value) (在每个值后键入“输入”)

MasterCard  R$ 66,12  
Hipercard  R$ 7.501,98  
MasterCard  R$ 48,19  
Hipercard  R$ 7.742,46  
MasterCard  R$ 48,19  
Hipercard  R$ 5.846,14  
MasterCard  R$ 48,19  
Hipercard  R$ 4.210,65  
MasterCard  R$ 48,19  
Hipercard  R$ 2.892,20  
MasterCard  R$ 48,19  
Hipercard  R$ 1.944,19  
Hipercard  R$ 584,04  

Because then i can sort with this command: sort test2.txt /o output.txt 因为这样我可以使用以下命令进行sort test2.txt /o output.txtsort test2.txt /o output.txt

I guess doing by this way is easier. 我想通过这种方式更容易。


@michael_heath This is what i was talking: @michael_heath这是我在说的:

Hipercard   R$    584,04
Hipercard   R$  1.944,19
Hipercard   R$  2.892,20
Hipercard   R$  4.210,65
Hipercard   R$  5.846,14
Hipercard   R$  7.501,98
Hipercard   R$  7.742,46
------------------------
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     66,12
------------------------
Visa        R$     48,19
Visa        R$     48,19
Visa        R$     48,19
------------------------

Before you can sort the data, you'll need to parse it. 在对数据进行排序之前,您需要对其进行解析。 You can use the ConvertFrom-String cmdlet with a template to do this: 您可以将ConvertFrom-String cmdlet与模板一起使用来执行此操作:

$Template = @'
{CardProvider*:MasterCard}
R$ {Amount:66,12}

{CardProvider*:Hipercard}
R$ {Amount:7.501,98}

{CardProvider*:Visa}
R$ {Amount:4.210,65}

'@

$Transactions = Get-Content path\to\file.txt |ConvertFrom-String -TemplateContent $Template

The $Transactions variable will now contain an array of objects with a CardProvider and an Amount property each. $Transactions变量现在将包含一个对象数组,每个对象都具有CardProviderAmount属性。 To sort by provider and then by amount we can do something like: 要按提供者排序,然后按金额排序,我们可以执行以下操作:

# Define output file name
$OutputFile = 'output.txt'
# Group by card type first
$Transactions |Group-Object CardProvider |ForEach-Object {
    # Write seperator to output file:
    '----------------' |Add-Content -Path $OutputFile 
    # Now we sort each group of cards by descending amount
    $_.Group |Sort-Object {$_.Amount -replace '\.' -replace ',','.' -as [decimal]} -Descending |ForEach-Object {
        # Finally write the contents to file again
        $_.CardProvider,"R$ $($_.Amount)","" |Add-Content -Path $OutputFile
    }
}

All batch-file code 所有批处理文件代码

Option 1 选项1

@echo off
setlocal enabledelayedexpansion

(
    for /f "tokens=1,2" %%A in (frt01.txt) do (
        if not "%%~A" == "R$" (
            set "card=%%~A          "
            set "card=!card:~,11!"
        ) else (
            set "value=          %%~B"
            set "value=!value:~-9!"
            echo !card! %%~A !value!
        )
    )
) > unsorted.txt

sort unsorted.txt /o output.txt

rem  Optional code below to insert ------- between rows after sort.

set "last_card="
(
    for /f "delims=" %%A in (output.txt) do (
        for /f %%B in ("%%~A") do set "card=%%~B"
        if defined last_card if not "!card!" == "!last_card!" (
            echo ------------------------
        )
        echo %%A
        set "last_card=!card!"
    )
    echo ------------------------
) > tmp.txt

move /y tmp.txt output.txt

This is the delayed expansion concept. 这是延迟扩展的概念。 Fixed spacing is used so sort can order better with different lengths of the cards and of the currency values. 使用固定的间距,以便在不同长度的卡和货币值的情况下可以更好地sort

  • for /? for token descriptions etc. 用于令牌描述等
  • set /? for substitution syntax used ie set "card=!card:~,11!" 用于使用的替换语法,即set "card=!card:~,11!" .

Option 2 选项2

@(
    @echo off
    setlocal enabledelayedexpansion
    for /f "tokens=1,2" %%A in (frt01.txt) do (
        if not "%%~A" == "R$" (
            set "card=%%~A          "
            call set "card=%%card:~,11%%"
        ) else (
            set "value=          %%~B"
            call set "value=%%value:~-9%%"
            call echo "%%card%%" "%%A" "%%value%%"
        )
    )
) | sort /o output.txt

This is a unusual concept. 这是一个不寻常的概念。 Like previous code, uses fixed spacing. 像以前的代码一样,使用固定的间距。 It uses no temporary file as it pipes direct to sort . 它不使用临时文件,因为它通过管道直接进行sort

Uses setlocal enabledelayedexpansion even though no ! 使用setlocal enabledelayedexpansion即使没有! are used. 被使用。 Delayed variables would not expand. 延迟变量不会扩展。 Without, delayed expansion, the call set and call echo would not work. 如果没有延迟扩展,则call setcall echo将无法工作。

Works on Win7 though perhaps it's strange behavior may make the other code a better option. 可在Win7上运行,尽管也许奇怪的行为可能会使其他代码成为更好的选择。


Option 3 选项3

@echo off
setlocal

if not "%~1" == "" (
    if "%~1" == "/?" goto :help
    for %%A in (%*) do call :read "%%~A"
    exit /b
)

rem If no arguments, do this optional task instead.
> "%~dp0unsorted.txt" call :read "frt01.txt"
sort "%~dp0unsorted.txt" /o "%~dp0output.txt"
del "%~dp0unsorted.txt"

exit /b

:read
setlocal enabledelayedexpansion

for /f "usebackq tokens=1,2" %%A in ("%~1") do (
    if not "%%~A" == "R$" (
        set "card=%%~A          "
        set "card=!card:~,11!"
    ) else (
        set "value=          %%~B"
        set "value=!value:~-9!"
        echo !card! %%~A !value!
    )
)
exit /b

:help
echo Prints Hipercard, MasterCard... vertical list as 3 horizontal fixed columns.
echo If multiple files are passed as arguments, they will be printed in sequence.
echo.
echo Syntax: %~nx0 file [file [file]]
echo.
echo Examples:
echo   %~nx0 "test 1.txt"
echo   %~nx0 test1.txt test2.text ^| sort /o output.txt
exit /b

This is a standalone script or run code below the rem remark line. 这是rem 备注行下方的独立脚本或运行代码。 You could remove the optional code and place the script in PATH so you can run it from a console anywhere. 您可以删除可选代码并将脚本放置在PATH中,以便可以从任何地方的控制台运行它。

If the script is named cardsorter.cmd, then you could ie 如果脚本名为cardsorter.cmd,则可以

cardsorter frt01.txt | sort /o output.txt

at a command prompt. 在命令提示符下。


Output Of All Options 所有选项的输出

Hipercard   R$    584,04
Hipercard   R$  1.944,19
Hipercard   R$  2.892,20
Hipercard   R$  4.210,65
Hipercard   R$  5.846,14
Hipercard   R$  7.501,98
Hipercard   R$  7.742,46
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     48,19
MasterCard  R$     66,12
Visa        R$  4.210,65
Visa        R$  4.210,65
Visa        R$  4.210,65

Note 注意

Option 1 inserts -------------- to separate card types. 选项1插入--------------以分隔卡类型。

Here is a batch file solution. 这是一个批处理文件解决方案。 To stack the lines together you just use a variable to hold the previous line. 要将行堆叠在一起,您只需使用变量来保留前一行。

@echo off
setlocal enabledelayedexpansion

set "line1="
(FOR /F "delims=" %%G IN (input.txt) DO (
    IF NOT DEFINED line1 (
        set "line1=%%G
    ) else (
        echo !line1! %%G
        set "line1="
    )
))>temp.txt
sort temp.txt>output.txt

The following code parses the input file into an array of transaction objects, sorts them, then outputs the sorted array. 以下代码将输入文件解析为事务对象的数组,对它们进行排序,然后输出排序后的数组。 Not that the amounts were sorted as a right-justified string. 并不是说金额是按右对齐的字符串排序的。

$unsortedtransactions = @()

Get-Content .\in.txt -ReadCount 3 |
    ForEach-Object {
        $Matches = $null
        "$_" -match '([^\s]*)\s*(\S*)\s*(.*)$(.*)$' | Out-Null
        $thisrecord = [PSCustomObject] @{ Card = $Matches[1]
            Currency = $Matches[2]
            Amount = $("{0,15}" -f @($Matches[3].Trim()))
        }
        $unsortedtransactions += $thisrecord
    }

$sortedtransactions = $unsortedtransactions | Sort-Object -Property Card, Currency, Amount

foreach ($t in $sortedtransactions) {
    $("{0}`r`n{1} {2}`r`n" -f @($t.Card, $t.Currency, $t.Amount.Trim())) |
        Out-File -FilePath st.txt -Encoding ascii -Append
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM