Search code examples
phparraysstringsortingfilenames

How to naturally sort a flat array of filenames (some ending with numbers, e.g. "-2")?


I have an PHP array consisting of filenames, like:

black.png
blue.png
green.png
orange.png
orange-2.png
red.png
red-2.png
yellow.png
yellow-2.png

This is also how the list is sorted in Windows Explorer (and how it should look like in the end). When I read the directory in PHP and output the list using sort() I get following:

black.png
blue.png
green.png
orange-2.png
orange.png
red-2.png
red.png
yellow-2.png
yellow.png

Filenames ending with "-2" are shown first - what I would like to avoid. I've already tried using natsort() with the same result.


Solution

  • The hyphen character - is ASCII character 2D, and comes ahead of all numbers and letters in the character set. More significantly, it also comes immediately before the full stop . character, which is 2E.

    This is why they're being sorted first: because sort() sees the characters yellow as being the same, and then compares - with . and puts - first.

    If all your characters have the same file extension, you can resolve this by simply sorting the files on the base filename -- ie remove the .png from the end of all of them. This will mean that yellow gets sorted ahead of yellow-2. You can replace the extensions after sorting if you need to.

    Another option would be to use a different character for the hyphen. The underscore character is located toward the end of the ASCII table, and will thus be sorted the way you want, even if you keep the file extensions in place.

    natsort() doesn't work (as you've noted in the question) because the hyphen is seen as a negative. So yellow.png is treated as item zero, and yellow-2.png is treated as item negative two, and is therefore sorted first. Again, using underscore rather than hyphen could help here. Or you could simply not use a separator character at all.

    If you absolutely don't want to change the file names, your final option is to write the sort yourself using usort(). This will allow you to write the function that compares items to decide which is sorted higher or lower. In this case, you could for example still replace the hyphens with underscores and then do the sort, but only within the context of the usort() function; the string replace would only affect the local variables in that fucntion, so your actual filenames in your main code would remain untouched.