12

次のリストは正しくソートされません (IMHO):

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

私の便利な ASCII チャートと Unicode C0 Controls および Basic Latin チャートには、序数が 95 (U+005F) のアンダースコア (低い線) があります。これは、大文字の AZ よりも大きい数字です。並べ替えは、アンダースコアで終わる文字列を最後に配置する必要がありました。

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 型のコレクションであるため、2 つの比較操作は同等である必要があると思います。

そして今、例#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_

2 つの ArrayList には同じ文字列が含まれていますが、並べ替えが異なります。なんで?

4

4 に答える 4

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 メソッドを呼び出すのではなく、通常はラップされたオブジェクトで .NET メソッドを呼び出す PowerShell メソッドを呼び出します。したがって、この方法では実際のタイプのオブジェクトを取得できません。さらに、それらはオーバーライドできます。次のコードを検討してください。

$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ます。それが起こらない特定のケースの 1 つは、配列を渡すと、配列要素がアンラップされないことです。そこで、ここに間接的なレベルを追加しましょう。

$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ただし、代わりに使用することを好みます。

About StringComparer:ご覧のとおり、比較対象の両方が文字列でない場合は、 にStringComparer依存しIComparable.CompareToて比較します。そして、PSObject実装IComparableに従ってソートが行われるように、インターフェースをPSObject IComparable実装します。

于 2015-11-14T02:26:44.403 に答える
0

Windows は ASCII ではなく Unicode を使用するため、表示されているのは en-US の Unicode ソート順です。並べ替えの一般的なルールは次のとおりです。

  1. 数字、次に小文字と大文字の混合
  2. 数字の前に特殊文字があります。

あなたの例を拡張すると、

$a = @( 'ABCZ', 'ABC_', 'ABCA', 'ABC4', 'abca' )

$a | sort-object
ABC_
ABC4
abca
ABCA
ABCZ
于 2014-09-12T18:21:37.187 に答える
0

あなたが本当にこれをやりたいのなら....醜いことは認めますが、うまくいきます。これが定期的に行う必要がある場合は、関数を作成します。

$a = @( 'ABCZ', 'ABC_', 'ABCA', 'ab1z' ) $ascii = @()

foreach ($a の $item) { $string = "" for ($i = 0; $i -lt $item.length; $i++) { $char = [int] [char] $item[$i] $文字列 += "$char;" }

$ascii += $string
}

$b = @()

foreach ($ascii の $item | Sort-Object) { $string = "" $array = $item.Split(";") foreach ($char in $array) { $string += [char] [int] $文字}

$b += $string
}

$a $b

ABCA ABCZ ABC_

于 2015-11-13T23:23:34.973 に答える