问题描述
我正尝试以另一种方式删除后缀,但最终输出与输入相同
#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"
的两个组合。