views:

480

answers:

7

I want to throw the last three character from file name and get the rest?

I have this code:

char* remove(char* mystr) {

    char tmp[] = {0};
    unsigned int x;

    for (x = 0; x < (strlen(mystr) - 3); x++)
        tmp[x] = mystr[x];

    return tmp;
}
+6  A: 

Use rindex to locate the "." character. If the string is writable, you can replace it with the string terminator char ('\0') and you're done.

char * rindex(const char *s, int c);

 DESCRIPTION
 The rindex() function locates the last character matching c (converted to a char) in the null-terminated string s.
diciu
This maps "some/path.name/readme" to "some/path", which is wrong. It might be that this input is impossible, but I think it's worth stating the assumptions.
Steve Jessop
Additionally, you may wish to consider changing the function name. The routine remove() is generally used for removing/deleting a file from the directory.
Sparky
It's worth noting that rindex is not ISO-C: strrchr may be a better option.
paxdiablo
+3  A: 

I would try the following algorithm:

last_dot = -1

for each char in str:
    if char = '.':
        last_dot = index(char)

if last_dot != -1:
    str[last_dot] = '\0'
Bastien Léonard
A: 

Just replace the dot with "0". If you know that your extension is always 3 characters long you can just do:

char file[] = "test.png";
file[strlen(file) - 4] = 0;
puts(file);

This will output "test". Also, you shouldn't return a pointer to a local variable. The compiler will also warn you about this.

WakiMiko
A: 

This should do the job:

char* remove(char* oldstr) {
   int oldlen = 0;
   while(oldstr[oldlen] != NULL){
      ++oldlen;
   }
   int newlen = oldlen - 1;
   while(newlen > 0 && mystr[newlen] != '.'){
      --newlen;
   }
   if (newlen == 0) {
      newlen = oldlen;
   }
   char* newstr = new char[newlen];
   for (int i = 0; i < newlen; ++i){
      newstr[i] = oldstr[i];
   }
   return newstr;
}
Viktor Sehr
+2  A: 

If you literally just want to remove the last three characters, because you somehow know that your filename has an extension exactly three chars long (and you want to keep the dot):

char *remove_three(const char *filename) {
    size_t len = strlen(filename);
    char *newfilename = malloc(len-2);
    if (!newfilename) /* handle error */;
    memcpy(newfilename, filename, len-3);
    newfilename[len - 3] = 0;
    return newfilename;
}

Or let the caller provide the destination buffer (which they must ensure is long enough):

char *remove_three(char *dst, const char *filename) {
    size_t len = strlen(filename);
    memcpy(dst, filename, len-3);
    dst[len - 3] = 0;
    return dst;
}

If you want to generically remove a file extension, that's harder, and should normally use whatever filename-handling routines your platform provides (basename on POSIX, _wsplitpath_s on Windows) if there's any chance that you're dealing with a path rather than just the final part of the filename:

/* warning: may modify filename. To avoid this, take a copy first
   dst may need to be longer than filename, for example currently
   "file.txt" -> "./file.txt". For this reason it would be safer to
   pass in a length with dst, and/or allow dst to be NULL in which
   case return the length required */
void remove_extn(char *dst, char *filename) {
    strcpy(dst, dirname(filename));
    size_t len = strlen(dst);

    dst[len] = '/';
    dst += len+1;

    strcpy(dst, basename(filename));
    char *dot = strrchr(dst, '.');
    /* retain the '.' To remove it do dot[0] = 0 */
    if (dot) dot[1] = 0;
}

Come to think of it, you might want to pass dst+1 rather than dst to strrchr, since a filename starting with a dot maybe shouldn't be truncated to just ".". Depends what it's for.

Steve Jessop
+1 - Much better than my answer.
Tim Post
A: 

Try:

char *remove(char* mystr) {
    char *retstr;
    char *lastdot;
    if (mystr == NULL)
         return NULL;
    if ((retstr = malloc (strlen (mystr) + 1)) == NULL)
        return NULL;
    strcpy (retstr, mystr);
    char *lastdot = strrchr (retstr, '.');
    if (lastdot != NULL)
        *lastdot = '\0';
    return retstr;
}

You'll have to free the returned string yourself. It simply finds the last . in the string and replaces it with a null terminator character. It will handle errors (passing NULL or running out of memory) by returning NULL.

It won't work with things like /this.path/is_bad since it will find the . in the non-file portion but you could handle this by also doing a strrchr of /, or whatever your path separator is, and ensuring it's position is NULL or before the . position.


A more general purpose solution to this problem could be:

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

// remove_ext: removes the "extension" from a file spec.
//   mystr is the string to process.
//   dot is the extension separator.
//   sep is the path separator (0 means to ignore).
// Returns an allocated string identical to the original but
//   with the extension removed. It must be freed when you're
//   finished with it.
// If you pass in NULL or the new string can't be allocated,
//   it returns NULL.

char *remove_ext (char* mystr, char dot, char sep) {
    char *retstr, *lastdot, *lastsep;

    // Error checks and allocate string.

    if (mystr == NULL)
        return NULL;
    if ((retstr = malloc (strlen (mystr) + 1)) == NULL)
        return NULL;

    // Make a copy and find the relevant characters.

    strcpy (retstr, mystr);
    lastdot = strrchr (retstr, dot);
    lastsep = (sep == 0) ? NULL : strrchr (retstr, sep);

    // If it has an extension separator.

    if (lastdot != NULL) {
        // and it's before the extenstion separator.

        if (lastsep != NULL) {
            if (lastsep < lastdot) {
                // then remove it.

                *lastdot = '\0';
            }
        } else {
            // Has extension separator with no path separator.

            *lastdot = '\0';
        }
    }

    // Return the modified string.

    return retstr;
}

int main (int c, char *v[]) {
    char *s;
    printf ("[%s]\n", (s = remove_ext ("hello", '.', '/'))); free (s);
    printf ("[%s]\n", (s = remove_ext ("hello.", '.', '/'))); free (s);
    printf ("[%s]\n", (s = remove_ext ("hello.txt", '.', '/'))); free (s);
    printf ("[%s]\n", (s = remove_ext ("hello.txt.txt", '.', '/'))); free (s);
    printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', '/'))); free (s);
    printf ("[%s]\n", (s = remove_ext ("/has.dot/in.path", '.', '/'))); free (s);
    printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', 0))); free (s);

    return 0;
}

and this produces:

[hello]
[hello]
[hello]
[hello.txt]
[/no.dot/in_path]
[/has.dot/in]
[/no]
paxdiablo
Excellent, great work. thanks.
alaamh
if i want only the file name without extension and with out the path?thanks
alaamh
A: 

To get paxdiablo's second more general purpose solution to work in a C++ compiler I changed this line:

if ((retstr = malloc (strlen (mystr) + 1)) == NULL)

to:

if ((retstr = static_cast(malloc (strlen (mystr) + 1))) == NULL)

Hope this helps someone.

Rory Milne