Как отсортировать по имени файла так же, как это делает проводник Windows? - PullRequest
42 голосов
/ 25 марта 2011

Это известная проблема порядка "ASCIIbetical" по сравнению с порядком "Natural" применительно к powershell.Чтобы иметь возможность сортировать в PowerShell так же, как это делает проводник, вы можете использовать эту оболочку через API StrCmpLogicalW, которая фактически выполняет естественную сортировку для Windows Explorer.Это потребует некоторой сантехники.

Тем не менее, эта статья предполагает, что в Python есть реализация с тремя строками.Можно надеяться, что командлет Get-ChildItem или, по крайней мере, провайдер файловой системы могут иметь встроенную опцию естественной сортировки.К сожалению, они этого не делают.

Итак, вот вопрос, какова простейшая реализация этого в Powershell?Под простым я подразумеваю наименьшее количество кода для написания и, возможно, никаких сторонних / внешних скриптов / компонентов.В идеале мне нужна функция short Powershell, которая бы выполняла для меня сортировку.

Ответы [ 3 ]

67 голосов
/ 25 марта 2011

Вот очень короткий код (только блок сценария $ToNatural), который выполняет трюк с регулярным выражением и оценщиком совпадений, чтобы заполнить числа пробелами. Затем мы сортируем входные данные с добавленными числами, как обычно, и в результате получаем естественный порядок.

$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

Выход:

----- 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

И, наконец, мы используем этот однострочный для сортировки файлов по именам в естественном порядке:

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

Выход:

    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                                                                                                              
3 голосов
/ 19 января 2018

Позвольте мне скопировать и вставить свой ответ из другого вопроса.

Имя объекта сортировки Powershell с номерами неправильно

Проводник Windows использует устаревшую версиюAPI в shlwapi.dll с именем StrCmpLogicalW при сортировке строк, поэтому мы видим разные результаты сортировки.

Я не хочу использовать метод заполнения нулями, поэтому напишите скрипт.

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

Так как я не эксперт по C #, поэтому запросы на получение запросов приветствуются.

Найдите следующий скрипт powershell, использующий тот же 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)
}

Поиск результатов тестаниже.

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

А,

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
3 голосов
/ 25 марта 2011

Перевод с python на PowerShell работает довольно хорошо:

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

Вы можете сделать это еще лучше, если используете Подход прокси-функции . Вы добавляете параметр -natur к Sort-Object, и у вас есть довольно красивое решение.

Обновление : Сначала я был довольно удивлен, что PowerShell обрабатывает сравнение массивов таким образом. После того, как я попытался создать тестовые файлы ("a0", "a100", "a2") + 1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt }, оказалось, что это не работает. Итак, я думаю, что не существует элегантного решения, как, например, PowerShell статичен, а python динамичен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...