2011-03-25 15 views
27

Este es el famoso problema del orden "ASCIIbetical" frente al orden "Natural" aplicado a powershell. Para poder ordenar en PowerShell de la misma manera que lo hace el explorador, puede usar this wrapper sobre la API StrCmpLogicalW, que en realidad realiza la ordenación natural para el Explorador de Windows. Esto requerirá algo de fontanería.¿Cómo ordenar por nombre de archivo de la misma manera que lo hace Windows Explorer?

Sin embargo, this article sugiere que hay una implementación de tres líneas del tipo en python. Uno esperaría que el cmdlet Get-ChildItem o al menos el Proveedor del sistema de archivos pueda tener una opción de clasificación natural incorporada. Lamentablemente, no lo hacen.

Así que aquí está la pregunta, ¿cuál es la implementación más simple de esto en Powershell? Por simple me refiero a la menor cantidad de código para escribir, y posiblemente no a scripts/componentes de terceros/externos. Idealmente quiero una corta función Powershell que haría la clasificación para mí.

+0

Gracias a la pregunta interesante, simplemente he tratado de utilizar un evaluador habitual coincidencia de expresión en PowerShell. ¡Y realmente funciona! Debería pensar en otras aplicaciones, entonces. –

Respuesta

52

Aquí hay un código muy corto (solo el bloque de script $ToNatural) que hace el truco con una expresión regular y un evaluador de coincidencias para rellenar los números con espacios. Luego ordenamos la entrada con números acolchados como de costumbre y en realidad obtenemos un orden natural como resultado.

$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) } 

'----- test 1 ASCIIbetical order' 
Get-Content list.txt | Sort-Object 

'----- test 2 input with padded numbers' 
Get-Content list.txt | %{ . $ToNatural } 

'----- test 3 Natural order: sorted with padded numbers' 
Get-Content list.txt | Sort-Object $ToNatural 

Salida:

----- test 1 ASCIIbetical order 
1.txt 
10.txt 
3.txt 
a10b1.txt 
a1b1.txt 
a2b1.txt 
a2b11.txt 
a2b2.txt 
b1.txt 
b10.txt 
b2.txt 
----- test 2 input with padded numbers 
        1.txt 
        10.txt 
        3.txt 
a     10b     1.txt 
a     1b     1.txt 
a     2b     1.txt 
a     2b     11.txt 
a     2b     2.txt 
b     1.txt 
b     10.txt 
b     2.txt 
----- test 3 Natural order: sorted with padded numbers 
1.txt 
3.txt 
10.txt 
a1b1.txt 
a2b1.txt 
a2b2.txt 
a2b11.txt 
a10b1.txt 
b1.txt 
b2.txt 
b10.txt 

Y finalmente utilizamos esta sola línea para ordenar los archivos por los nombres en orden natural:

Get-ChildItem | Sort-Object { [regex]::Replace($_.Name, '\d+', { $args[0].Value.PadLeft(20) }) } 

Salida:

Directory: C:\TEMP\_110325_063356 

Mode    LastWriteTime  Length Name                             
----    -------------  ------ ----                             
-a---  2011-03-25  06:34   8 1.txt                             
-a---  2011-03-25  06:34   8 3.txt                             
-a---  2011-03-25  06:34   8 10.txt                             
-a---  2011-03-25  06:34   8 a1b1.txt                            
-a---  2011-03-25  06:34   8 a2b1.txt                            
-a---  2011-03-25  06:34   8 a2b2.txt                            
-a---  2011-03-25  06:34   8 a2b11.txt                            
-a---  2011-03-25  06:34   8 a10b1.txt                            
-a---  2011-03-25  06:34   8 b1.txt                             
-a---  2011-03-25  06:34   8 b2.txt                             
-a---  2011-03-25  06:34   8 b10.txt                            
-a---  2011-03-25  04:54   99 list.txt                            
-a---  2011-03-25  06:05  346 sort-natural.ps1                          
-a---  2011-03-25  06:35   96 test.ps1                            
+1

Puede guardar ese script de $ ToNatural en su perfil, luego estará disponible cuando lo desee. – JasonMArcher

2

traducción del pitón de PowerShell funciona bastante bien:

function sort-dir { 
    param($dir) 
    $toarray = { 
     @($_.BaseName -split '(\d+)' | ?{$_} | 
     % { if ([int]::TryParse($_,[ref]$null)) { [int]$_ } else { $_ } }) 
    } 
    gci $dir | sort -Property $toarray 
} 

#try it 
mkdir $env:TEMP\mytestsodir 
1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt } 
sort-dir $env:TEMP\mytestsodir 
Remove-Item $env:TEMP\mytestsodir -Recurse 

Puede hacerlo aún mejor cuando se utiliza Proxy function approach. Agregas el parámetro -natur al Sort-Object y tienes una solución bastante hermosa.

Actualización: Primero me sorprendió bastante que PowerShell maneje la comparación de matrices de esta manera. Después de que traté de crear archivos de prueba ("a0", "a100", "a2") + 1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt }, resultó que no funciona. Por lo tanto, creo que no hay una solución elegante como PowerShell, porque está estática bajo las cubiertas, mientras que Python es dinámico.

0

Permitir para copiar y pegar mi respuesta de otra pregunta.

Powershell Sort-Object Name with numbers doesn't properly

explorador de Windows utiliza una API legado en shlwapi.dll llamada StrCmpLogicalW al ordenar cadenas, esa es la razón por la que ver los diferentes resultados de clasificación.

No quiero utilizar el método de relleno de ceros, así que escriba un script.

https://github.com/LarrysGIT/Powershell-Natural-sort

Puesto que no soy un experto en C#, por lo tire solicitudes son apreciados.

Encuentra el siguiente script de powershell, utiliza la misma API.

function Sort-Naturally 
{ 
    PARAM(
     [string[]]$strArray 
    ) 

    Add-Type -TypeDefinition @' 
using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Runtime.InteropServices; 

namespace NaturalSort { 
    public static class NaturalSort 
    { 
     [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] 
     public static extern int StrCmpLogicalW(string psz1, string psz2); 

     public static System.Collections.ArrayList Sort(System.Collections.ArrayList foo) 
     { 
      foo.Sort(new NaturalStringComparer()); 
      return foo; 
     } 
    } 

    public class NaturalStringComparer : IComparer 
    { 
     public int Compare(object x, object y) 
     { 
      return NaturalSort.StrCmpLogicalW(x.ToString(), y.ToString()); 
     } 
    } 
} 
'@ 

    return [NaturalSort.NaturalSort]::Sort($strArray) 
} 

Encuentra resultados de pruebas a continuación.

PS> # Natural sort 
PS> . .\NaturalSort.ps1 
PS> Sort-Naturally -strArray @('2', '1', '11') 
1 
2 
11 
PS> # If regular sort is used 
PS> @('2', '1', '11') | Sort-Object 
1 
11 
2 

Y,

PS> # Not good 
PS> $t = (ls .\Scripts*.txt).name 
PS> $t | Sort-Object 
Scripts1.txt 
Scripts10.txt 
Scripts2.txt 
PS> # Good 
PS> Sort-Naturally -strArray $t 
Scripts1.txt 
Scripts2.txt 
Scripts10.txt 
+0

¿Puede dar ventajas/desventajas de su respuesta en comparación con la respuesta aceptada? –

Cuestiones relacionadas