sorting - Powershell 对带下划线的字符串进行排序

标签 sorting powershell stringcomparer

以下列表无法正确排序(恕我直言):

$a = @( 'ABCZ', 'ABC_', 'ABCA' )
$a | sort
ABC_
ABCA
ABCZ

我方便的 ASCII 图表和 Unicode C0 控件以及基本拉丁语图表 有下划线(低线),序数为 95 (U+005F)。该数字比大写字母 A-Z 更大。排序应该将以下划线结尾的字符串放在最后。

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包含相同的字符串,但排序方式不同。为什么?

最佳答案

在许多情况下,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 接口(interface),以便根据 PSObject IComparable 实现进行排序。

关于sorting - Powershell 对带下划线的字符串进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25734016/

相关文章:

azure - 在没有基本身份验证的情况下使用 Powershell 连接 Exchange Online

c# - 什么.NET StringComparer 相当于 SQL 的 Latin1_General_CI_AS

c# - 字符串与C#中的特殊字符比较

python - Sqlalchemy 以相同的顺序获得结果

java - 我怎样才能构建一个比较器,以与 PostgreSQL 相同的方式对字符串进行排序?

c# - C#-如何按顺时针顺序(tl,tr,br,bl)对4点列表进行排序,以用于opencv getPerspective?

php - MYSQL中的if条件用于排序距离和检查状态

powershell - 将 Gitlab CI Trigger Curl 转换为 Powershell Invoke-RestMethod

powershell - 实时测试连接输出,无间隔