以下列表未正确排序(恕我直言):
$a = @( 'ABCZ', 'ABC_', 'ABCA' )
$a | sort
ABC_
ABCA
ABCZ
我方便的ASCII图表和Unicode C0控件和基本拉丁图表 具有下划线(低线),序数为95(U + 005F)。这是一个比大写字母A-Z更高的数字。 Sort应该将字符串以下划线结尾。
Get-Culture是en-US
下一组命令符合我的预期:
$a = @( 'ABCZ', 'ABC_', 'ABCA' )
[System.Collections.ArrayList] $al = $a
$al.Sort( [System.StringComparer]::Ordinal )
$al
ABCA
ABCZ
ABC_
现在我创建一个包含相同3个字符串的ANSI编码文件:
Get-Content -Encoding Byte data.txt
65 66 67 90 13 10 65 66 67 95 13 10 65 66 67 65 13 10
$a = Get-Content data.txt
[System.Collections.ArrayList] $al = $a
$al.Sort( [System.StringComparer]::Ordinal )
$al
ABC_
ABCA
ABCZ
包含下划线/下线的字符串不再正确排序。我错过了什么?
编辑:
让我们参考这个例子#4:
'A' -lt '_'
False
[char] 'A' -lt [char] '_'
True
似乎两个语句都应为False或两者都应为True。我在第一个语句中比较字符串,然后比较Char类型。字符串只是Char类型的集合,所以我认为两个比较操作应该是等价的。
现在例如#5:
Get-Content -Encoding Byte data.txt
65 66 67 90 13 10 65 66 67 95 13 10 65 66 67 65 13 10
$a = Get-Content data.txt
$b = @( 'ABCZ', 'ABC_', 'ABCA' )
$a[0] -eq $b[0]; $a[1] -eq $b[1]; $a[2] -eq $b[2];
True
True
True
[System.Collections.ArrayList] $al = $a
[System.Collections.ArrayList] $bl = $b
$al[0] -eq $bl[0]; $al[1] -eq $bl[1]; $al[2] -eq $bl[2];
True
True
True
$al.Sort( [System.StringComparer]::Ordinal )
$bl.Sort( [System.StringComparer]::Ordinal )
$al
ABC_
ABCA
ABCZ
$bl
ABCA
ABCZ
ABC_
两个ArrayList包含相同的字符串,但排序方式不同。为什么呢?
答案 0 :(得分:2)
在许多情况下,PowerShell在/ PSObject
中包装/解包对象。在大多数情况下,它是透明地完成的,你甚至都没有注意到这一点,但在你的情况下,这是导致你麻烦的原因。
$a='ABCZ', 'ABC_', 'ABCA'
$a|Set-Content data.txt
$b=Get-Content data.txt
[Type]::GetTypeArray($a).FullName
# System.String
# System.String
# System.String
[Type]::GetTypeArray($b).FullName
# System.Management.Automation.PSObject
# System.Management.Automation.PSObject
# System.Management.Automation.PSObject
如您所见,Get-Content
返回的对象包含在PSObject
中,阻止StringComparer
查看基础字符串并进行正确比较。强类型字符串收集无法存储PSObject
,因此PowerShell将解包字符串以将其存储在强类型集合中,这允许StringComparer
查看字符串并对其进行正确比较。
首先,当您编写$a[1].GetType()
或$b[1].GetType()
时,您不会调用.NET方法,而是编写PowerShell方法,这些方法通常在包装对象上调用.NET方法。因此,您无法以这种方式获得真实类型的对象。更重要的是,它们可以被覆盖,请考虑以下代码:
$c='String'|Add-Member -Type ScriptMethod -Name GetType -Value {[int]} -Force -PassThru
$c.GetType().FullName
# System.Int32
让我们通过反射调用.NET方法:
$GetType=[Object].GetMethod('GetType')
$GetType.Invoke($c,$null).FullName
# System.String
$GetType.Invoke($a[1],$null).FullName
# System.String
$GetType.Invoke($b[1],$null).FullName
# System.String
现在我们获得了$c
的真实类型,但它说$b[1]
的类型是String
而不是PSObject
。正如我所说,在大多数情况下,透明地展开,所以你看到包裹String
而不是PSObject
本身。没有发生的一个特殊情况是:当您传递数组时,数组元素不会被解包。所以,让我们在这里添加更多级别的间接:
$Invoke=[Reflection.MethodInfo].GetMethod('Invoke',[Type[]]([Object],[Object[]]))
$Invoke.Invoke($GetType,($a[1],$null)).FullName
# System.String
$Invoke.Invoke($GetType,($b[1],$null)).FullName
# System.Management.Automation.PSObject
现在,当我们将$b[1]
作为数组的一部分传递时,我们可以看到它的真实类型:PSObject
。虽然,我更喜欢使用[Type]::GetTypeArray
。
关于StringComparer
:as you can see,当两个比较对象都不是字符串时,StringComparer
依靠IComparable.CompareTo
进行比较。并PSObject
实施IComparable
接口,以便根据PSObject
IComparable
实施进行排序。
答案 1 :(得分:0)
Windows使用的是Unicode,而不是ASCII,因此您所看到的是en-US的Unicode排序顺序。排序的一般规则是:
扩展你的例子,
$a = @( 'ABCZ', 'ABC_', 'ABCA', 'ABC4', 'abca' )
$a | sort-object
ABC_
ABC4
abca
ABCA
ABCZ
答案 2 :(得分:0)
如果你真的想这样做......我会承认它很难看,但它确实有效。如果这是你需要定期做的事情,我会创建一个函数。
$ a = @(' ABCZ',' ABC _',' ABCA',' ab1z') $ ascii = @()
foreach($ item中的$ item) { $ string ="" for($ i = 0; $ i -lt $ item.length; $ i ++) { $ char = [int] [char] $ item [$ i] $ string + =" $ char;" }
$ascii += $string
}
$ b = @()
foreach($ ascii | Sort-Object中的$ item) { $ string ="" $ array = $ item.Split(";") foreach($ array in $ array) { $ string + = [char] [int] $ char }
$b += $string
}
$一个 $ B
ABCA ABCZ ABC _
答案 3 :(得分:-1)
我尝试了以下操作,排序符合预期:
[System.Collections.ArrayList] $al = [String[]] $a