2014-12-28 40 views
1

對不起,這個「另一個」sscanf問題,但我找不到任何解決方案的實驗。sscanf說明符%[]和緩衝區溢出

這裏是一個字符串,我想分析和提取2子被分隔「:」:

char *str = "tag:R123:P1234"; 

這個函數做的工作:

char r_value[5]; 
char p_value[6]; 
sscanf(str, "tag:%[^:]:%s", r_value, p_value); 
// now r_value = "R123" and p_value = "P1234" 

但現在我要確保我將不會溢出我的接收緩衝區:

sscanf(str, "tag:%[^:]:%5s", r_value, p_value); 
// this is good for p_value, if I give something bigger than 5 character long it 
// will be truncated, if less than 5 character long, I get it also 

但問題是%[]格式:

sscanf(str, "tag:%4[^:]:%5s", r_value, p_value); 
// this will be ok if initial r_value is 4 char or less long 
// but not OK if more than 4 char long, then it will be truncated, 
// but p_value will not be found... 

請注意我在嵌入式系統;我買不起非常大的緩衝區來提高溢出限制...

有沒有辦法解決我的問題?還是應該對每個字符執行手動循環以手動進行解析?

+0

你不想問'sscanf',不要使用它。沒有它,還有很多其他的方式來做事情。 –

+0

嘗試尋找['strtok()'函數](http://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm) –

回答

5

這個任務是非常容易使用strtok_r

char r_value[5]; 
char p_value[6]; 
char *token; 
char *saveptr; 

token = strtok_r(str, ":", &saveptr); 
if (token == NULL) 
    return; /* there is no ":" in the string so handle failure properly */ 
token = strtok_r(NULL, ":", &saveptr); 
if (token == NULL) 
    return; /* no more tokens found so handle failure properly */ 
strncpy(r_value, token, sizeof r_value); 
r_value[sizeof(r_value) - 1] = '\0'; 
token = strtok_r(NULL, ":", &saveptr); 
if (token == NULL) 
    return; /* no more tokens found so handle failure properly */ 
strncpy(p_value, token, sizeof p_value); 
p_value[sizeof(p_value) - 1] = '\0'; 

,並可以防止溢出的r_valuep_value

唯一額外的事情是,你應該複製str因爲strtok_r需要將它修改

char *str = "tag:R123:P1234"; 

變化

char *str = strdup("tag:R123:P1234"); 

,記得free(str)結尾。

+0

很好,這是非常好的,我可以負擔修改初始字符串,所以不需要保存。 順便說一句你如何在strncpy中使用sizeof without()? – GPTechinno

+0

'sizeof'是一個操作符而非函數。 –

+0

偉大的2個新事物在1個問題中學習!謝謝StackOverflow所有人;) – GPTechinno

4

sscanf()的優點之一是,如果第一個格式字符串不能按預期工作,則可以嘗試第二個(和第三個和...)格式。對於直接文件I/O變體,例如scanf()fscanf(),您不容易獲得第二次機會。

在這種情況下,我會考慮:

int n; 
if ((n = sscanf(str, "tag:%4[^:]:%5s", r_value, p_value)) == 1) 
    n = sscanf(str, "tag:%4[^:]%*[^:]:%5s", r_value, p_value); 
if (n != 2) 
    …report format error… 
…continue with extra characters skipped… 

%*[^:]*抑制掃描結果的分配和轉換規範不返回值從sscanf()計數。

如果您需要了解p_value太長,你可以使用一個%n轉換規格檢查偏移(注意%n沒有在返回值從sscanf()計數,並沒有實用價值將在o除非n == 2):

int o; 
int n; 
if ((n = sscanf(str, "tag:%4[^:]:%5s%n", r_value, p_value, &o)) == 1) 
    n = sscanf(str, "tag:%4[^:]%*[^:]:%5s%n", r_value, p_value, &o); 
if (n != 2 || str[o] != '\0') 
    …report format error… 
…continue with extra characters skipped… 

scanf()功能一般,特別是sscanf(),功能強大,靈活,非常努力,正確使用。要小心!

1

下面是與sscanf配合使用的另一種方法。第一個sscanf將R值轉換爲4個字符。第二個sscanf確定R值結束的位置,而不限制長度或存儲結果。第三個sscanf轉換P值,從第二個sscanf確定的索引開始。

char *str = "tag:R123:P1234"; 
char r_value[5]; 
char p_value[6]; 

int success = 0; 

if (sscanf(str, "tag:%4[^:]", r_value) == 1) // convert and store R value 
{ 
    int n; 
    sscanf(str, "tag:%*[^:]%n", &n);    // find the end of the R value 

    if (sscanf(&str[n], ":%5s", p_value) == 1) // convert and store P value 
     success = 1; 
} 

if (success) 
    printf("%s\n%s\n", r_value, p_value);