빈 필드를 지원하면서 sscanf를 사용하여 쉼표로 구분된 문자열의 필드를 어떻게 구문 분석합니까?

StackOverflow https://stackoverflow.com/questions/1508754

문제

빈 필드가 포함될 수 있는 쉼표로 구분된 문자열이 있습니다.예를 들어:

1,2,,4

기본을 사용하여

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

빈 필드 이전의 모든 값을 얻고 빈 필드 이후로는 예상치 못한 결과를 얻습니다.

sscanf()에서 빈 필드에 대한 표현식을 제거하면,

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

모든 것이 잘 작동합니다.

언제 빈 필드를 얻게 될지 모르기 때문에 빈 필드를 우아하게 처리하도록 표현식을 다시 작성할 수 있는 방법이 있습니까?

도움이 되었습니까?

해결책

사용하는 경우 strtok 쉼표를 분리기 문자로 사용하면 하나 이상의 문자열 목록을 얻을 수 있습니다.

내 것을보세요 여기서 답하십시오 자세한 내용은.

다른 팁

남자 sscanf:

[ 일치 a 비어 있지 않습니다 지정된 허용 문자 세트에서 일련의 문자;

(강조 추가).

현재 CSV 값을 다루고 있는 것 같습니다.인용된 문자열을 처리하기 위해 확장해야 하는 경우(예를 들어 필드에 쉼표가 포함될 수 있도록) scanf-family는 형식의 모든 복잡성을 처리할 수 없습니다.따라서 CSV 형식(변형)을 처리하도록 특별히 설계된 코드를 사용해야 합니다.

'에서 설정된 CSV 라이브러리 구현에 대한 논의를 찾을 수 있습니다.프로그래밍 실습' - C 및 C++에서.의심할 바 없이 다른 많은 제품도 사용할 수 있습니다.

다음은 쉼표로 분리 된 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

'%'후에 '*'를 넣어 읽기를 건너 뛰십시오. 또한 예를 들어 '%3S'를 기록한 3 자만 읽을 수 있습니다.

나는 같은 질문에 대한 답을 찾기 위해 여기에 도착했다. 나는 Scanf Funcion 뒤에 남겨두고 싶지 않았다. 결국, 나는 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, %...[...] 분리기가 필요합니다 %...[...]; 그렇지 않으면 나는 정말로 형식에 관심을 갖기 위해 정말로 쇠약 해졌다. %).

제대로 작동하기 위해이 코드를 약간 수정해야했습니다.

//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

탭 구분 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;
    }

그리고 ouput :

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

일부가 있습니다 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 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

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));
}

u! 그리고 그것은 단지 1 결 측값에 대한 것입니다
다른 답변에서 지적했듯이, 당신은 '일반적인'방식으로 문자열을 구문 분석하는 것이 훨씬 좋습니다. fgets() 그리고 strtok().

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top