2010-01-31 171 views
5

將分隔字符串轉換爲C(不是C++)字符串數組的一種有效方法是什麼?例如,我可能有:C:從分隔的源字符串創建字符串數組

char *input = "valgrind --leak-check=yes --track-origins=yes ./a.out" 

源字符串始終只有一個空格作爲分隔符。而且我想malloc分配的字符串char *myarray[]這樣的malloc分配數組:

myarray[0]=="valgrind" 
myarray[1]=="--leak-check=yes" 
... 

編輯我必須假設有在inputString令牌任意數量的,所以我不能只是限制它到10什麼的。

我試圖用strtok和我實現的鏈表來解決混亂的問題,但是valgrind抱怨太多,我放棄了。

(如果你想知道,這是一個基本的Unix shell我試着寫。)

+0

@Sneesh:這是一個很好的例子,說明如何在C語言中做到這一點,因爲舊格言是'有許多方法可以讓皮膚變白......'+1。 – t0mm13b 2010-01-31 12:50:13

回答

2

關於什麼的是這樣的:如果你把所有的input輸入的開始與

char* string = "valgrind --leak-check=yes --track-origins=yes ./a.out"; 
char** args = (char**)malloc(MAX_ARGS*sizeof(char*)); 
memset(args, 0, sizeof(char*)*MAX_ARGS); 

char* curToken = strtok(string, " \t"); 

for (int i = 0; curToken != NULL; ++i) 
{ 
    args[i] = strdup(curToken); 
    curToken = strtok(NULL, " \t"); 
} 
+0

實際上,我認爲使用指向字符串的256緩衝區不會浪費,除非你真的需要保存內存。 – Jack 2010-01-31 02:42:25

+0

strtok()修改輸入字符串,所以在字符串上使用它會在某些時候崩潰平臺。 – bk1e 2010-01-31 02:42:40

+0

我可以假設'MAX_ARGS'是安全的,類似於10,000,但代碼仍然應該工作10,001個參數... – yavoh 2010-01-31 02:43:18

1

是你要記住的malloc爲標誌字符串的結尾終止null一個額外的字節?

+0

是:'char * singleToken =(char *)malloc(strlen(tokPtr)* sizeof(char)+1);''tokPtr'是'strtok'的返回值。 – yavoh 2010-01-31 02:41:44

1

strsep(3)手冊頁上OSX:

char **ap, *argv[10], *inputstring; 

    for (ap = argv; (*ap = strsep(&inputstring, " \t")) != NULL;) 
      if (**ap != '\0') 
        if (++ap >= &argv[10]) 
          break; 

編輯的令牌的任意#:

char **ap, **argv, *inputstring; 

int arglen = 10; 
argv = calloc(arglen, sizeof(char*)); 
for (ap = argv; (*ap = strsep(&inputstring, " \t")) != NULL;) 
    if (**ap != '\0') 
     if (++ap >= &argv[arglen]) 
     { 
      arglen += 10; 
      argv = realloc(argv, arglen); 
      ap = &argv[arglen-10]; 
     } 

或什麼的接近。以上可能無法正常工作,但如果不行的話,它並不遙遠。建立一個鏈表比繼續呼叫realloc更高效,但這確實不僅僅是重點 - 關鍵是如何最好地利用strsep

+0

謝謝。我忘記提及我必須假設'inputString'中有任意數量的標記 - 例如,我不能假設爲10。 – yavoh 2010-01-31 02:39:00

2

,那麼你永遠不能擁有更多的令牌比strlen(input)。如果您不允許「」作爲令牌,那麼您永遠不會擁有超過strlen(input)/2令牌。所以除非input巨大的你可以放心地寫。

char ** myarray = malloc((strlen(input)/2) * sizeof(char*)); 

int NumActualTokens = 0; 
while (char * pToken = get_token_copy(input)) 
{ 
    myarray[++NumActualTokens] = pToken; 
    input = skip_token(input); 
} 

char ** myarray = (char**) realloc(myarray, NumActualTokens * sizeof(char*)); 

作爲進一步的優化,可以保持input四周,只是\ 0替換空間,把指針到input緩衝到myArray的[]。除非由於某些原因,您需要以單獨釋放它們,否則不需要爲每個標記單獨使用一個malloc。

+0

使用你的'strlen(輸入)/ 2'思路 - 謝謝! – yavoh 2010-01-31 03:06:26

0

查看其他答案,對於C初學者來說,由於代碼尺寸太小,看起來很複雜,我認爲我會將它放入初學者中,但實際上可能更容易解析字符串,而不是使用strtok ...是這樣的:

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

char **parseInput(const char *str, int *nLen); 
void resizeptr(char ***, int nLen); 

int main(int argc, char **argv){ 
    int maxLen = 0; 
    int i = 0; 
    char **ptr = NULL; 
    char *str = "valgrind --leak-check=yes --track-origins=yes ./a.out"; 
    ptr = parseInput(str, &maxLen); 
    if (!ptr) printf("Error!\n"); 
    else{ 
     for (i = 0; i < maxLen; i++) printf("%s\n", ptr[i]); 
    } 
    for (i = 0; i < maxLen; i++) free(ptr[i]); 
    free(ptr); 
    return 0; 
} 

char **parseInput(const char *str, int *Index){ 
    char **pStr = NULL; 
    char *ptr = (char *)str; 
    int charPos = 0, indx = 0; 
    while (ptr++ && *ptr){ 
     if (!isspace(*ptr) && *ptr) charPos++; 
     else{ 
      resizeptr(&ptr, ++indx); 
      pStr[indx-1] = (char *)malloc(((charPos+1) * sizeof(char))+1); 
      if (!pStr[indx-1]) return NULL; 
      strncpy(pStr[indx-1], ptr - (charPos+1), charPos+1); 
      pStr[indx-1][charPos+1]='\0'; 
      charPos = 0; 
     } 
    } 
    if (charPos > 0){ 
     resizeptr(&pStr, ++indx); 
     pStr[indx-1] = (char *)malloc(((charPos+1) * sizeof(char))+1); 
     if (!pStr[indx-1]) return NULL; 
     strncpy(pStr[indx-1], ptr - (charPos+1), charPos+1); 
     pStr[indx-1][charPos+1]='\0'; 
    } 
    *Index = indx; 
    return (char **)pStr; 
} 

void resizeptr(char ***ptr, int nLen){ 
    if (*(ptr) == (char **)NULL){ 
     *(ptr) = (char **)malloc(nLen * sizeof(char*)); 
     if (!*(ptr)) perror("error!"); 
    }else{ 
     char **tmp = (char **)realloc(*(ptr),nLen); 
     if (!tmp) perror("error!"); 
     *(ptr) = tmp; 
    } 
} 

我稍微修改了代碼,使其更容易。我使用的唯一字符串函數是strncpy ..確定它有點冗長,但它會動態地重新分配字符串數組,而不是使用硬編碼的MAX_ARGS,這意味着當只有3或4個時,雙指針已經佔用了內存,這也會使得內存使用效率和微小,通過使用realloc,通過使用isspace涵蓋了簡單的解析,因爲它使用指針進行迭代。當遇到一個空格時,realloc使用雙指針,而malloc用於保存字符串的偏移量。

注意如何在resizeptr函數中使用三重指針..事實上,我認爲這將提供一個簡單的C程序,指針,realloc,malloc,傳遞引用,解析的基本元素一個字符串...

希望這會有所幫助, 最好的問候, 湯姆。