2015-01-30 3 views
4

공백으로 구분 된 정수의 여러 행을 포함하는 파일을 C로 분석하여 동적 배열의 동적 배열로 변환하려고합니다. 각 행은 배열 배열의 배열입니다. 행의 수와 각 행의 요소는 일정하지 않습니다.C에서 공백으로 분리 된 여러 공백의 문자열을 구문 분석합니다.

내가 지금까지 한 것은 fgets를 사용하여 각 줄을 문자열로 잡는 것입니다.

그러나 공백으로 구분 된 정수 문자열을 구문 분석하는 방법을 알 수는 없습니다.

나는 (공백으로 구분 된 정수의 전체 파일을 구문 분석하는 데 fscanf를 사용할 수 있기 때문에) sscanf를 사용할 수 있다고 생각했습니다. 그러나 sscanf에는 다른 기능이 있습니다. sscanf는 문자열의 첫 번째 숫자 만 구문 분석합니다. 내 생각 엔 그 줄은 문자열이 아니기 때문에 스트림입니다.

스트리밍 스트림을 만드는 방법을 둘러 보았지만 C (비표준 라이브러리를 사용할 수 없음)에서 사용할 수있는 것처럼 보이지 않습니다.

char* line; 
char lineBuffer[BUFFER_SIZE]; 
FILE *filePtr; 
int value; 

...

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) { 

    printf("%s\n", lineBuffer); 

    while(sscanf(lineBuffer, "%d ", &value) > 0) { 
     printf("%d\n", value); 
    } 
} 

내가 문자열을 구문 분석하는 데 사용할 수있는 뭔가가 있나요. 그렇지 않은 경우이 전체 시스템에 대한 대안이 있습니까? 나는 REGEX를 사용하지 않는 것을 선호한다. 존재하는 경우 경기의 마지막에 대한 포인터, 현재 위치를 저장하는 문자 포인터를 제공

+0

예 : ['strtok'] (http://en.cppreference.com/w/c/string/byte/strtok)를 사용하여 공간에서 문자열을 분리 한 다음 ['sscanf'] (http : // en. cppreference.com/w/c/io/fscanf) 또는 ['strtol'] (http://en.cppreference.com/w/c/string/byte/strtol)를 참조하십시오. –

+0

관련 - https://stackoverflow.com/questions/10826953/sscanf-doesnt-move-scans-same-integer-everytime-c 및 https://stackoverflow.com/questions/13503135/get-number-of-characters -read-by-sscanf. 귀하의 질문에 대한 답변이 있기 때문에 중복 된 것으로 투표 할 것입니다. – sashoalm

+1

또한 "% d"형식은 선행 공백을 건너므로 수동으로 형식으로 수행 할 필요가 없습니다. –

답변

4

사용 strtol() :

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) { 

    printf("%s\n", lineBuffer); 
    char* p = lineBuffer; 
    while(p < lineBuffer+BUFFER_SIZE) { 
     char* end; 
     long int value = strtol(p , &end , 10); 
     if(value == 0L && end == p) //docs also suggest checking errno value 
      break; 

     printf("%ld\n", value); 
     p = end ; 
    } 
} 
1

사용 strtok() 기능 분리 문자 및 장소로 " " (공간)와

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) { 

    printf("%s\n", lineBuffer); 

    char *token=strtok(line," "); 

    while(token!=NULL) 
    { 
     if(sscanf(token, "%d", &value) > 0) 
      printf("%d\n", value); 
     token=strtok(NULL," "); 
    } 
} 
0

그냥 입력 라인을 통해 루프를 사용하는 푸티 Atol을 이용, : strtok() 반환 NULL 각각의 토큰이 각 토큰에서 각 숫자를 인쇄 얻을 때 종료 루프에서이()는 다음 공백 구분 기호에서 멈춘다. 긍정적 인 정수에만 적합;)하지만 빠르다. 수많은 strtok 및 sscanf 문서를 읽을 필요가 없으며 정수 사이에 산재 해있는 "노이즈"가있는 상황에서도 견고합니다.
음수 int에서도 작동하도록하려면 isdigit()를! isspace()로 바꾸면됩니다.

void bla() 
{ 
    const char * input = " 1   3   4  6  "; 
    size_t i; 
    size_t len = strlen(input); 
    for (i = 0; i < len; ++i) 
    { 
     if (isdigit(input[i])) 
     { 
      printf("%d\n", atol(&input[i])); 
      while (i < len && isdigit(input[i])) 
       ++i; 
     } 

    } 
} 

void bla1() 
{ // positive and negative ints version 
    const char * input = " 10   -3   42  6  "; 
    size_t i; 
    size_t len = strlen(input); 
    for (i = 0; i < len; ++i) 
    { 
     if (!isspace(input[i])) 
     { 
      printf("%d\n", atol(&input[i])); 
      while (i < len && !isspace(input[i])) 
       ++i; 
     } 
    } 
    /* Output: 
     10 
     -3 
     42 
     6 

    */ 
} 

질문의 다음 부분은 (암시 적)이고, 방법에 구문 분석 INT 값을 저장하는 동적 배열을 처리하는 방법. 여기에 위의 코드를 기반으로하는 솔루션입니다. chunkSize는 입력에 대해 너무 작게 설정되므로 realloc 코드 섹션도 작동하는지 테스트 할 수 있습니다.

typedef struct DataRow_tag 
{ 
    int32_t *data; 
    size_t length; 
} DataRow_t; 

// Returns a "bool" in C-style. Yes, there is stdbool.h in ansi c99 but it is disadviced. 
// (Platform dependent trouble in the context of C/C++ interaction, often across library/DLL boundaries. 
// Especially if you compile C with a C-compiler and the C++ code with C++ compiler. Which happens. 
// Every now and then, sizeof(c++ bool) != sizeof(C bool) and you waste a lot of time finding the problem.) 
// The caller takes ownership of the DataRow_t::data pointer and has to free() it when done using it. 
// 0: false -> fail 
// 1: true -> success! 
int 
ReadRowWithUnknownNumberOfColumnsOfInt32 
    (const char * row  // Zero terminated string containing 1 row worth of data. 
    , DataRow_t *result  // Pointer to the place the data will be stored at. 
    ) 
{ 
    int success = 0; 
    size_t chunkSize = 10; // Set this value to something most likely large enough for your application. 

    // This function is not cleaning up your garbage, dude ;) Gimme a clean result structure! 
    assert(NULL != result && NULL == result->data); 
    if (NULL != result && NULL == result->data) 
    { 
     result->length = 0; 
     size_t rowLength = strlen(row); 
     const char *pInput = row; 
     const char *pEnd = &row[rowLength-1]; 

     result->data = (int32_t*)malloc(chunkSize * sizeof(int32_t)); 
     if (NULL != result->data) 
     { 
      for (; pInput < pEnd; ++pInput) 
      { 
       assert(pInput <= pEnd); 
       assert(*pInput != 0); 
       if (!isspace(*pInput)) // ultra correct would be to cast to unsigned char first...says microsoft code analyzer in paranoia mode. 
       { 
        long lval = atol(pInput); // what is a long anyway? 4 bytes, 2 bytes, 8 bytes? We only hope it will fit into our int32_t... 
        // TODO: we could test here if lval value fits in an int32_t...platform dependent! 
        result->data[result->length++] = lval; 
        if (result->length == chunkSize) 
        { // our buffer was too small... we need a bigger one. 
         chunkSize = chunkSize + chunkSize; // doubling our buffer, hoping it will be enough, now. 
         int32_t * temp = (int32_t*)realloc(result->data, chunkSize * sizeof(int32_t)); 
         if (NULL == temp) 
         { // realloc is a funny function from the dark ages of c. It returns NULL if out of memory. 
          // So we cannot simply use result->data pointer for realloc call as this might end up with a memory leak. 
          free(result->data); 
          result->length = 0; 
          break; 
         } 
         else 
         { 
          result->data = temp; 
         } 
        } 
        while (pInput < pEnd && !isspace(*pInput)) 
         ++pInput; 
       } 
      } 
      if (pInput >= pEnd) 
       success = 1; 
      else 
      { // make sure we do not leave result in some funny state. 
       result->length = 0; 
       free(result->data); // free(NULL) legal. If memblock is NULL, the pointer is ignored and free immediately returns. 
       result->data = NULL; 
      } 
     } 
    } 

    return success; 
} 
void Bla2() 
{ 
    const char * input = "-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13"; 
    DataRow_t dataRow = { 0 }; 
    if (ReadRowWithUnknownNumberOfColumnsOfInt32(input, &dataRow)) 
    { 
     for (size_t i = 0; i < dataRow.length; ++i) 
     { 
      printf("%d ", dataRow.data[i]); 
     } 
     printf("\n"); 

     free(dataRow.data); 
     dataRow.data = NULL; 
     dataRow.length = 0; 
    } 
} 
+0

자동 다운 투표 봇이 있습니까? 브라우저가 "게시"버튼을 누른 후에로드되기도 전에 -1이되었습니다. – BitTickler

+0

@chux의 경우 답변 텍스트의 마지막 줄을 적용해야합니다.) – BitTickler

+0

@chux 및 btw ... 사례. -123 만 123로 반환됩니다. while은 if 내부에 있으므로 '-'또는 '+'를 이미 지나쳤습니다. – BitTickler

2

fgets()을 통해 행을 읽는 것이 가장 좋은 첫 번째 단계입니다.

2 방법 : strtol() (더 나은 오류 처리) 및 sscanf()

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) { 
    char *endptr; 
    while (1) { 
    errno = 0; 
    long num = strtol(line, &endptr, 10); 
    if (line == endptr) break; // no conversion 
    if (errno) break; // out of range or other error 

    #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX 
    // long and int may have different ranges 
    if (num < INT_MIN || num > INT_MAX) { 
     errno = ERANGE; 
     break; // out of range 
    } 
    #endif 

    int value = (int) num; 
    printf("%d\n", value); 
    line = endptr; 
    } 
    while (isspace((unsigned char) *endptr)) endptr++; 
    if (*endptr != '\0') Handle_ExtraGarbageAtEndOfLine(); 
} 

"지금까지 문자열의 첫 번째 숫자를 구문 분석에만 sscanf를." 그렇진 않습니다. sscanf()"%n"을 사용하여 검색이 중지 된 위치를 기록하십시오.

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) { 
    int n; 
    while (1) { 
    n = 0; 
    int value; 
    if (sscanf(line, "%d %n", &value, &n) != 1) break; 
    printf("%d\n", value); 
    line += n; 
    } 
    if (line[n] != '\0') Handle_ExtraGarbageAtEndOfLine(); 
} 
0

당신은 사용해야

lineBuffer = (char *)malloc(sizeof(BUFFER_SIZE + 1)); 

보다 :

char lineBuffer[BUFFER_SIZE]; 

귀하의 스택 것이다 당신에게 감사를!

+1

반드시 그렇지는 않습니다. 예를 들어, 32 비트 Linux에서는 힙보다 스택에 더 많은 공간이있을 수 있습니다. (https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/5/html/Tuning_and_Optimizing_Red_Hat_Enterprise_Linux_for_Oracle_9i_and_10g_Databases /sect-Oracle_9i_and_10g_Tuning_Guide-Growing_the_Oracle_SGA_to_2.7_GB_in_x86_Red_Hat_Enterprise_Linux_2.1_Without_VLM-Linux_Memory_Layout.html) 그리고 64 비트 메모리 모델에서는 사용 가능한 힙 또는 스택 주소 공간이 부족해지기 전에 실제 메모리를 거의 다 써 버릴 것입니다. 힙 성능에 신경 쓰지 마라. –

+0

당신은 전적으로 @AndrewHenle입니다, 고마워요. –

관련 문제