按升序对Fileinfo列表进行排序

时间:2019-06-30 03:27:17

标签: arrays vb.net sorting

我有以下文件列表

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)对数组进行排序,但我需要将其放在一个排序的数组或列表中

我在这里尝试了多个线程,包括此method。使用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 个答案:

答案 0 :(得分:1)

您将必须编写代码来解析名称的各个部分,并将其视为数字。您使用的任何内置比较器都将字符串视为字符串,以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()

为了娱乐,这是一个正则表达式版本:

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)

它特别引人注意。这里有 ton 多余的工作正在进行,

  • d.Name已经是一个字符串,无需调用ToString()
  • 由于只需要一个字符就可以通过下标访问它,因此无需调用Substring()
  • 您要比较一个字符,无需进行字符串比较。

您真的想要这个:

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

这是一个很多的代码,它的性能会好得多,值得您花时间研究一下并理解原因。