2010-08-07 101 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只使用字符串的一部分。