使用Powershell对CSV文件进行排序

4
我正在尝试对一个大型 CSV 文件进行排序。问题出在使用 sort-object 命令时,针对列名为 Combined 的列。它没有按照我期望的方式进行排序!为了测试,我已经删除了所有未被排序的数据,并且减少了行数。
原始 CSV:
Combined
1A THE BIG
7
3A SPRING
19
LUZREN
21
23
25
29
1 HONEY
5
3 THE GOOD
11
ARVALA
BRASAID
13
MEADOWCLAW

PowerShell:
Import-Csv orginal.csv -delimiter ',' | Sort-Object -Unique Combined 

Combined
--------
1 HONEY
11
13
19
1A THE BIG
21
23
25
29
3 THE GOOD
3A SPRING
5
7
ARVALA
BRASAID
LUZREN
MEADOWCLAW

我期望的是这样的:
Combined
1A THE BIG
5
7
11
13
19
21
23
25
29
1 HONEY
3 THE GOOD
3A SPRING
ARVALA
BRASAID
LUZREN
MEADOWCLAW

在完美的世界里,我希望是这样的:
Combined
1A THE BIG
1 HONEY
3 THE GOOD
3A SPRING
5
7
11
13
19
21
23
25
29
ARVALA
BRASAID
LUZREN
MEADOWCLAW

请问有人能解释一下为什么会发生这种情况吗?我已经对着显示器砸了一个星期了。另外,我该如何获得我想要的输出结果呢?
提前感谢!

默认排序是逐个符号排序。为什么“1A THE BIG”应该排在“1 HONEY”之前? - Alexey A.
它像字符串一样对它们进行排序,这是它应该工作的方式。如果你把每个数字看作是在A之前的字母,那么按字母顺序排序就很合理。 - JNK
3个回答

4

我不确定我理解排序逻辑,但是试试这个:

$StartsWithNumber = { if ($_.Combined -match '^\d*\D' ) { $_.Combined } }
$IsNumber = { if ($_.Combined -match '^\d*$') { [int]$_.Combined } }
$OnlyLetters = { if ($_.Combined -imatch '^[a-z ]*$') { $_.Combined } }

Import-Csv original.csv | 
Sort-Object $OnlyLetters, $IsNumber, $StartsWithNumber -Unique

Combined
--------
1 HONEY
1A THE BIG
3 THE GOOD
3A SPRING
5
7
11
13
19
21
23
25
29
ARVALA
BRASAID
LUZREN
MEADOWCLAW

1
+1 我认为在 $startsWithNumer 中使用正则表达式更好,如 '^\d*\D',以防某些值以多个数字开头。 对吗? - CB.
太好了!我的完美世界的输出有误(是我自己的失误)。你的解决方案输出了我需要的结果。谢谢! - jetgerbil

1
尝试这个:(不是完美世界中的)
Import-Csv original.csv -delimiter ',' | 
Sort-Object { [int]([regex]::Replace( $_.combined , "\D" , "" )) } ,  `
{ [regex]::Replace( $_.combined , "\d" , "" ) } -unique

Combined
--------
ARVALA
BRASAID
LUZREN
MEADOWCLAW
1 HONEY
1A THE BIG
3 THE GOOD
3A SPRING
5
7
11
13
19
21
23
25
29

在@Aryadev的好回答之后,这段代码还可以对以多于一个数字开头的值进行排序:

$allToNumbers = { [int]([regex]::Replace( $_.combined , "\D" , "" )) }
$StartsWithNumber = { if ($_.Combined -match '^\d+\D') { $_.Combined } }
$IsNumber = { if ($_.Combined -match '^\d*$') { [int]$_.Combined } }
$OnlyLetters = { if ($_.Combined -imatch '^[a-z ]*$') { $_.Combined } }

Import-Csv original.csv | 
Sort-Object  $OnlyLetters, $allnumbers, $IsNumber,  $StartsWithNumber -Unique

0

Import-CSV 默认将所有值作为 string 导入。这就是你看到的结果。它按字母顺序排序。在字母数字中,“priority”的顺序是:空格、数字、字母。

我认为你不能在没有一些严肃的代码的情况下得到想要的输出。我的唯一建议是将纯数字解析为 int,这样例如数字 30 就会在数字 5 之后。可以像这样完成:

$intvalue = 10000
import-csv .\test.csv | % {
    if ([int]::TryParse($_.Combined, [ref]$intvalue)) {
        $_.Combined = $intvalue
    }
    $_
} | Sort-Object -Unique Combined

Combined
--------
1 HONEY
5
7
11
13
19
1A THE BIG
21
23
25
29
3 THE GOOD
3A SPRING
ARVALA
BRASAID
LUZREN 
MEADOWCLAW 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接