Сортировать Fileinfo список в порядке возрастания - PullRequest
0 голосов
/ 30 июня 2019

У меня есть список файлов следующим образом

10_2017
123_2018
500_2017
20_2019
100_2017
25_2017
32_2018

Который я хочу отсортировать как

10_2017
25_2017
100_2017
500_2017
32_2018
123_2018
20_2019

Я могу отсортировать массив по Array.sort(f1,new FileInfoSort), если я читаю годыотдельно, но мне нужно это в одном отсортированном массиве или списке

Я пробовал несколько потоков здесь, включая этот метод .Я использовал f1.Sort(Function(x, y) x.Name.CompareTo(y.Name)) и Array.Sort(f1.toArray, New FileInfoSort), но безуспешно. Я даже пытался разделить файлы по папкам по годам и раздельно читать их в списки, объединяя их, что, похоже, не сработало

Использование кода

Dim d1 As New DirectoryInfo(AppFolder)
Dim f1 As List(Of FileInfo) = d1.GetFiles("*.LBK", SearchOption.TopDirectoryOnly).ToList
'f1.Sort(Function(x, y) x.Name.CompareTo(y.Name))
f1.SortNatural(Function(x) x.Name)

Разделение файлов по папкам по годам, затем чтение и объединение

        Dim d1 As New DirectoryInfo(AppFolder.User.data)
        Dim d2 As List(Of DirectoryInfo) = New List(Of DirectoryInfo)
        'data folders have names like "d_2019"
        For Each d As DirectoryInfo In d1.GetDirectories
            If d.Name.ToString.Substring(0, 1) = "d" Then d2.Add(d)
        Next
        Dim f1 As List(Of FileInfo) = New List(Of FileInfo)
        For Each Dir As DirectoryInfo In d2
            f1.AddRange(Dir.GetFiles("*.LBK", SearchOption.TopDirectoryOnly))
            'trying to sort
            'f1.Sort(Function(x, y) x.Name.CompareTo(y.Name))
            f1.SortNatural(Function(x) x.Name)
        Next
Module ListExt
    <DllImport("shlwapi.dll", CharSet:=CharSet.Unicode)>
    Private Function StrCmpLogicalW(ByVal lhs As String, ByVal rhs As String) As Integer

    End Function

    <Extension()>
    Sub SortNatural(Of T)(ByVal self As List(Of T), ByVal stringSelector As Func(Of T, String))
        self.Sort(Function(lhs, rhs) StrCmpLogicalW(stringSelector(lhs), stringSelector(rhs)))
    End Sub

    <Extension()>
    Sub SortNatural(ByVal self As List(Of String))
        self.Sort(AddressOf StrCmpLogicalW)
    End Sub
End Module

Любой метод, который я использовал до сих пор, выводит

10_2017
20_2019
25_2017
32_2018
100_2017
123_2018
500_2017

У меня нетИдея, как подойти к этому даже.Если такой список / массив не представляется возможным, идеи о том, как я мог бы структурировать свои файлы так, чтобы я мог читать файлы в порядке, который я предпочитаю выше, также приветствуются!

1 Ответ

1 голос
/ 30 июня 2019

Вам нужно будет написать код, чтобы разобрать отдельные разделы имени и рассматривать их как числа. Любой встроенный компаратор, который вы используете, будет обрабатывать строки как строки, где все, что начинается с 1, предшествует всему, что начинается с 2, даже когда значения «100» и «2». Даже так называемые «естественные» сорта достаточно хороши для проверки первого раздела.

Вам также нужно убрать расширение LBK из названия.

Dim splitChars() As Char = {"_"c}
Dim d1 As New DirectoryInfo(AppFolder)
Dim f1 As List(Of FileInfo) = 
   d1.EnumerateFiles("*.LBK", SearchOption.TopDirectoryOnly).
   OrderBy(Function(fi) 
              Dim parts = fi.Name.Replace(".LBK", "").Split(splitChars)
              Return (Integer.Parse(parts(1)) * 1000) + Integer.Parse(parts(0))
           End Function).
   ToList()

Для забавы, вот версия Regex:

Dim exp As New Regex("(\d{1,3})_(\d{4})");

Dim d1 As New DirectoryInfo(AppFolder)
Dim f1 As List(Of FileInfo) = 
   d1.EnumerateFiles("*.LBK", SearchOption.TopDirectoryOnly).
   OrderBy(Function(fi) 
              Dim parts = exp.Matches(fi.Name)(0).Groups
              Return (Integer.Parse(parts(2).Value) * 1000) + Integer.Parse(parts(1).Value)
           End Function).
   ToList()

Посмотрите, как это работает здесь:

https://dotnetfiddle.net/UAae8P


Я также хотел прокомментировать эту строку из одного из примеров в вопросе:

If d.Name.ToString.Substring(0, 1) = "d" Then d2.Add(d)

Выделено как требующее некоторого внимания. Здесь тонна дополнительной работы, которая не нужна:

  • d.Name уже является строкой, не нужно звонить ToString()
  • Поскольку вам нужен только один символ, вы можете получить к нему доступ через индекс, не нужно вызывать Substring()
  • Вы сравниваете один символ , нет необходимости сравнивать строки.

Вы действительно хотите это:

If d.Name(0) = "d"c Then d2.Add(d)

Это на 1040 * много меньше кода, и он будет работать намного лучше, и вам стоит потратить время на изучение этого и понять, почему.

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