2009-10-02 367 views
8

我有一個逗號分隔的字符串,可能包含空字段。例如:如何在支持空字段時使用sscanf解析逗號分隔字符串中的字段?

1,2,,4 

使用基本

sscanf(string,"%[^,],%[^,],%[^,],%[^,],%[^,]", &val1, &val2, &val3, &val4); 

我得到的空場之前,所有的值,然後從起空場意外的結果。

當我從的sscanf()刪除空字段的表達,

sscanf(string,"%[^,],%[^,],,%[^,],%[^,]", &val1, &val2, &val3, &val4); 

一切順利的罰款。

由於我不知道什麼時候我會得到一個空字段,有沒有辦法重寫表達式來優雅地處理空字段?

回答

8

如果您使用strtok且逗號爲分隔符,您將得到一個或多個字符串爲空/零長度的字符串列表。

查看我的answer here瞭解更多信息。

+0

是的,那就是如果我不能找到一個方法來適應sscanf的,我會去的路線。我仍然希望我可以適應我的sscanf,但據我所知[^,]不匹配空序列。 – Belrog 2009-10-02 10:28:56

+0

@Belrog,似乎sscanf的內部計數器卡住了,如果字符串包含',,'。所以最好使用'strtok'。 – 2009-10-02 10:53:21

+0

我真的不希望這樣做。我有一個長長的不同類型的值列表(字符/整數/浮點數)。這將意味着一個字段計數器和一個大的switch語句來正確地分配它。 *嘆息* – Belrog 2009-10-02 11:19:10

5

man sscanf

[與從指定組 接受字符的字符的非空序列;

(強調增加)。

+1

確實如此。有沒有改變這種行爲的標誌? – 2012-07-20 05:32:34

0

這看起來像你正在處理CSV值。如果您需要擴展它以處理帶引號的字符串(例如,使字段可以包含逗號),則會發現該系列無法處理格式的所有複雜性。因此,您需要使用專門用於處理(您的變體)CSV格式的代碼。

您可以在C和C++中找到關於'The Practice of Programming'中設置的CSV庫實現的討論。毫無疑問,還有許多其他可用的。

0

scanf()返回分配的項目數。也許你可以使用該信息...

char *data = "1, 2,,, 5, 6"; 
int a[6]; 
int assigned = sscanf(data, "%d,%d,%d,%d,%d,%d", a, a+1, a+2, a+3, a+4, a+5); 
if (assigned < 6) { 
    char fmt[18]; 
    switch (assigned) { 
     default: assert(0 && "this did not happen"); break; 
     case 0: fmt = ",%d,%d,%d,%d,%d"; break; 
     case 1: fmt = "%d,,%d,%d,%d,%d"; break; 
     case 2: fmt = "%d,%d,,%d,%d,%d"; break; 
     case 3: fmt = "%d,%d,%d,,%d,%d"; break; 
     case 4: fmt = "%d,%d,%d,%d,,%d"; break; 
     case 5: fmt = "%d,%d,%d,%d,%d,"; break; 
    } 
    sscanf(data, fmt, a+(assigned<=0), a+1+(assigned<=1), a+2+(assigned<=2), 
         a+3+(assigned<=3), a+4+(assigned<=4)); 
} 

呃! 這只是一個缺少的值
正如其他答案指出的,你最好以「常用」方式解析字符串:fgets()strtok()

0

這是我的版本掃描逗號分隔的int值。該代碼檢測空的和非整數的字段。

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

int main(){ 
    char str[] = " 1 , 2 x, , 4 "; 
    printf("str: '%s'\n", str); 

    for(char *s2 = str; s2;){ 
    while(*s2 == ' ' || *s2 == '\t') s2++; 
    char *s1 = strsep(&s2, ","); 
    if(!*s1){ 
     printf("val: (empty)\n"); 
    } 
    else{ 
     int val; 
     char ch; 
     int ret = sscanf(s1, " %i %c", &val, &ch); 
     if(ret != 1){ 
     printf("val: (syntax error)\n"); 
     } 
     else{ 
     printf("val: %i\n", val); 
     } 
    } 
    } 

    return 0; 
} 

結果:

str: ' 1 , 2 x, , 4 ' 
val: 1 
val: (syntax error) 
val: (empty) 
val: 4 
0

放了之後 '*' 的 '%' 跳過閱讀。另外,例如只能讀取3個字符,記錄'%3s'。

+0

你的文章不是關於在這裏被問到的主題。我已刪除該鏈接以防止您的答案被標記爲垃圾郵件。 – 2012-10-30 08:53:43

0

我到達這裏尋找相同問題的答案。我不想留下scanf功能。最後,我自己構建了一個zsscanf,在那裏我分析了格式,sscanf逐一檢查了每個數據,並檢查了sscanf的返回結果,看看我是否有空的讀取。這在某種程度上是我的特殊情況:我只想要一些字段,其中一些字段可能是空的,並且不能假設分隔符。

#include <stdarg.h> 
#include <stdio.h> 

int zsscanf(char *data, char *format, ...) 
{ 
    va_list argp; 
    va_start(argp, format); 
    int fptr = 0, sptr = 0, iptr = 0, isptr = 0, ok, saved = 0; 
    char def[32]; 
    while (1) 
    { 
     if (format[fptr] != '%') 
     { 
      ok = sscanf(&format[fptr], "%28[^%]%n", def, &iptr); 
      if (!ok) break; 
      fptr += iptr; 
      def[iptr] = '%'; 
      def[iptr+1] = 'n'; 
      def[iptr+2] = 0; 
      ok = sscanf(&data[sptr], def, &isptr); 
      if (!ok) break; 
      sptr += isptr; 
     } 
     else 
      if (format[fptr+1] == '%') 
      { 
       if (data[sptr] == '%') 
       { 
        fptr += 2; 
        sptr += 1; 
       } 
       else 
       { 
        ok = -1; 
        break; 
       } 
      } 
      else 
      { 
       void *savehere = NULL; 
       ok = sscanf(&format[fptr], "%%%28[^%]%n", &def[1], &iptr); 
       if (!ok) break; 
       fptr += iptr; 
       def[0] = '%'; 
       def[iptr] = '%'; 
       def[iptr+1] = 'n'; 
       def[iptr+2] = 0; 
       isptr = 0; 
       if (def[1] != '*') 
       { 
        savehere = va_arg(argp, void*); 
        ok = sscanf(&data[sptr], def, savehere, &isptr); 
        if (ok == 0 && isptr == 0) 
        { 
         // Let's assume only char types. Won't hurt in other cases. 
         ((char*)savehere)[0] = 0; 
         ok = 1; 
        } 
        if (ok > 0) 
        { 
         saved++; 
        } 
       } 
       else 
       { 
        ok = sscanf(&data[sptr], def, &isptr) == 0; 
       } 
       if (ok < 0) break; 
       sptr += isptr; 
      } 
    } 
    va_end(argp); 
    return saved == 0 ? ok : saved; 
} 

int main() 
{ 
    char *format = "%15[^\t;,]%*1[\t;,]" // NameId 
        "%*[^\t;,]%*1[\t;,]" // Name 
        "%*[^\t;,]%*1[\t;,]" // Abbreviation 
        "%*[^\t;,]%*1[\t;,]" // Description 
        "%31[^\t;,]"; // Electrical Line 
    char nameId[16]; 
    char elect[32]; 
    char *line1 = "TVC-CCTV-0002\tTVC-CCTV-0002\tTVC-CCTV-0002\tCCTV DOMO CAMERA 21-32-29\tELECTRICAL_TopoLine_823\tfoo\tbar"; 
    char *line2 = "TVC-CCTV-0000;;;;;foo;bar;"; 

    int ok = zsscanf(line1, format, nameId, elect); 
    printf ("%d: |%s|%s|\n", ok, nameId, elect); 
    ok = zsscanf(line2, format, nameId, elect); 
    printf ("%d: |%s|%s|\n", ok, nameId, elect); 
    return 0; 
} 

輸出:

2: |TVC-CCTV-0002|ELECTRICAL_TopoLine_823| 
    2: |TVC-CCTV-0000|| 

被警告,它沒有經過全面測試並具有很大的侷限性(最明顯的:只接受%...s%...c%...[...],需要分隔爲%...[...],否則我會真的很討厭格式化字符串,這種方式我只關心%)。

0

我不得不修改這個代碼有點正常工作:

//rm token_pure;gcc -Wall -O3 -o token_pure token_pure.c; ./token_pure 
#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char str[] = " 1 , 2 x, , 4 "; 
    char *s1; 
    char *s2; 
    s2=(void*)&str; //this is here to avoid warning of assignment from incompatible pointer type 
     do { 
      while(*s2 == ' ' || *s2 == '\t') s2++; 
      s1 = strsep(&s2, ","); 
      if(!*s1){ 
       printf("val: (empty)\n"); 
      } 
      else{ 
       int val; 
       char ch; 
       int ret = sscanf(s1, " %i %c", &val, &ch); 
       if(ret != 1){ 
        printf("val: (syntax error)\n"); 
       } 
       else{ 
        printf("val: %i\n", val); 
       } 
      } 
     } while (s2!=0); 
     return 0; 
    } 

和輸出:

val: 1 
val: (syntax error) 
val: (empty) 
val: 4 
0

我爲標籤的修改分隔TSV文件,希望它可以幫助:

//rm token_tab;gcc -Wall -O3 -o token_tab token_tab.c; ./token_tab 
#include <stdio.h> 
#include <string.h> 

int main() 
{ 
// char str[] = " 1  2 x   text 4 "; 
    char str[] = " 1\t 2 x\t\t text\t4 "; 
    char *s1; 
    char *s2; 
    s2=(void*)&str; //this is here to avoid warning of assignment from incompatible pointer type 
     do { 
      while(*s2 == ' ') s2++; 
      s1 = strsep(&s2, "\t"); 
      if(!*s1){ 
       printf("val: (empty)\n"); 
      } 
      else{ 
       int val; 
       char ch; 
       int ret = sscanf(s1, " %i %c", &val, &ch); 
       if(ret != 1){ 
        printf("val: (syntax error or string)=%s\n", s1); 
       } 
       else{ 
        printf("val: %i\n", val); 
       } 
      } 
     } while (s2!=0); 
     return 0; 
    } 

而且輸出繼電器:

val: 1 
val: (syntax error or string)=2 x 
val: (empty) 
val: (syntax error or string)=text 
val: 4 
0

有一些問題的strtok這裏列出()http://benpfaff.org/writings/clc/strtok.html

因此,最好是避免strtok的

現在,考慮包含空字段的字符串如下:

char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here 

可以使用簡單的功能,能夠將字符串轉換爲CSV格式讀給一個int數組

int strCSV2Float(float *strFloatArray , char *myCSVStringing); 

請看以下使用

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



int strCSV2Float(float *strFloatArray , char *myCSVStringing); 

    void main() 
{ 

    char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here 
    float floatArr[10]; // specify size here 
    int totalValues = 0; 

    printf("myCSVString == %s \n",&myCSVString[0]); 

    totalValues = strCSV2Float(&floatArr[0] , &myCSVString[0]); // call the function here 

    int floatValueCount = 0; 

    for (floatValueCount = 0 ; floatValueCount < totalValues ; floatValueCount++) 
    { 

     printf("floatArr[%d] = %f\n",floatValueCount , floatArr[floatValueCount]); 

    } 

} 




int strCSV2Float(float *strFloatArray , char *myCSVStringing) 
{ 

int strLen = 0; 
int commaCount =0; // count the number of commas 
int commaCountOld =0; // count the number of commas 
int wordEndChar = 0; 
int wordStartChar = -1; 
int wordLength =0; 


    for(strLen=0; myCSVStringing[strLen] != '\0'; strLen++) // first get the string length 
    { 

     if ((myCSVStringing[strLen] == ',') || (myCSVStringing[strLen+1] == '\0')) 
     { 
      commaCount++; 
      wordEndChar = strLen; 
     } 
     if ((commaCount - commaCountOld) > 0) 
     { 
      int aIter =0; 
      wordLength = (wordEndChar - wordStartChar); 
      char word[55] = ""; 
      for (aIter = 0; aIter < wordLength; aIter++) 
      { 
      word[aIter] = myCSVStringing[strLen-wordLength+aIter+1]; 
      } 

      if (word[aIter-1] == ',') 
      word[aIter-1] = '\0'; 

      // printf("\n"); 
      word[wordLength] = '\0'; 
      strFloatArray[commaCount-1] = atof(&word[0]); 

      wordLength = 0; 
      wordStartChar = wordEndChar; 
      commaCountOld = commaCount; 

     } 
    } 

    return commaCount; 

} 

輸出如下:

myCSVString == -1.4,2.6,,-0.24,1.26 
floatArr[0] = -1.400000 
floatArr[1] = 2.600000 
floatArr[2] = 0.000000 
floatArr[3] = -0.240000 
floatArr[4] = 1.260000