尝试从字符串中删除后缀但没有成功

Trying to remove suffix from string with no success

我正在尝试通过下一种方式删除后缀,但最终输出与输入相同


    #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;
    }

例如,如果文件名是 tmp.doc outnewFileName 也是 tmp.doc

"...从字符串中删除后缀但没有成功"

在下面:

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

被修改的变量需要表示为(*outNewFileName),所以上面改成:

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

此外,在这种方法中,可以通过数学计算得出数组索引,但可能很难正确执行,并且不是从字符串中删除文件扩展名的唯一方法。以下是替代方法,例如,不需要使用 #define 值,也不需要数组索引计算。 (但确实需要 . 文件扩展分隔符)...

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.
        ...

编辑 以解决评论:

另一种比前一种方法更灵活的方法是创建一种功能来指定用哪个定界符来标识文件扩展名,并将其作为参数传递给 return 字符串部分直至并包括该分隔符的最后一次出现。例如:

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

其中 return_base_filename() 定义为:

//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;
}

请注意,此方法适用于以下 filename/directory 形式:

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

第 3 次需要调用 2 次 'return_base_filename(,)',第一次使用“\”作为分隔符,第二次使用“.”。

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

你很亲近!您的 strlen(inFileName) - strlen(SUFFIX) 表达式找到了终止新字符串的正确位置,但实际上您并未对该表达式执行任何操作。

要在该位置终止字符串,请将那里的 char 的值设置为 0(即 nul 终止符):

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

在这段代码中,我们使用(*outNewFileName)来引用新的字符串数组,方括号中的偏移量来引用需要更改的实际字符nul . (需要圆括号,因为 [] 运算符的优先级高于 * 运算符。)


注意:正如评论中所指出的,如果您启用了完整的编译器警告,您会看到这个(来自 clang-cl):

warning : expression result unused [-Wunused-value]

如有任何进一步的说明,欢迎随时提出 and/or 解释。

我宁愿:

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("%s\n", outnewFileName);

    free(outnewFileName);

    return 0;
}

您应该始终阅读警告并将警告级别设置为最大可能。如果你这样做:

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

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

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

说明问题。

初学者总是尝试编写更通用的函数。

这个函数声明

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

不好。它依赖于神奇的字符串文字 ".doc"。所以您不能使用该功能去除其他后缀。

其次,通过引用传递第一个参数(指针)会产生一个问题,即在分配新地址之前是否释放指针。

函数定义也没有意义

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

你的意思好像是

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

但这种做法是不正确的。传递的字符串可以没有必须删除的后缀。在这种情况下,该函数将 return 一个无效的字符串。或者传递的字符串可以多次包含与后缀相同的组合。此外,用户可以传递长度小于后缀长度的字符串。

所以这个函数的声明和它的定义整体上是错误和糟糕的。

函数可以按照下面的演示程序所示的方式声明和定义。

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

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

    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;
}

程序输出为

tmp.doc

如您所见,该函数确实从包含符号 ".doc".

的两个组合的字符串 "tmp.doc.doc" 中删除了后缀