关于Powershell:Powershell – 使用特殊字符对字符串对象进行排序

Powershell - Sorting string objects with a special character

我在跑步

1
'S-tst','ssrst','srst2','s-zaa','s-a','s-zf' | Sort-Object

我不应该得到回报吗

1
2
3
4
5
6
s-a
s-tst
s-zaa
s-zf
srst2
ssrst

但是我得到了以下信息:

1
2
3
4
5
6
s-a
srst2
ssrst
S-tst
s-zaa
s-zf

这怎么可能?排序对象在排序时只看字母吗?有没有办法用特殊的字符来分类?


这种行为是设计出来的,但并不总是人们想要/期望的。如果要用每个字符按ASCII顺序排序字符串,请使用以下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Add-Type @"
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;

    public class SimpleStringComparer: IComparer, IComparer<string>
    {

        private static readonly CompareInfo compareInfo = CompareInfo.GetCompareInfo(CultureInfo.InvariantCulture.Name);

        public int Compare(object x, object y)
        {
            return Compare(x as string, y as string);
        }
        public int Compare(string x, string y)
        {
            return compareInfo.Compare(x, y, CompareOptions.OrdinalIgnoreCase);
        }
        public SimpleStringComparer() {}
    }
"
@


[string[]]$myList = 's-a','s-a1','s''a','s''a1', 'sa','sa1','s^a','S-a','S-a1','S''a','S''a1', 'Sa','Sa1','S^a'

[System.Collections.Generic.List[string]]$list = [System.Collections.Generic.List[string]]::new()
$list.AddRange($myList)
[SimpleStringComparer]$comparer = [SimpleStringComparer]::new()
$list.Sort([SimpleStringComparer]::new())
$list

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
s'a
S'
a
s'a1
S'
a1
s-a
S-a
s-a1
S-a1
sa
Sa
sa1
Sa1
s^a
S^a

更多信息

对于注释中的@tessellatingheckler,可以将字符串强制转换为char数组,从而按字符代码(序数)顺序对字符串进行排序。但是,它仍然以一种潜在的意外方式处理连字符和撇号(因为这些字符被忽略):

1
2
$myList = 's-a','s-a1','s''a','s''a1', 'sa','sa1','s^a','S-a','S-a1','S''a','S''a1', 'Sa','Sa1','S^a'
$myList | Sort-Object -Property { [char[]] $_ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
s'a
S'
a
s-a
S-a
s'a1
S'
a1
s-a1
S-a1
s^a
S^a
sa
Sa
sa1
Sa1

当前的排序行为是通过设计实现的。PowerShell似乎实现了"单词排序"。本文记录如下:https://msdn.microsoft.com/en-us/library/windows/desktop/dd318144(v=vs.85).aspx排序函数

除了忽略连字符和撇号(除了比较其他相同的字符串时),这种类型还将标点字符视为字母数字之前的字符,并处理重音字母及其对应字符。简单的演示如下:

1
32..255 | %{[string][char][byte]$_} | sort

要定义其他排序行为,目前您可能需要使用.NET,例如:

1
Add-Type @"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    using System;
    using System.Runtime.InteropServices;
    using System.Collections;
    public class NumericStringComparer: IComparer
    {
        //https://msdn.microsoft.com/en-us/library/windows/desktop/bb759947%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
        [DllImport("shlwapi.dll")]
        public static extern int StrCmpLogicalW(string psz1, string psz2);
        public int Compare(object x, object y)
        {
            return Compare(x as string, y as string);
        }
        public int Compare(string x, string y)
        {
            return StrCmpLogicalW(x, y);
        }
        public NumericStringComparer() {}
    }
1
2
3
4
5
"@

[System.Collections.ArrayList]$myList = 's-a','s-a1','s''a','s''a1', 'sa','sa1','s^a','S-a','S-a1','S''a','S''a1', 'Sa','Sa1','S^a', , '100a','1a','001a','2a','20a'
$myList.Sort([NumericStringComparer]::new())
$myList -join ', '

上面按照Windows资源管理器的方式对字符串进行排序(即,将前导数字视为数字值):

1
s'a, s'a1, S'a, s-a, S-a, S-a1, S'a1, s-a1, S^a, s^a, 1a, 001a, 2a, Sa, Sa1, sa, sa1, 20a, 100a

我已经提交了一个特性建议,以便在Sort-Object上提供更友好的PS解决方案。请参阅https://github.com/powershell/powershell/issues/4098