TransWikia.com

Trying to remove suffix from string with no success

Stack Overflow Asked by user273283 on December 5, 2020

I am trying to remove suffix in next way, but eventually the output is the same as input


    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define SUFFIX ".doc"
    
    static void removeSuffix(char **outNewFileName, const char *inFileName)
    {
        *outNewFileName = strdup(inFileName);
        outNewFileName[strlen(inFileName) - strlen(SUFFIX)];
    }

    int main ()
    {
        char *fileName = "tmp.doc";
        char *outnewFileName  = NULL;
        
        removeSuffix(&outnewFileName, fileName);

        free(outnewFileName);
    
        return 0;
    }

for example if fileName is tmp.doc outnewFileName is also tmp.doc

4 Answers

You are very close! Your strlen(inFileName) - strlen(SUFFIX) expression finds the correct place at which to terminate the new string, but you don't actually do anything with that expression.

To terminate the string at that position, set the value of the char there to zero (which is the nul terminator):

static void removeSuffix(char** outNewFileName, const char* inFileName)
{
    *outNewFileName = strdup(inFileName);
    (*outNewFileName)[strlen(inFileName) - strlen(SUFFIX)] = '';
}

In this code, we use (*outNewFileName) to refer to the new string array, and the offset from that in the square brackets to refer to the actual character that needs to be changed to nul. (The round brackets are required, because the [] operator has higher precedence that the * operator.)


Note: As pointed out in the comments, if you had full compiler warnings enabled, you would have seen this (from clang-cl):

warning : expression result unused [-Wunused-value]

Feel free to ask for any further clarification and/or explanation.

Correct answer by Adrian Mole on December 5, 2020

For starters always try to write a more generic function.

This function declaration

static void removeSuffix(char **outNewFileName, const char *inFileName);

is bad. It relays on the magic string literal ".doc". So you may not use the function to remove other suffixes.

Secondly passing the first argument (pointer) by reference arises a question should the pointer be freed before assigning to it a new address or not.

The function definition also does not make sense

static void removeSuffix(char **outNewFileName, const char *inFileName)
{
    *outNewFileName = strdup(inFileName);
    outNewFileName[strlen(inFileName) - strlen(SUFFIX)];
}

It seems you mean

static void removeSuffix(char **outNewFileName, const char *inFileName)
{
    *outNewFileName = strdup(inFileName);
    ( *outNewFileName )[strlen(inFileName) - strlen(SUFFIX)] = '';
}

But this approach is incorrect. The passed string can have no suffix that have to be removed. In this case the function will return an invalid string. Or the passed string can contain the same combination as the suffix more than one time. And moreover the user can pass a string the length of which is less than the length of the suffix.

So this function declaration and its definition in whole are wrong and bad.

The function can be declared and defined the following way as it is shown in the demonstrative program below.

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

char * removeSuffix( const char *s1, const char *s2 )
{
    char *p = NULL;
    
    if ( *s2 != '' )
    {
        for ( char *current = ( char * )s1; ( current = strstr( current, s2 ) ) != NULL; ++current )
        {
            p = current;
        }
    }       
    
    if ( p == NULL || *( p + strlen( s2 ) ) != '' ) p = ( char * )( s1 + strlen( s1 ) );
    
    size_t n = p - s1;
        
    p = malloc( n + 1 );
        
    if ( p )
    {
        memcpy( p, s1, n );
        p[n] = '';
    }

    return p;
}

int main(void) 
{
    char *fileName = "tmp.doc.doc";
    char *suffix = ".doc";
    
    char *outnewFileName  = removeSuffix( fileName, suffix );
    
    if ( outnewFileName ) puts( outnewFileName );
    
    free( outnewFileName );
    
    return 0;
}

The program output is

tmp.doc

As you can see the function indeed removed the suffix from the string "tmp.doc.doc" that contains two combinations of the symbols ".doc".

Answered by Vlad from Moscow on December 5, 2020

I would rather:

char* strrrstr(const char *haystack, const char *needle)
{
    const char *r = NULL;
    size_t nlen = strlen(needle);

    if(haystack && needle)
    {
        while (1) 
        {
            char *p = strstr(haystack, needle);
            if (!p)
                break;
            r = p;
            haystack = p + nlen;
        }
    }
    return (char *)r;
}


#define SUFFIX ".doc"

static char *removeSuffix(const char *str, const char *suffix)
{
    char *newString = strdup(str);
    char *pos;
    if(newString)
    {
        pos = strrrstr(newString, suffix);
        if(pos) *pos = 0;
    }
    return newString;
}

int main ()
{
    static char *fileName = "tmp.doctmp.doc";
    char *outnewFileName;
    
    outnewFileName = removeSuffix(fileName, SUFFIX);

    printf("%sn", outnewFileName);

    free(outnewFileName);

    return 0;
}

You should always read the warnings and set the warning level to maximum possible. If you do then:

<source>:36:23: warning: statement with no effect [-Wunused-value]

   36 |         outNewFileName[strlen(inFileName) - strlen(SUFFIX)];

      |         ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

explains the problem.

Answered by P__J supports women in Poland on December 5, 2020

"...remove suffix from string with no success"

In the following:

outNewFileName[strlen(inFileName) - strlen(SUFFIX)];

the variable that is being modified needs to be expressed as (*outNewFileName), so change above to:

(*outNewFileName)[strlen(inFileName) - strlen(SUFFIX)];

Also, in this method, doing the math to arrive at the array index is possible, but can be difficult to do correctly, and is not the only way to snip off a file extension from a string. The following is is alternative method that, for example, will not require using a #define value, and requires no array index calculations. (But does require the . file extension delimiter)...

char *new = strdup(filename);//after getting a new string at least as long as original...
if(new)//use strtok
{
    char *tok - strtok((*outNewFileName), ".");
    if(tok)
    {
        strcpy((*outNewFileName), tok);//tok contains string w/o extension.
        ...

Edit to address comments:

Another method, more flexible than the previous is to create an ability to specify which delimiter to identify the file extension, and pass it as an argument to return the portion of the string up to and including the last occurrence of that delimiter. For example:

strcpy((*outNewFileName), return_base_filename((*outNewFileName), '.'));

Where return_base_filename() is defined as:

//returns string upto last occurance of delimiter.
char * return_base_filename(char *in, char delimiter)
{
    char *ptr = in;
    char *ptrKeep = NULL;
    
    while(*ptr)
    {
        if(*ptr == delimiter) 
        {
            ptrKeep = ptr;
        }
        ptr++;
    }
    in[ptrKeep - in]=0;
    return in;
}

Note that this method works for the following filename/directory forms:

  • "base.ext"
  • "base1.base2.base3.ext"
  • "C:\one\two\three\with.this.file.txt"

The 3rd requires 2 calls to 'return_base_filename(,)', first using '' as deliminator, 2nd using '.'.

char buf[] = {"C:\one\two\three\with.this.file.txt"};
strcpy(buf, return_base_filename(buf,'\'));
 strcpy(buf, return_base_filename(buf,'.'));

Answered by ryyker on December 5, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP