Powershell-使用特殊字符对字符串对象进行排序

cdt*_*cfc 5 sorting powershell

我在跑

'S-tst','ssrst','srst2','s-zaa','s-a','s-zf' | Sort-Object
Run Code Online (Sandbox Code Playgroud)

我不应该得到回报

s-a
S-tst
s-zaa
s-zf
srst2
ssrst
Run Code Online (Sandbox Code Playgroud)

但是我得到以下信息:

s-a
srst2
ssrst
S-tst
s-zaa
s-zf
Run Code Online (Sandbox Code Playgroud)

这怎么可能 ?排序对象在排序时仅看字母吗?有什么办法可以按特殊字符对它进行排序?

Joh*_*van 5

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

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
Run Code Online (Sandbox Code Playgroud)

输出:

s'a
S'a
s'a1
S'a1
s-a
S-a
s-a1
S-a1
sa
Sa
sa1
Sa1
s^a
S^a
Run Code Online (Sandbox Code Playgroud)

更多信息

根据注释中的@TessellatingHeckler,您可以通过将字符串转换为字符数组来按字符代码(序数)顺序对字符串进行排序。但是,它仍然以一种可能出乎意料的方式处理连字符和撇号(因为这些字符被忽略):

$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[]] $_ }
Run Code Online (Sandbox Code Playgroud)
s'a
S'a
s-a
S-a
s'a1
S'a1
s-a1
S-a1
s^a
S^a
sa
Sa
sa1
Sa1
Run Code Online (Sandbox Code Playgroud)

当前的排序行为是设计使然。PowerShell 似乎实现了“字排序”。这在此处记录:https : //msdn.microsoft.com/en-us/library/windows/desktop/dd318144(v=vs.85).aspx#SortingFunctions

除了忽略连字符和撇号(除非在比较相同的字符串时除外),这种排序还将标点符号视为出现在字母数字之前,并将重音字母与其对应物一起处理。一个简单的演示可以这样看:

32..255 | %{[string][char][byte]$_} | sort
Run Code Online (Sandbox Code Playgroud)

要定义其他排序行为,目前您可能需要深入研究 .Net,如下所示:

32..255 | %{[string][char][byte]$_} | sort
Run Code Online (Sandbox Code Playgroud)
Add-Type @"
Run Code Online (Sandbox Code Playgroud)
    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() {}
    }
Run Code Online (Sandbox Code Playgroud)

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

"@

[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 ', '
Run Code Online (Sandbox Code Playgroud)

我已经提交了一个功能建议,以在Sort-Object. 见https://github.com/PowerShell/PowerShell/issues/4098

  • `$a = [System.Collections.ArrayList]@('srs', 's-a', 's-z'); $a.Sort([System.StringComparer]::Ordinal)` - 来自 /sf/ask/1298068971/(可能使这个 Q 重复) (4认同)
  • 这是排序行为的 [Jon Skeet 解释](/sf/ask/1532058881/)。据我所知,`Sort-Object` 接受一个 `-Culture` 参数,但是我无法通过顺序排序找到文化,创建新的自定义文化需要管理员权限并在它之前在系统范围内注册可以使用,所以让PS有点卡住了。 (3认同)