Member 11089831 Ответов: 1

Как мне справиться с сортировкой в этом особом случае?


Vb.net использование VS 2015 для кодирования.

Я создал приложение для нашего инженерного персонала, которое извлекает "последнюю" версию печати (формат pdf) - при вводе номера печати. Файлы называются в этом формате-drawing number_revision_page_date
пример 0092933_C_1-2_061217. pdf

У меня есть две проблемы, с которыми мне нужна помощь. Я нахожу файлы с помощью directory.getfiles с лямбда-функцией orderbydescending. Раньше я использовал полное имя файла, однако у нас есть рисунки, которые обозначаются двумя буквами. revs - это от a до z - затем повторите с aa, ab, ac ... и т. д. Windows при сортировке по имени файла перечислит rev aa перед z. поэтому он всегда выбирает z в качестве верхней rev при сортировке по убыванию по имени файла. потому что он сортирует символы слева направо по столбцам. Итак, моя следующая попытка заключалась в использовании даты в имени файла с использованием точного синтаксического анализа даты, преобразующего последние 6 цифр в дату. Моя проблема сейчас в том, что по какой-то причине rev b и c были сделаны в один и тот же день. b сначала появляется в результатах ... но поскольку он смотрит только на дату, либо он может быть результатом, поскольку даты совпадают.

Первоначально я смотрел на свойство system last modified date - file, однако в случае, когда кто-то по какой-либо причине воссоздает старый PDF-файл, эта дата теперь новее последней версии и выбирается, так что ее нельзя использовать.

каковы мои варианты здесь, чтобы гарантировать, что я выбираю последний файл?

вот пример моего оператора get files.

Что я уже пробовал:

Dim result = Directory.GetFiles(fp, f).OrderByDescending(Function(x) Date.ParseExact(x.Substring(x.Length - 10, 6), "MMddyy", enusCulture)).First

1 Ответов

Рейтинг:
1

Graeme_Grant

Требование: двойная буква оборотов-от a до z-затем повторите с aa, ab, ac...и т. д. С rev aa перед z. длина перед Альфа, затем убывающая на альфа.

Таким образом, для пользовательской сортировки версии ревизии Интерфейс IComparer<t>
[^] лучше всего подходит для этой задачи. Вот компаратор, который будет сортировать по длине, а затем по буквам альфа-версии:

Class RevisionComparer
    Implements IComparer(Of String)

    Private Function IComparer_Compare(x As String, y As String) As Integer _
        Implements IComparer(Of String).Compare

        ' same values
        If x = y Then Return 0

        ' different lengths
        If x.Length > y.Length Then Return 1
        If x.Length < y.Length Then Return -1

        ' the same length, more work required
        If x.Length = y.Length Then

            For i As Integer = 0 To x.Length - 1

                ' check by position
                If x(i) > y(i) Then Return 1
                If x(i) < y(i) Then Return -1

            Next

        End If

        ' must be identical, so no change
        Return 0

    End Function

End Class

Прежде чем мы сможем что-либо сделать, нам нужно создать и протестировать RevisionComparer класс:
Module Module1

    Sub Main()

        Dim testRevisions = New List(Of String)() From {
            "c",
            "aa",
            "az",
            "ba",
            "bz",
            "aaa",
            "bca",
            "cba",
            "aza",
            "azz"
        }

        Dim sortedRevisions = testRevisions.OrderByDescending(Function(x) x, New RevisionComparer())

        For Each item In sortedRevisions
            Console.WriteLine(item)
        Next

        Console.WriteLine("-- DONE --")
        Console.ReadKey()

    End Sub

End Module

Который выводит:
cba
bca
azz
aza
aaa
bz
ba
az
aa
c
-- DONE --

Итак, теперь мы готовы к главному событию.

Требования таковы:
1. файлы называются в этом формате-drawing number_revision_page_date
.... пример 0092933_C_1-2_061217. pdf
2. двойная буква оборотов-от А до Я-затем повторите с aa,ab, ac...и т. д.
3. Дата до пересмотра??? (предполагается, что не уточняется)

Вот решение, основанное на вышеуказанных 3-х выявленных требованиях. (Примечание: легко изменить)

Во-первых, нам нужно разбить имя файла на его части:
Class FileType
    Public Property ID As String
    Public Property Name As String
    Public Property Revision As String
    Public Property [Date] As String
    Public Property Pages As String
End Class

Используя RevisionComparer выше мы теперь можем сортировать имена файлов:
Module Module1

    Sub Main()

        ' test data
        Dim files = New List(Of String)() From {
            "0092933_C_1-2_061117.pdf",
            "0092933_Z_1-2_120617.pdf",
            "0092933_AZ_1-2_120617.pdf",
            "1092933_AB_1-2_061117.pdf",
            "1092933_Z_1-2_120617.pdf",
            "1092933_CZ_1-2_120617.pdf"
        }

        Dim data As New List(Of FileType)()

        For Each file In files

            ' break up the filename
            Dim parts = IO.Path.GetFileNameWithoutExtension(file).Split(New Char() {"_"c}, StringSplitOptions.RemoveEmptyEntries)

            ' valid number of parts
            If parts.Length = 4 Then

                Dim fileDate = Nothing
                DateTime.TryParse(parts(2), fileDate)

                data.Add(New FileType() With {
                    .ID = parts(0),
                    .Revision = parts(1),
                    .[Date] = parts(2),
                    .Pages = parts(3),
                    .Name = file
                })

            End If
        Next

        ' sort the files by: date desc, then revision desc...
        Dim sorted = data.OrderByDescending(Function(x) x.[Date]) _
                        .ThenByDescending(Function(x) x.Revision, New RevisionComparer()) _
                        .GroupBy(Function(x) x.ID) _
                        .Select(Function(x) x.First())

        ' report the results
        For Each item In sorted
            Console.WriteLine(item.Name)
        Next

        Console.WriteLine("-- DONE --")
        Console.ReadKey()

    End Sub

End Module

Который выводит:
1092933_CZ_1-2_120617.pdf
0092933_AZ_1-2_120617.pdf
-- DONE --


ОБНОВЛЕНИЕ: Вы просите "решение только для Linq", и ответ-да, вы можете избежать дополнительных классов и сделать это одним очень длинным запросом Linq, но тогда все сводится к удобочитаемости и ремонтопригодности. Вышеприведенное решение ставит галочки в обоих этих ящиках.

Однако вы запросили однострочный запрос Linq, так что вот он, используя вышеприведенный компаратор из-за требования пользовательской сортировки. Все используемые части находятся в приведенном выше более красноречивом решении:
Dim sortedV2 = files.Select(Function(x) IO.Path.GetFileNameWithoutExtension(x).Split(New Char() {"_"c}, StringSplitOptions.RemoveEmptyEntries)) _
                    .OrderByDescending(Function(x) x(2)) _
                    .ThenByDescending(Function(x) x(1), New RevisionComparer()) _
                    .GroupBy(Function(x) x(0)) _
                    .Select(Function(x) x.First()) _
                    .Select(Function(x) String.Join("_", x) + ".pdf")

' report the results
For Each item In sortedV2
    Console.WriteLine(item)
Next

Примечание: проблема с этим однострочным решением Linq заключается в том, что если у вас есть неправильно названный файл, оператор Linq вызовет исключение, однако вышеупомянутое "оригинальное" решение этого не сделает.


CPallini

5.

Graeme_Grant

Спасибо. :)