2010-08-07 66 views
3

我想在C中做一些非常基本的字符串处理(例如,给定一个文件名,截断文件扩展名,操作文件名,然后添加回扩展名) - 我在C上生锈,出现分段错误。简单的C字符串操作

char* fname; 
char* fname_base; 
char* outdir; 
char* new_fname; 
..... 
fname = argv[1]; 
outdir = argv[2]; 
fname_len = strlen(fname); 
strncpy(fname_base, fname, (fname_len-4)); // weird characters at the end of the truncation? 
strcpy(new_fname, outdir); // getting a segmentation on this I think 

strcat(new_fname, "/"); 
strcat(new_fname, fname_base); 
strcat(new_fname, "_test"); 
strcat(new_fname, ".jpg"); 
printf("string=%s",new_fname); 

任何建议或指针的欢迎。

许多这样的基本问题

回答

3

你需要为new_fnamefname_base分配内存。这里的是你会怎么做它new_fname

new_fname = (char*)malloc((strlen(outdir)+1)*sizeof(char)); 

strlen(outdir)+1,+ 1部分是为NULL字符'\0'终止分配内存。

+1

+1,但我认为你的意思是“空字符”,而不是“空指针”。 – 2010-08-24 18:47:09

+0

@David X:谢谢!修正了。 – 2010-08-24 20:45:25

1

感谢和道歉你要的malloc fname_basenew_fname,我相信。

即:

fname_base = (char *)(malloc(sizeof(char)*(fname_len+1))); 
fname_base[fname_len] = 0; //to stick in the null termination 

,类似的还有new_fnameoutdir

1

您正在使用未初始化的指针作为strcpy-like函数的目标:fname_basenew_fname:您需要分配内存区域进行工作,或将它们声明为char数组,例如

char fname_base[FILENAME_MAX]; 
char new_fname[FILENAME_MAX]; 
1

,你可以在一个声明中

if (asprintf(&new_fname,"%s/%s_text.jpg",outdir,fname_base) >= 0) 
    // success, else failed 

结合已建议malloc,与字符串操作,然后在某个时刻,free(new_fname)来释放内存。

(注意,这是一个GNU扩展,也可在* BSD)

+1

基于'vsnprintf'开发自己的'asprintf'实现并不重要,然后你的代码是可移植的。 – 2010-08-07 15:03:56

+2

确实,特别是如果你“在C上生锈而且[...]出现分段错误” – mvds 2010-08-07 15:51:55

2

除了什么其他的都表示,我会小心

strncpy(fname_base, fname, (fname_len-4)); 

我们假定你是要砍关闭最后4个字符(。???)。如果没有文件扩展名,或者它不是3个字符,那么这不会做你想要的。下面应该给你一个什么可能需要的想法(我假设最后一个'。'表示文件扩展名)。需要注意的是我的“C”是很生疏

char *s; 
s = (char *) strrchr (fname, '.'); 
if (s == 0) 
{ 
    strcpy (fname_base, fname); 
} 
else 
{ 
    strncpy (fname_base, fname, strlen(fname)-strlen(s)); 
    fname_base[strlen(fname)-strlen(s)] = 0; 
} 
+0

我担心4个字符等问题,但首先要解决分段错误。非常感谢您的上述代码,它非常棒! – trican 2010-08-07 16:12:49

1

清洁代码(警告!):

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

const char *extra = "_test.jpg"; 

int main(int argc, char** argv) 
{ 
    char *fname = strdup(argv[1]); /* duplicate, we need to truncate the dot */ 
    char *outdir = argv[1]; 
    char *dotpos; 
    /* ... */ 
    int new_size = strlen(fname)+strlen(extra); 
    char *new_fname = malloc(new_size); 
    dotpos = strchr(fname, '.'); 
    if(dotpos) 
    *dotpos = '\0'; /* truncate at the dot */ 
    new_fname = malloc(new_size); 
    snprintf(new_fname, new_size, "%s%s", fname, extra); 
    printf("%s\n", new_fname); 
    return 0; 
} 
+0

非常感谢这个解决方案 - 它看起来像一个优雅的方法,所以我会用这个。谢谢! – trican 2010-08-07 17:19:22

+0

你是'malloc()'''new_fname''两次调用之间没有'free()'。 – Praetorian 2010-08-07 17:19:25

+0

你说得对,实际上第二个malloc()是一个错误,并不是真正需要的。 我并不关心free(),因为app很快就要退出;),fname strdup()也需要一个free()。 – 2010-08-07 20:55:25

0

任何C字符串操作的基本是,你必须写入(和读取,除非.. ......)记忆你“拥有”。声明一些东西是一个指针(type *x)为指针保留空间,而不是指向对象当然不能被魔术知道,所以你必须malloc(或类似的)或提供一个本地缓冲区,如char buf[size]

而且您应该始终知道缓冲区溢出。

至于建议的sprintf使用(用正确分配目标缓冲区)或类似的可能是一个不错的主意。无论如何,如果你想保持当前的strcat的做法,我记得你,来连接字符串,strcat的一贯以“走” thourgh从最初到现在的字符串,因此,如果你不需要(OPS!)缓冲区溢出检查任何类型的附加字符“手工”都会更快一些:基本上,当你完成追加字符串时,你知道新的结束位置,并且在下一个strcat中,你可以从那里开始。

但是strcat的不允许知道附加的最后一个字符的地址,并使用strlen的将抵消的努力。因此,一个可能的解决方案可能是

size_t l = strlen(new_fname); 
new_fname[l++] = '/'; 
for(i = 0; fname_base[i] != 0; i++, l++) new_fname[l] = fname_base[i]; 
for(i = 0; testjpgstring[i] != 0; i++, l++) new_fname[l] = testjpgstring[i]; 
new_fname[l] = 0; // terminate the string... 

,您可以继续使用l ...(testjpgstring =“_test.jpg”)

但是,如果你的程序是完整的字符串操作,我建议使用库对于字符串(lazyness我经常用巧舌如簧)

+1

感谢所有这些全面的信息 - 它最受赞赏! – trican 2010-08-07 16:14:17

1

在下面的代码,我不调用malloc。

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

/* Change this to '\\' if you are doing this on MS-windows or something like it. */ 
#define DIR_SYM '/' 
#define EXT_SYM '.' 
#define NEW_EXT "jpg" 


int main(int argc, char * argv[]) { 
    char * fname; 
    char * outdir; 

    if (argc < 3) { 
     fprintf(stderr, "I want more command line arguments\n"); 
     return 1; 
    } 
    fname = argv[1]; 
    outdir = argv[2]; 

    char * fname_base_begin = strrchr(fname, DIR_SYM); /* last occurrence of DIR_SYM */ 
    if (!fname_base_begin) { 
     fname_base_begin = fname; // No directory symbol means that there's nothing 
           // to chop off of the front. 
    } 

    char * fname_base_end = strrchr(fname_base_begin, EXT_SYM); 
    /* NOTE: No need to search for EXT_SYM in part of the fname that we have cut off 
    * the front and then have to deal with finding the last EXT_SYM before the last 
    * DIR_SYM */ 
    if (!fname_base_end) { 
     fprintf(stderr, "I don't know what you want to do when there is no extension\n"); 
     return 1; 
    } 

    *fname_base_end = '\0'; /* Makes this an end of string instead of EXT_SYM */ 
    /* NOTE: In this code I actually changed the string passed in with the previous 
    * line. This is often not what you want to do, but in this case it should be ok. 
    */ 

    // This line should get you the results I think you were trying for in your example 
    printf("string=%s%c%s_test%c%s\n", outdir, DIR_SYM, fname_base_begin, EXT_SYM, NEW_EXT); 

    // This line should just append _test before the extension, but leave the extension 
    // as it was before. 
    printf("string=%s%c%s_test%c%s\n", outdir, DIR_SYM, fname_base_begin, EXT_SYM, fname_base_end+1); 

    return 0; 
} 

我能够与不分配内存建在字符串中,因为我让printf其实担心构建它脱身,并采取知道原来FNAME串不会在将来需要的优势。

我可以计算多长时间将需要基于各部分,然后用sprintf形成对我来说是字符串已分配的字符串的空间。

另外,如果你不想改变fname字符串的内容你也可以使用了:

printf("string=%s%c%*s_test%c%s\n", outdir, DIR_SYM, (unsigned)fname_base_begin -(unsigned)fname_base_end, fname_base_begin, EXT_SYM, fname_base_end+1); 

为了printf只使用字符串的一部分。