2014-12-13 7 views
0

학생 평균 범위를 찾는 프로그램을 작성해야합니다. 학생의 성이 인 경우 char name을 유연하게 처리하는 방법을 모르겠다.?데이터 구조의 가변 길이 가변

struct student { 
    char group[5]; 
    char name[21]; 
    char exam[5]; 
    char test[12]; 
}; 

void student_average_scope(FILE *file) { 
    struct student this; 
    int i; 
    int sum; 

    while (fgets((char *)&this, sizeof(this), file) != NULL) { 
    this.name[20] = '\0'; 
    this.exam[4] = '\0'; 

    for (i = 0, sum = 0; i < 4; i++) { 
     sum += this.exam[i] - '0'; 
    } 

    printf("%s - %.1f\n", this.name, (float)sum/4); 
    } 

    fclose(file); 
} 

내 학생 목록 :

4273 Багров Д. С. 5454 знззз 
4273 Нуйикн А. А. 4333 знзнз 
4272 Галкин Г. А. 5445 ззззз 
4273 Иванов А. А. 3433 знззн 
4272 Козлов И. И. 4443 ззззз 
4272 Козлов В. И. 4444 знззз 
4272 Бобров П. Н. 4543 знззз 
4272 Шмелев И. И. 4443 знззн 
+2

I 자동 분석 구조체를 사용하여 문자열 좋은 생각이고, 문제는의 직접적인 효과라고 생각하지 않습니다를 그 선택. 예를 들어 fgets를 사용하여 문자 배열로 행을 읽은 다음 sscanf를 사용하여 파싱합니다. – fvu

+1

가장 긴 이름이 가능하도록 (메모리 비효율적이지만 매우 직관적 인)'name' 요소 크기를 늘리거나'char *'으로 만들고'malloc'을 사용하십시오. 또한 "문자열 파싱"('strtok'시도)을 재고해야합니다. – usr2564301

+1

나는 @fvu 제안을 제안하지만'sscanf' 대신'strtok'에 관심이있을 것입니다. –

답변

3

는 매우 편리한 수단 문자열을 파싱한다.

은 우리가 후있어 일정한 패턴이있는 int 및 문자열에 의해 followd 3 문자열 뒤에 INT는 것을이 예를 들면 가정합니다 입력 문자열

4273 Багров Д. С. 5454 знззз 

을 감안할 때. 다른 방법이 있습니다. 저는 이것들을 다시 생각해 보겠습니다.

아주 기본적인 데모 :

#include <stdio.h> 

int main(void) { 
    char * inputdata = "4273 Багров Д. С. 5454 знззз"; 
    // variables to receive the scanned data 
    int firstint, secondint; 
    char firststring[32]; 
    char secondstring[32]; 
    char thirdstring[32]; 
    char fourthstring[32]; 
    // important, you should check whether the number of converted elements 
    // matches what you expect: 
    int scannedelements; 

    // let's scan the input 
    scannedelements = sscanf (inputdata,"%d %s %s %s %d %s",&firstint, &firststring, secondstring, 
       thirdstring,&secondint,fourthstring); 
    // and show what we found. Notice the similarity between scanf and printf 
    // but also note the subtle differences!!! 
    printf("We scanned %d %s %s %s %d %s\n",firstint, firststring, secondstring, 
       thirdstring,secondint,fourthstring); 
    printf("That's a total of %d elements %d\n",scannedelements); 
    return 0; 
} 

출력 : 내가 정수로 당신이 시험이라는 분야를 스캔

We scanned 4273 Багров Д. С. 5454 знззз 
That's a total of 6 elements 

공지 사항, 당신은 쉽게의 루프에 의해 그것의 숫자를 추출 할 수 있습니다 digit = data % 10; data = data/10;

이제 문자열의 첫 번째 그룹이 3 개의 다른 출력으로 잘리는 사실이 귀찮을 수 있습니다.

#include <stdio.h> 

int main(void) { 
    char * inputdata = "4273 Багров Д. С. 5454 знззз"; 
    // variables to receive the scanned data 
    int firstint, secondint; 
    char firststring[32]; 
    char secondstring[32]; 
    char thirdstring[32]; 
    char fourthstring[32]; 
    // important, you should check whether the number of converted elements 
    // matches what you expect: 
    int scannedelements; 

    // Alternatively, let's scan the group of 3 strings into 1 variable 
    scannedelements = sscanf (inputdata,"%d %[^0-9] %d %s",&firstint, firststring, &secondint,fourthstring); 
    // and show what we found. 
    printf("We scanned %d %s %d %s\n",firstint, firststring,secondint,fourthstring); 
    printf("That's a total of %d elements %d\n",scannedelements); 
    return 0; 
} 

출력 :

We scanned 4273 Багров Д. С. 5454 знззз 
That's a total of 4 elements -1079150400 

공지 후미 공간 Багров Д. С.에서 문제가 될 수도 있고 그렇지 않을 수도 그것이 자리를 만날 때까지 상기 출력 데이터에 따라, 우리는 읽기 sscanf를 지시 할 수있다 , 그러나 그것은 쉽게 제거됩니다. 여러분의 편의를 위해

는,이 코드는 ideone로 볼 수 있습니다 : http://ideone.com/4gFlxf#sthash.KQfhcYxr.dpuf

이 예는 거의 scanf와 함께 무엇이 가능한지의 표면을 긁는 다, 나는 그것의 manpage은 더 많은 가능성을 발견하고 탐구하는 것이 좋습니다.

- 평균 점수를 계산하는 방법에

:

#include <stdio.h> 

int main(void) { 
    int inputdata = 24680; 

    int average = 0; 
    int number_digits = 0; 
    int digit = 0; 
    int digits = 0; 

    while (inputdata > 0) { 
     digit = inputdata % 10; // modulo by 10 is the last digit 
     average += digit; 
     digits++; 
     inputdata = inputdata/10; // integer division by 10 = remove last digit 
    } 

    if (digits > 0) { // to avoid dividing by zero is some edge case 
     printf ("The average over %d scores is %.1f\n", digits, (double) average/digits); 
    } else { 
     printf ("As the input was 0, the average is 0"); 
    } 

    return 0; 
} 
+0

부 노트 : 그 정수는 실제로 * 정수가 아닙니다. *; 그들은 4 자릿수의 연속입니다. 그래서 그들은 해체되어야합니다 (상당히 기초적이지만 여전히). 나는 그들이'sscanf'로 4 자리 숫자로 읽힐 수 있다고 생각하지 않습니까? – usr2564301

+0

@Jongware 나는 그 주제에 관한 쪽지를 추가하고 있었다. 자릿수를 int로 변환하면 숫자를 char에서 int로 변환하는 대신 수치로 추출 할 수있다. 어느 IMO도 더 안전하고 편리하다. – fvu

+0

@fvn 미안하지만, 평균 범위를 지금 어떻게 찾을 수 있는지, 그리고 이름 공간을 어떻게 제거 할 수 있는지 이해하지 못합니다. – rel1x

1

가장 쉬운 방법은 긴 학생의 이름을 수 있도록 char name의 크기를 증가시키는 것이다. 그러나 원시 데이터를 struct로 바로 읽는 것은 매우 안전하지 않습니다. 임시 문자열을 사용하여 구문 분석하는 것이 좋습니다.

이 방법을 사용하면 struct student이 더 이상 필요하지 않으며 namechar *이 될 필요가 없습니다. 읽기 버퍼는 각 라인의 모든 데이터를 읽을만큼 커야합니다. 자체적으로 fgets은 '너무 길다'는 줄을 읽을 수는 있지만 (정확히 \n으로 끝나지는 않습니다) 정확한 데이터를 파싱하려면 많은 플래그가 필요합니다. 이 경우 버퍼를 "확실하게 충분히 크게"만드는 것이 더 쉽습니다. 입력의 구조가 비교적 일정한 경우

void student_average_scope(FILE *file) { 
    int i; 
    int sum; 
    char read_buf[128]; /* must be long enough! */ 
    char *name_ptr, *exam_ptr; 

    while (fgets(read_buf, sizeof(read_buf), file) != NULL) 
    { 
    /* find name */ 
    name_ptr = strchr (read_buf, ' '); 
    if (!name_ptr) continue; 
    /* 'name_ptr' now points to last name */ 
    /* we need some trickery to skip zero(!) or more initials ... */ 
    exam_ptr = name_ptr; 
    while (*exam_ptr && !isdigit(*exam_ptr)) 
    { 
     exam_ptr++; 
    } 
    if (!*exam_ptr) continue; 
    /* exam_ptr now points to first digit after name */ 
    exam_ptr[-1] = 0; 

    sum = 0; 
    for (i = 0; i < 4; i++) { 
     sum += exam_ptr[i] - '0'; 
    } 

    printf("%s - %.1f\n", name_ptr, (float)sum/4); 
    } 

    fclose(file); 
} 

출력

@Jongware,는 scanf 가정에 기초하여 용액의 요청
Багров Д. С. - 4.5 
Константинопольский А. А. - 3.2 
Галкин Г. А. - 4.5 
Иванов А. А. - 3.2 
Козлов И. И. - 3.8 
Козлов В. И. - 4.0 
Бобров П. Н. - 4.0 
Шмелев И. И. - 3.8 
+0

나는'exam_ptr [-1] = 0;'이 유효하다고 생각하지 않는다. 나는 이것이 컴파일 될 것이라고 생각하지 않는다. 이건 오타예요? –

+0

그것은, 그렇습니다, 그리고 그것은 (그 순서대로) 아닙니다. 'exam_ptr'는 시작 부분을 넘어서서'read_buf'를 가리킬 것입니다. – usr2564301