2014-09-08 4 views
12

La liste suivante ne trie pas correctement (à mon humble avis):Powershell Trier de chaînes par des underscores

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

Mon tableau ASCII pratique et Unicode Commandes C0 et carte de base latine ont le trait de soulignement (ligne basse) avec un ordinal de 95 (U + 005F). C'est un nombre plus élevé que les lettres majuscules A-Z. Le tri aurait dû mettre la fin de la chaîne avec un trait de soulignement en dernier.

Get-Culture est en-US

La prochaine série de commandes fait ce que je pense:

$a = @('ABCZ', 'ABC_', 'ABCA') 
[System.Collections.ArrayList] $al = $a 
$al.Sort([System.StringComparer]::Ordinal) 
$al 
ABCA 
ABCZ 
ABC_ 

Maintenant, je crée un fichier codé ANSI contenant les mêmes 3 chaînes:

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 

Une fois de plus, la chaîne contenant le trait de soulignement/ligne de fond n'est pas triée correctement. Qu'est-ce que je rate?


Edit:

Soit Référence cet exemple # 4:

'A' -lt '_' 
False 
[char] 'A' -lt [char] '_' 
True 

On dirait que les deux déclarations doivent être faux ou les deux doivent être vrai. Je compare les chaînes dans la première déclaration, puis je compare le type Char. Une chaîne est simplement une collection de types Char, donc je pense que les deux opérations de comparaison devraient être équivalentes.

Et maintenant, par exemple # 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_ 

Les deux ArrayList contiennent les mêmes chaînes, mais sont triées différemment. Pourquoi?

+2

Je pense que ce qui vous manque, c'est que vous attendez des réponses non standard de Windows. Il a toujours priorisé les symboles avant les lettres, il suffit de regarder le système de fichiers. Créez des fichiers avec ces noms, triez-les par nom, et ils seront triés de la même façon avec ABC_ en premier. – TheMadTechnician

+5

[Le tri des chaînes n'est plus effectué par le code ASCII.] (Http://blogs.msdn.com/b/oldnewthing/archive/2004/05/18/134051.aspx) –

+0

Aussi, autant que je sache, bizarrerie avec la deuxième partie a quelque chose à voir avec 'ArrayList'. Utiliser un 'String.Collections.Generic.List [chaîne]' fortement typé 'trie comme prévu. De plus, utiliser 'string []' est comme prévu avec 'Array :: Sort', mais' object [] 'non. –

Répondre

0

Windows utilise Unicode, pas ASCII, ce que vous voyez est l'ordre de tri Unicode pour en-US. Les règles générales de tri sont:

  1. numéros, puis entremêlées minuscules et majuscules
  2. caractères spéciaux se produisent avant les numéros.

Prolonger votre exemple,

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

$a | sort-object 
ABC_ 
ABC4 
abca 
ABCA 
ABCZ 
+0

Mais l'OP demande explicitement l'ordre 'Ordinal', et chaque objet individuel dans '$ a' rapporte un type de' String', mais ils ne rentrent pas dans un tableau 'String'. Donc, oui, nous obtenons l'ordre Unicode par défaut sur 'Object' au lieu de l'ordre' Ordinal' demandé. Mais pourquoi? –

+0

Le tri des chaînes Unicode peut être vu en pratique ici: http://minaret.info/test/sort.msp – Bradski

-1

J'ai essayé ce qui suit et le tri est comme prévu:

[System.Collections.ArrayList] $al = [String[]] $a 
0

Si vous voulez vraiment faire .... Je dois admettre qu'il est moche mais ça marche. Je créerais une fonction si c'est quelque chose que vous devez faire sur une base régulière.

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

foreach (poste $ en $ a) { $ string = "" pour ($ i = 0; $ i -lt $ article.longueur; $ i ++) { $ char = [int] [char] $ article [$ i] $ chaîne + = "$ char;" }

$ascii += $string 
} 

$ b = @()

foreach ($ item en ascii $ | Sort-Object) { $ string = "" $ array = item.Split $ ("; «) foreach ($ omble chevalier dans le tableau $) { $ string + = [char] [int] $ omble chevalier }

$b += $string 
} 

$ par $ b

ABCA ABCZ ABC_

2

Dans de nombreux cas wrap PowerShell/objets Déballer dans/de PSObject. Dans la plupart des cas, c'est fait de manière transparente, et vous ne le remarquez même pas, mais dans votre cas, c'est ce qui cause votre problème.

$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 

Comme vous pouvez le voir, l'objet retourné de Get-Content sont enveloppés dans PSObject, qui empêchent StringComparer de voir des chaînes sous-jacentes et de les comparer correctement. La collecte de chaînes fortement typée ne peut pas stocker PSObject s, donc PowerShell dépliera les chaînes pour les stocker dans une collection fortement typée, ce qui permet à StringComparer de voir les chaînes et de les comparer correctement.

Edit:

Tout d'abord, lorsque vous écrivez que $a[1].GetType() ou que vous $b[1].GetType() ne remet pas les méthodes .NET, mais les méthodes PowerShell, qui nécessitent normalement des méthodes .NET sur l'objet enveloppé. Ainsi, vous ne pouvez pas obtenir le type réel d'objets de cette façon. Plus encore, les peuvent être outrepassées, considérez ce code:

$c='String'|Add-Member -Type ScriptMethod -Name GetType -Value {[int]} -Force -PassThru 
$c.GetType().FullName 
# System.Int32 

Appelons méthodes .NET thru réflexion:

$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 

Maintenant, nous obtenons le type réel pour $c, mais il est dit que le type de $b[1] est String et non PSObject. Comme je le dis, dans la plupart des cas déballage fait de manière transparente, de sorte que vous voyez String enveloppé et pas PSObject lui-même. Un cas particulier où cela ne se produit pas est le suivant: lorsque vous passez le tableau, les éléments du tableau ne sont pas déballés. Alors, laissez-nous ajouter le niveau d'indirection supplémentaire ici:

$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 

Maintenant, nous passons $b[1] dans le cadre du tableau, on peut voir le type réel de celui-ci: PSObject. Bien que, je préfère utiliser [Type]::GetTypeArray à la place.

A propos StringComparer: as you can see, quand sont des chaînes, puis StringComparer pas des objets à la fois par rapport reposent sur IComparable.CompareTo à titre de comparaison. Et PSObject mettre en œuvre l'interface IComparable, de sorte que le tri sera effectué conformément à la mise en œuvre PSObjectIComparable.

+0

Je pense que vous êtes sur quelque chose. Mais le tri réorganise les objets PSObject, mais pas comme je le pensais. $ a [1] .GetType(). Name et $ b [1] .GetType(). Name retourne "String". Pouvez-vous me diriger vers la documentation avec plus de détails sur les tableaux, PSObjects, et comment le StringComparer pourrait fonctionner lors de la présentation de PSObjects? Merci. – bretth

+1

@bretth Je mets à jour ma réponse. Désolé, je ne peux pas vous indiquer une bonne documentation à ce sujet. Grande partie de mes connaissances PowerShell obtenues par l'expérimentation et le creusement avec ILSpy. À mon humble avis, PowerShell manque vraiment de documentation sur de nombreuses pièces internes. – PetSerAl

+0

Ceci peut aussi être lié? https://stackoverflow.com/questions/44731470/powershell-sorting-string-objects-with-a-special-character – JohnLBevan

Questions connexes