views:

679

answers:

7

I am using the following piece of C code to change file extension.

{

 #define EVAL_MAX_LEN (300)

 int nLen;
 char szOut [EVAL_MAX_LEN] = {0};
 char szPath [EVAL_MAX_LEN] = "/db/file/face.bmp";

 // Get string length !!!
 nLen = strlen (szPath);

 if ((nLen > 0) && (nLen < EVAL_MAX_LEN)) {

      while (nLen) {

           // Check for extension character !!!
           if (szPath [nLen] == '.') {

                szPath [nLen] = '\0';
                break;
           }

           nLen --;

      }//while (nLen ...

 // Create output file name and with new extension
 sprintf (szOut, "%s.txt", szPath);

 }// if ((nLen > 0) && (nLen < EVAL_MAX_LEN ...

}

Any suggestion for a better and more elegant code is welcome. I also know that in Windows we can use _splitpath to do this in an elegant manner. But right now, I am using this code in Linux.

A: 

There can be more than one period. You should use PathFindExtension() and PathRenameExtension().

jeffamaphone
Those are Windows-only functions, OP asked for Linux.
Adam Rosenfield
+1  A: 

I would consider using strnlen() and base EVAL_MAX_LEN from the system's PATH_MAX (or whatever is applicable) limit (check its return value prior to proceeding).

That will avoid even trying to write questionable content to szOut, or having to check for the # of characters not printed to szOut if the string is too big (though I'd still suggest favoring snprintf() over sprintf()).

Tim Post
+3  A: 

You should use snprintf() instead of sprintf() to avoid a potential buffer overflow. You can also use strrchr(3) to find the last period, instead of coding your own loop. However, two things to be careful of:

  1. What if the input file has no extension, i.e. there is no dot in the path name?
  2. What if the input file has no extension, but one of the folders in the path name has a dot in it? For example: "/home/joeuser/dotted.folder/thefile"

Make sure you handle both of those cases appropriately.

Adam Rosenfield
+1  A: 

How about this one?

#define EVAL_MAX_LEN (300)
int nLen;
char szOut [EVAL_MAX_LEN] = {0};
char szPath [EVAL_MAX_LEN] = "/db/file/face.bmp";
char *res = NULL;
// Get string length !!!
nLen = sizeof (szPath);
memset(szOut,0,sizeof(szOut));
if ((nLen > 0) && (nLen <= EVAL_MAX_LEN)) {
    res = strrchr( szPath, '.' );
    if(res != NULL)
        *res = '\0';
}
// Create output file name and with new extension
sprintf (szOut, "%s.txt", szPath);
// if ((nLen
eaanon01
+2  A: 
// find file name
char *pFile = strrchr(szPath, '/');
pFile = pFile == NULL ? szPath : pFile+1;
// change extension
char *pExt = strrchr(pFile, '.');
if (pExt != NULL)
    strcpy(pExt, ".txt");
else
    strcat(pFile, ".txt");
Ferruccio
I'm [insert politician you like here], and I approve this code. Although it would be nice to specify that '\\' is for Windows, and for *nix you would use '/'. Especially since the OP appears to be using *nix.
Chris Lutz
Ferruccio
+1  A: 

You need to use dirname and basename to split the directory and filename and then split filename to get last "." and then merge dirname and '/' and basename to get renamed extension.

Pseduodcode where result is the output..

 char *dirc, *basec, *bname, *dname,*result;
 char *path = "/etc/passwd";
 char *extn = NULL;
  dirc = strdup(path);
  basec = strdup(path);
  dname = dirname(dirc);
  bname = basename(basec);
  extn = strrchr( bname, '.' );
  if(extn != NULL)  *extn = '\0';
  sprintf(result, "%s/%s.txt"", dname, bname);
  free(dirc);
  free(basec);
lakshmanaraj
Note that strdup(), for all its greatness (and believe me, I love it), is NOT a standard ANSI C89 or ISO C99 function. It is *nix (or *BSD) specific. It's fairly easy to write your own strdup() if you don't have one, but just so we're clear, it's not a standard C function.
Chris Lutz
I have clearly mentioned that only written a pseduocode, It is up to the author to write equivalent functions. Only concept needs to be considered from above code.
lakshmanaraj
+2  A: 

How about writing some test cases? If the above is a subroutine named change_ext_to_txt(char* szPath), you'll want to test:

change_ext_to_txt("/foo/bar.baz"); 
change_ext_to_txt("/foo/bar"); 
change_ext_to_txt("/foo/bar.baz/bal.bat"); 
change_ext_to_txt("/foo/bar.baz/bal");

and make sure the right thing comes out of all of them. Hint: all the solutions presented so far (including yours!) fail on at least one of the above.

pjz
Please see My code will not fail.
lakshmanaraj
It will fail if you hand it "/foo/bar.baz/bal" ; it will return "/foo/bar.txt", which is incorrect. But don't take my word for it; try it.
pjz