2013-12-17 40 views
1

我想知道C程序員通常如何從字符串中提取數據?我讀了很多關於strtok,但我個人不喜歡這個函數的工作方式。不得不用NULL作爲參數再次調用它,這對我來說似乎很奇怪。有一次,我偶然發現了這小小的一段代碼,我覺得這很圓滑:C從字符串中提取變量的方式

sscanf(data, "%*[^=]%*c%[^&]%*[^=]%*c%[^&]", usr, pw); 

這從一個URL查詢字符串(只var1=value&var2=value)提取數據。

是否有理由使用strtok超過sscanf?性能可能?

+1

http://unixplatform.blogspot.com/2010/01/avoiding-costly-strtok-call-sscanf-call.html –

+0

很有趣!但是我的問題是,'ssrtf'比'sscanf'更多的習慣嗎? –

+0

地道?噢,你的意思是那個「最佳實踐」?它是最符合您要求的那個。 –

回答

1

他們的任務,在某些種類的每一更好或更方便:

  • sscanf讓你簡明地指定一個相當複雜的模板解析值超出一行文本,但它是非常無情。如果您的輸入文本與模板中的字符不同,則掃描將失敗。出於這個原因,例如,它幾乎從來都不是用於人爲輸入的正確工具。它對掃描自動生成的輸出非常有用,例如服務器日誌行。

  • strtok更靈活,但也更加冗長:解析只有幾個字段的行可能需要很多行代碼。它也具有破壞性:它實際上修改了傳遞給它的字符串,因此在調用strtok之前可能需要複製數據。

2

恕我直言,最好的方式是最可讀和可以理解的方式。sscanfstrtok完全不符合您的用戶/ pw提取URL的要求。

相反,找你正在尋找串的邊界(以URL斜槓,在at符號,冒號,你有什麼)與strchrstrrchr,然後從開始memcpy的結束給你在哪裏需要NUL上的數據和粘性。如果字符串具有意想不到的格式,這也允許適當的錯誤處理。

+0

strtok的分隔符字符串可以'「;:」'。即超過1個字符分隔符是好的 – egur

0

我自己創建了功能的一些定義,可以幫助如炭**Split(src, sep)功能和int DoubleArrLen(char **arr), 如果你能在這裏有什麼辦法改善它是小1小時工作事小頭文件。

#include <string.h> 
#include <stdlib.h> 
#include <malloc.h> 
#include <assert.h> 
char *substring(char *string, int position, int length) 
{ 
    char *pointer; 
    int c; 

    pointer = malloc(length+1); 

    if (pointer == NULL) 
    { 
     printf("Unable to allocate memory.\n"); 
     exit(EXIT_FAILURE); 
    } 

    for (c = 0 ; c < position -1 ; c++) 
     string++; 

    for (c = 0 ; c < length ; c++) 
    { 
     *(pointer+c) = *string;  
     string++; 
    } 

    *(pointer+c) = '\0'; 

    return pointer; 
} 

char **Split(char *a_str, const char a_delim) 
{ 
    char **result = 0; 
    size_t count  = 0; 
    char *tmp  = a_str; 
    char *last_comma = 0; 

    /* Count how many elements will be extracted. */ 
    while (*tmp) 
    { 
     if (a_delim == *tmp) 
     { 
      count++; 
      last_comma = tmp; 
     } 
     tmp++; 
    } 
    /* Add space for trailing token. */ 
    count += last_comma < (a_str + strlen(a_str) - 1); 

    /* Add space for terminating null string so caller 
     knows where the list of returned strings ends. */ 
    count++; 
    result = malloc(sizeof(char *) * count); 

    if (result) 
    { 
     char delim[2] = { a_delim, '\0' }; // Fix for inconsistent splitting 
     size_t idx = 0; 
     char *token = strtok(a_str, delim); 

     while (token) 
     { 
      assert(idx < count); 
      *(result + idx++) = strdup(token); 
      token = strtok(0, delim); 
     } 
     assert(idx == count - 1); 
     *(result + idx) = 0; 
    } 
    return result; 
} 
static int SplitLen(char **array) 
{ 
    int i = 0; 
    while (*array++ != 0) 
     i++; 
    return i; 
} 
int IndexOf(char *str, char *ch) 
{ 
    int i; 
    int cnt; 
    int result = -1; 
    if(strlen(str) >= strlen(ch)) 
    { 
     for(i = 0; i<strlen(str); i++) 
     { 
      if(str[i] == ch[0]) 
      { 
       result = i; 
       for(cnt = 1; cnt < strlen(ch); cnt++) 
       { 
        if(str[i + cnt] != ch[cnt]) result = -1; break; 
       } 
      } 
     } 
    } 
    return result; 
} 
int IndexOfChar(char *str, char ch) 
{ 
    int result = -1; 
    int i = 0; 
    for(;i<strlen(str); i++) 
    { 
     if(str[i] == ch) 
     { 
      result = i; 
      break; 
     } 
    } 
    return result; 
} 

一點解釋可以是功能: 子串函數提取的字符串的一部分。 IndexOf()函數在源字符串內搜索字符串。其他應該是不言自明的。 這包括拆分功能正如我前面指出的那樣,你可以用這個來代替的strtok ..

1

sscanf採用了非常不完整的(雖然高效地實現)正則表達式語法,所以如果你想要做更復雜的東西,你不能使用sscanf

這就是說,strtok是不可重入的,所以如果你使用線程,那麼你的運氣不好。

但是總的來說,對於特定情況而言,結果跑得更快並且更優雅的那個往往被認爲是這種情況下最習慣的。

+0

strtok在現代libc(至少5年)上是可重入的。 – egur

+0

@egur不是glibc,因爲你需要使用'strtok_r' – randomusername

+0

AFAIK他們使用線程本地存儲的狀態。 – egur

1

strtok是一個非常簡單,低級別的函數,主要用於標記具有未知元素數的字符串。

NULL用於告訴strtok從最後一個位置繼續掃描字符串,爲您節省一些指針操作,並可能(內部爲strtok)進行一些初始化。

還有可讀性的問題。看代碼片段,需要一些時間來了解發生了什麼。