-2

I am trying to sort the following files in this order:

TMP_SDF_1180741.PDF
TMP_SDF_1179715.PDF
TMP_SDF_1162371.PDF
TMP_SDF_1141511.PDF
TMP_SDF_1131750.PDF
TMP_SDF_1117362.PDF
TMP_SDF_1104199.PDF
TMP_SDF_1082698.PDF
TMP_SDF_1062921.PDF
TMP_SDF_1043875.PDF
TMP_SDF_991514.PDF
TMP_SDF_970621.PDF
TMP_SDF_963154.PDF
TMP_SDF_952954.PDF
TMP_SDF_948067.PDF
TMP_SDF_917669.PDF
TMP_SDF_904315.PDF
TMP_SDF_899902.PDF
TMP_SDF_892398.PDF
TMP_SDF_882024.PDF

But the actual output is this:

TMP_SDF_991514.PDF
TMP_SDF_970621.PDF
TMP_SDF_963154.PDF
TMP_SDF_952954.PDF
TMP_SDF_948067.PDF
TMP_SDF_917669.PDF
TMP_SDF_904315.PDF
TMP_SDF_899902.PDF
TMP_SDF_892398.PDF
TMP_SDF_882024.PDF
TMP_SDF_1180741.PDF
TMP_SDF_1179715.PDF
TMP_SDF_1162371.PDF
TMP_SDF_1141511.PDF
TMP_SDF_1131750.PDF
TMP_SDF_1117362.PDF
TMP_SDF_1104199.PDF
TMP_SDF_1082698.PDF
TMP_SDF_1062921.PDF
TMP_SDF_1043875.PDF

I have tried researching sort methods by GetFiles but when I apply them, i get errors about system collections not able to bind to a 1-dimensional array and it is frustrating. Here is my code:

Dim di As New IO.DirectoryInfo("C:\temp")
        Dim aryFi As IO.FileInfo() = di.GetFiles("*.PDF")
        Dim fi As IO.FileInfo       
        For Each fi In aryFi
            My.Computer.FileSystem.RenameFile("C:\TEMP\" & fi.Name, listBox1.SelectedItem.ToString & ".pdf")
            listBox1.SelectedIndex = listBox1.SelectedIndex - 1
        Next

I am renaming files to be a1 a2 a3 etc so that when I combine in PDF, they are in chronological order. The way i want the sorting, will place them in chronological order. I am sure there is an easier way. As you can tell, the higher the number in the PDF file (1180741) the most recent date of the content of the file. While 882024 would be the oldest file content.

OneFineDay
  • 9,004
  • 3
  • 26
  • 37
  • It is doing an alpha sort. It doesnt matter than the *numerals* represent a date or sequence *to you*, it is just text to the computer such that "9" will sort higher than "10734756". – Ňɏssa Pøngjǣrdenlarp May 01 '15 at 15:06
  • I'm not 100% clear on what you're trying to do. If you want to get a list of your file names in alphabetical order, put the file names into an array list of type string, and use the sort method. If you want to sort it in the opposite direction, use the reverse method after the sort method. – ChicagoMike May 01 '15 at 15:06
  • Plutonix: I honestly do understand that. The problem is, if i sort by the "Name" column in the actual directory, it sorts how I am requesting. Why it does not default to that in VB is confusing. – user3753642 May 01 '15 at 15:07
  • That last idea I posted doesn't work. I'm an idiot. Too early in the morning still for debugging. – ChicagoMike May 01 '15 at 15:13
  • Because Explorer is not a ListBox and it is written differently than your code. There *is* a way, you need to write come code to do it (like Explorer most likely did) – Ňɏssa Pøngjǣrdenlarp May 01 '15 at 15:13
  • ChicagoMike: I was originally dumping the fi.Names to a sorted listbox but that was also giving the incorrect output. Again, I understand the logic in the sorting but windows sorts correctly so i know there has to be a way. – user3753642 May 01 '15 at 15:13
  • I came here for help. I honestly expected more than pointing out the obvious. As I stated in my post, I know that windows sorts differently. I am asking how to do that. Google results upon Google results show many ways of sorting but none of them sound right. Which is why i came here and gave distinct information on what i need. I don't need anyone to write the sort method for me, i just need a push in the appropriate direction. – user3753642 May 01 '15 at 15:18
  • 1
    To expand on Plutonix's comment, Explorer will sort on numbers differently because it assumes that you want them to be considered numbers, not text. – Tony Hinkle May 01 '15 at 15:19
  • Thank you Tony. That sounds about right. Do you know what the sort method would be called? Alphanumeric? – user3753642 May 01 '15 at 15:22
  • 1
    There are many different ways to accomplish this. Perhaps the easiest to explain is if the non-numeric parts of the filenames will always be the same, strip off the letters, put them into an array of integers, sort them, and then in the output add the letters back. – Tony Hinkle May 01 '15 at 15:22
  • SO isnt meant for pushes, hints, guidance, suggestions or ideas. Ask a concrete question, get a concrete answer. See [Ask]. To that end there is nothing there attempting to convert your names to values to get the desired sort - we'd just be doing your work for you. Also note that this is not a unique question - even within the last hour. That said, put the names in a list, perhaps as a List(of Name-Value) which is a class which contains the name and the *numeric* value, Sort the list Up or Down and set the listbox datasource to that list. – Ňɏssa Pøngjǣrdenlarp May 01 '15 at 15:23
  • No--if Explorer is using it, I doubt that it's .NET. I don't know of any functions that would sort like this, but there's probably something out there someone has written and shared--will just take a lot of searching and asking. – Tony Hinkle May 01 '15 at 15:23
  • Well stated Plutonix. In that case, can you please concretely write me the exact function to sort the above list how I Windows sorts it? – user3753642 May 01 '15 at 15:26
  • 1
    See http://stackoverflow.com/questions/4788227/sorting-a-list-of-strings-numerically-1-2-9-10-instead-of-1-10-2 – Tony Hinkle May 01 '15 at 15:31

2 Answers2

2

As has been stated in the comments, you need to sort them numerically rather than alphabetically. I don't know the specific sorting algorithm that is used by Windows Explorer, or if it's possible to use the same library, but it's certainly possible to write your own algorithm that sorts however you want.

The first step in doing that is to extract just the numeric part that you want to use as the sort key. Without knowing more details, it's hard to say what the best option for that would be. If you know that the number always starts at a particular character position in the string, you could simply use String.SubString. If it's always delimited by "_" and "." you could use String.Split. If you need something more complex, or if you need the parsing rules to be configurable, you may want to consider using RegEx. As an example, here's a simple example method that uses String.Split:

Public Function GetSortKey(fileName As String) As Integer
    Return Integer.Parse(fileName.Split({"_"c, "."c})(2))
End Function

Once you have a method that extracts the sort key for a given file name, you can use it to sort them like this:

di.GetFiles("*.PDF").OrderBy(Function(x) GetSortKey(x.Name))
Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
  • I have a feeling this will work. Thank you for the hard work. But I have the error of: Cannot be converted to a 1-dimensional array on this line: Dim aryFi As IO.FileInfo() = di.GetFiles("*.PDF").OrderBy(Function(x) GetSortKey(x.Name)) -- Please note, i am using a for loop right after this to rename each file in the array. – user3753642 May 01 '15 at 15:48
  • 1
    The `OrderBy` LINQ extension method does not return an array. It returns an `IEnumerable(Of T)` (in this case, `IEnumerable(Of FileInfo)`). So, either change `aryFi` to `IEnumerable(Of FileInfo)` or else convert the return value to an array using LINQ's `ToArray` function (e.g. `...OrderBy(...).ToArray()`). – Steven Doggart May 01 '15 at 15:53
  • Adding .ToArray alleviated that error but now i have the wrong input string for the parse method. an actual file name looks like this: TMP38667016-R_prod_1180741-095621.PDF -- So i changed the parse method to: (fileName.Split({"_"c, "-"c})(2)) -- but perhaps i don't understand what the parse is doing. I tried to logically analyze and see that it is splitting at the 2nd instance of _ perhaps I am wrong. I only need to sort the integer value of 1180741 i dont care about 095621 – user3753642 May 01 '15 at 15:59
  • In that example file name (TMP38667016-R_prod_1180741-095621.PDF), which part do you want to use as the sort key? – Steven Doggart May 01 '15 at 16:00
  • 1180741 should be the sort key. so anything between _ and - – user3753642 May 01 '15 at 16:02
  • The `Split` method will split the string into an array dividing it at any of the given delimiters, so if you give it "_" and "-" as delimiters, it will return `{"TMP38667016", "R", "prod", "1180741", "095621.PDF"}`. As such, you would want the fourth element, which is index 3, since it's zero-based. – Steven Doggart May 01 '15 at 16:05
  • You are an extremely talented programmer. Thank you for your time Sir! – user3753642 May 01 '15 at 16:16
0

Perhaps you could take advantage of some tools that you have at your hands

    Dim reg As RegEx = new RegEx("\d+")
    Dim ordered = new List(Of OrderedFiles)()
    for each s in Directory.GetFiles("C:\temp", "*.PDF")
        Dim aFile = new OrderedFiles() 
        aFile.FileName = s
        aFile.Sequence = Convert.ToInt32(reg.Match(s).Value)
        ordered.Add(aFile)
    Next

    for each aFile in ordered.OrderByDescending(Function(x) x.Sequence)
        Console.WriteLine(Path.GetFileName(aFile.FileName))
    Next
End Sub


Class OrderedFiles
    Public FileName as String
    Public Sequence as Integer
End Class

In this example you have a custom class with the filename and the numeric part that you want to sort. Then a Regex expression that matches any numeric value in your files is applied to your files to build a instance of the class with the name and the numeric part. At the end of the loop just call the Linq method that orders your list by descending order

Steve
  • 213,761
  • 22
  • 232
  • 286