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

问题描述

我正尝试以另一种方式删除后缀,但最终输出与输入相同


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

例如,如果fileName是tmp.doc,则outnewFileName也是tmp.doc

解决方法

您非常亲密!您的strlen(inFileName) - strlen(SUFFIX)表达式找到了终止新字符串的正确位置,但实际上您对该表达式没有任何作用。

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

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

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


注意:如评论中所指出的,如果启用了完整的编译器警告,您将可以看到此信息(来自 clang-cl ):

警告:表达式结果未使用的[-未使用值]

随时要求进一步的澄清和/或解释。

,

“ ...从字符串中删除后缀失败”

以下:

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

编辑 以发表评论:

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

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

请注意,此方法适用于以下文件名/目录形式:

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

第3次需要两次调用'return_base_filename(,)',首先使用'\'作为分隔符,第二次使用'。'。

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

我宁愿:

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)] = '\0';
}

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

因此,此函数声明及其定义总体上是错误的,也是错误的。

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

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

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

    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

如您所见,函数确实从字符串"tmp.doc.doc"中删除了后缀,该字符串包含符号".doc"的两个组合。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...