Search code examples
cstringfilenames

Extract the file name and its extension in C


So we have a path string /home/user/music/thomas.mp3.

Where is the easy way to extract file name(without extension, "thomas") and it's extension ("mp3") from this string? A function for filename, and for extension. And only GNU libc in our hands.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FILENAME_SIZE 256

char *filename(char *str) {
    char *result;
    char *last;
    if ((last = strrchr(str, '.')) != NULL ) {
        if ((*last == '.') && (last == str))
            return str;
        else {
            result = (char*) malloc(MAX_FILENAME_SIZE);
            snprintf(result, sizeof result, "%.*s", (int)(last - str), str);
            return result;
        }
    } else {
        return str;
    }
}

char *extname(char *str) {
    char *result;
    char *last;
    if ((last = strrchr(str, '.')) != NULL) {
        if ((*last == '.') && (last == str))
            return "";
        else {
            result = (char*) malloc(MAX_FILENAME_SIZE);
            snprintf(result, sizeof result, "%s", last + 1);
            return result;
        }
    } else {
        return ""; // Empty/NULL string
    }
}

Solution

  • Regarding your actual code (all the other answers so far say to scrap that and do something else, which is good advice, however I am addressing your code as it contains blunders that it'd be good to learn about in advance of next time you try to write something).

    Firstly:

    strncpy(str, result, (size_t) (last-str) + 1);
    

    is not good. You have dest and src around the wrong way; and further this function does not null-terminate the output (unless the input is short enough, which it isn't). Generally speaking strncpy is almost never a good solution to a problem; either strcpy if you know the length, or snprintf.

    Simpler and less error-prone would be:

    snprintf(result, sizeof result, "%.*s", (int)(last - str), str);
    

    Similary in the other function,

    snprintf(result, sizeof result, "%s", last + 1);
    

    The snprintf function never overflows buffer and always produces a null-terminated string, so long as you get the buffer length right!

    Now, even if you fixed those then you have another fundamental problem in that you are returning a pointer to a buffer that is destroyed when the function returns. You could fix ext by just returning last + 1, since that is null-terminated anyway. But for filename you have the usual set of options:

    • return a pointer and a length, and treat it as a length-counted string, not a null-terminated one
    • return pointer to mallocated memory
    • return pointer to static buffer
    • expect the caller to pass in a buffer and a buffer length, which you just write into

    Finally, returning NULL on failure is probably a bad idea; if there is no . then return the whole string for filename, and an empty string for ext. Then the calling code does not have to contort itself with checks for NULL.