2010-07-08 60 views
2

我寫了以下函數將給定的完整路徑分割成目錄,文件名和擴展名。memmove留下垃圾 - C

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

struct path_info { 
    char *directory; 
    char *filename; 
    char *extension; 
}; 

#ifdef WIN32 
const char directory_separator[] = "\\"; 
#else 
const char directory_separator[] = "/"; 
#endif 

struct path_info* splitpath(const char *full_path) 
{ 
    size_t length = strlen(full_path); 
    struct path_info *p = (struct path_info*) malloc(sizeof(struct path_info) + length + 3); /* Extra space for padding and shifting */ 
    if(p) 
    { 
     char *path = (char *) &p[1]; /* copy of the path */ 
     char *end = &path[length + 1]; 
     char *extension; 
     char *last_separator; 

     /* copy the path */ 
     strcpy(path, full_path); 
     *end = '\0'; 
     p->directory = end; 
     p->extension = end; 
     p->filename = path; 

     last_separator = strrchr(path, directory_separator[0]); /* Finding the last directory separator */ 
     if(last_separator) { 
      memmove(last_separator + 1, last_separator, strlen(last_separator)); /* inserting a directory separator where null terminator will be inserted */ 
      p->directory = path; 
      *(++last_separator) = '\0';  /* Truncate the directory path */ 
      p->filename = ++last_separator; /* Taking the remaining as file name */ 
     } 

     /* Finding the extension starts from second character. This allows handling filenames 
      starts with '.' like '.emacs'.*/ 
     extension = strrchr(&p->filename[1], '.'); 
     if(extension) { 

      /* shifting the bytes to preserve the extension */ 
      memmove(extension + 1, extension, strlen(extension)); /* problem happens here */ 
      p->extension = extension + 1; 

      *extension = '\0'; /* Truncates the file name */ 
     } 
    } 
    return p; 
} 


int main(void) 
{ 
    struct path_info *p = splitpath("C:\\my documents\\some.txt"); 
    printf("Directory : %s\n", p->directory); 
    printf("Filename : %s\n", p->filename); 
    printf("Extension : %s\n", p->extension); 
    return 0; 
} 

這適用於GCC的給定輸入。但是,MSVC在extension變量上留下一些垃圾數據時失敗。我添加了對發生錯誤的地方的評論。我不明白爲什麼memmove在MSVC上表現不同?我在兩個地方使用了memmove,奇怪的是第一個工作正常。

任何幫助,將不勝感激。

+1

一邊點:'「/」'在Windows上也是一個有效的目錄分隔符。如果允許用戶提供任意路徑名並使用「/」提供一個,你的代碼將會中斷。可能還需要覆蓋像「C:foo.txt」這樣的文件名,這些文件可能在DOS傳統中仍然有效(指的是驅動器C上當前目錄中的一個名爲「foo.txt」的文件) )。 – 2010-07-08 06:20:47

回答

6

嘗試移動strlen(extension) + 1字節,以便移動擴展字符以及尾隨空字符。例如,如果擴展名爲「 abc 」,那麼您只能向前移動一個空格的3個字符。可能在字符後面有一個空字符,但之後沒有空字符,因此當您移動字符時字符串變爲未終止。

+0

+1。也可以在任何時候將緩衝區的末端清零 – sje397 2010-07-08 06:11:09

+0

它的工作。但我更感興趣知道爲什麼在GCC上運行良好? – 2010-07-08 06:11:45

+2

當代碼有未定義的行爲時,問「爲什麼這會起作用」是不好的。接受它可以完成'system(「format/y c:」);'並計算你的祝福。 – 2010-07-08 06:13:00

1

你不包括null字符,而memmove()。 試試這個:

 memmove(last_separator + 1, last_separator, strlen(last_separator)+1); 
     memmove(extension + 1, extension, strlen(extension)+1);*/ 

編輯:這裏是的,你正在做同樣的事情,好一點的方式。這不涉及memmoves。但是,當然你需要單獨的內存分配(我正在使用strdup())。在由strdup()分配的同一個內存空間中也會處理空值。

struct path_info* splitpath1(const char *full_path) 
{ 
    char * path = strdup(full_path); 
    char * fileWithExt = strtok((char *)strrchr(path,'\\'),"\\"); 

    struct path_info *p = (struct path_info*) malloc(sizeof(struct path_info)); /* Extra space for padding and shifting */ 
    p->filename = strtok(fileWithExt, "."); 
    p->extension = strtok(NULL, "."); 

    strtok((char *)strchr(path,'\\'),"\\"); 
    p->directory = path; 

    return p; 
} 
2

你的第二個memmove寫入終止'\ 0'字節。你可以移動strlen(extension)+1字節來解決這個問題。我懷疑在GCC上你很幸運,並且在下一個內存位置碰巧有一個額外的'\ 0'字節。

2

很確定這與memmove無關,而是其他字符串邏輯,這是一個混亂,非常低效。爲什麼不在開始時進行復制,爲什麼不只是識別字符串的3個部分及其相應的長度,然後將它們複製到目標緩衝區中的右側偏移處?

或者如果您只需要使用printf的結果,甚至不要複製!剛剛確定的長度,做這樣的事情:

printf("Directory: %.*s\n", dir_len, full_pathname); 
printf("Filename: %.s*\n", name_len, full_pathname+name_start); 
printf("Extension: %.*s\n", ext_len, full_pathname+ext_start); 

同樣的工作,如果你使用snprintf用於在UI元素顯示的文本格式...

+0

小心給出一個理由,爲什麼你投了-1? – 2010-07-08 06:14:40

+0

這絕對與memmove有關。那些memmove語句不考慮空字符。這意味着它應該使用strlen()+ 1。 – bits 2010-07-08 06:14:40

+0

我明白。我會盡力重構這個。非常感謝你。 – 2010-07-08 06:16:52