3

Here is my code:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int cmp(const void *a, const void *b) {
    const char **ia = (const char **)a;
    const char **ib = (const char **)b;
    return strcmp(*ia, *ib);
}

void print_array(char **array, size_t len) {
    size_t i;
    for(i=0; i<len; i++) {
        printf("%s, ", array[i]);
    }
    putchar('\n');
}

int main(int argc, char *argv[]) {

        char *strings[] = { "z1.doc", "z100.doc",  "z2.doc", "z3.doc", "z20.doc"};
        size_t strings_len = sizeof(strings) / sizeof(char *);
        print_array(strings, strings_len);
        qsort(strings, strings_len, sizeof(char *), cmp);
        print_array(strings, strings_len);

        system("PAUSE");
        return 1;
}

the actual output is

z1.doc, z100.doc, z2.doc, z20.doc, z3.doc

and I want it to be

z1.doc, z2.doc, z3.doc, z20.doc, z100.doc

What am doing wrong?

7
  • Yikes! So many stars! const char *ia = a; /* ... */ return strcmp(ia, ib); Commented Nov 5, 2009 at 0:22
  • Came across this at mail-archive.com/[email protected]/…: Explorer uses the StrCmpLogicalW() function in shlwapi.dll to sort filenames into "logical" order. The exact sort order is not guaranteed and may change with each Windows release or service pack! Commented Nov 5, 2009 at 0:26
  • 1
    @pmg: That's incorrect. Doing it as you suggest will surely result in segmentation fault. The alternative variant would be const char *ia = *(const char **) a;...; return strcmp(ia, ib);, but absolutely not what you suggest. Commented Nov 5, 2009 at 0:28
  • @AndreyT: it works for me :) [ codepad.org/zcFJ4o44 ] Commented Nov 5, 2009 at 0:37
  • 1
    @pmg: No, it doesn't. You simply failed to test it properly. You are re-interpreting pointer values as C-strings and comparing the resulting "garbage" strings. If it didn't crash for you, it is simply because you got lucky. Run it and take a look at your "sorted" array for a laugh. Then think about what you done. Again, your version makes no sense whatsoever. Commented Nov 5, 2009 at 0:41

3 Answers 3

4

The actual output is correct, the string "z100.doc" is less than "z2.doc". The strcmp compares character by character and when it gets to the '1' that is less than '2' and it stops there, so z100 < z2.

If you name the files z001.doc, z002.doc, z003.doc, z020.doc, z100.doc it will sort the way you want.

Sign up to request clarification or add additional context in comments.

2 Comments

-1 Renaming the files is not the answer to this question. The OP wants to do work for the user, not make the user do (unnecessary) work to get his/her files displayed properly. Anyone who upvotes this doesn't understand the concept of "interface usability" - no one other than us programmers expects lists to be sorted in the order that the standard strcmp() sorts in.
The question was "Why is the sorted output I get not the sorted output I expect, and how can I fix that?"
3

Change your comparator to say:

return (atoi(*ib + 1) - atoi(*ia + 1));

2 Comments

that will only work if there is only 1 number in the filename and all the characters are identical. it won't work properly for "z1aa.doc", "z1zz.doc", "z1bb.doc". it wont' work for "z10_10.doc" and "z10_5.doc", etc.
return (atoi(*ia + 1) - atoi(*ib + 1));
1

You want a natural sorting algorithm as opposed to the ASCIIbetical sorting algorithm provided by default. See Jeff's blog entry for a long rant about this, and some nice links to implementations of this natural algorithm. Just as a warning, the correct implementation (as opposed to the hacky answer you've accepted) is quite complicated, so don't try to implement it yourself, take someone else's (hopefully public domain or liberally licensed) implementation and use that instead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.