iriteshp
iriteshp

Reputation: 85

read string char and int from file in c programming

student.dat file
----------------
Stu:1 abc ($) - 55  in following order  (Stu: %d %s (%c) - %d)
Stu:2 pqr (^) - 82

I am trying to read this file and save highest grade details in the variable in c programming. my code is below but is not complete!

int main(){

    int num, grade;
    char id, name[35];

    FILE *fp = NULL;

    fp = fopen("student.dat", "r");
    if (fp != NULL) {

        while ((fp != '\n') && (fp != EOF)) {

            fscanf(fp, "%d %s %c %d", &num, name, id, &grade);
            printf("Student Num: %d", num);
            printf("Student Name: %s", name);
            printf("Student id: %c", id);
            printf("Student grade: %d", grade);         

        }

        fclose(fp);
    }else {
        printf("Failed to open file\n");
    }
}

Upvotes: 0

Views: 981

Answers (3)

deamentiaemundi
deamentiaemundi

Reputation: 5525

As user3386109 already hinted at: the format string "Stu: %d %s (%c) - %d" should do it. It actually doesn't, you need to add the newline, too.

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

int main()
{
  int num, grade, ret;
  char id, name[35];
  int lineno = 1;

  FILE *fp = NULL;

  // reset errno, just in case
  errno = 0;

  fp = fopen("student.dat", "r");

  if (fp != NULL) {
    for (;;) {
      ret = fscanf(fp, "Stu: %d %s (%c) - %d\n", &num, name, &id, &grade);
      if (ret != 4 && ret != EOF){
        fprintf(stderr,"fscanf() returned %d instead of 4 for line %d\n",ret,lineno);
        // unlikely, but cheap to check, so check
        if(errno != 0){
           fprintf(stderr,"With error %s\n",strerror(errno));
        }
        exit(EXIT_FAILURE);
      }
      if (ret == EOF) {
        // fscanf() returns EOF for end-of-file _and_ error.
        // check for error first
        if(errno != 0){
           fprintf(stderr,"The error %s occured while reading line %d\n",strerror(errno), lineno);
           exit(EXIT_FAILURE);
        }
        // we are done with the file at this point and can bail out graciously
        break;
      }
      printf("Student Num: %d, ", num);
      printf("Student Name: %s, ", name);
      printf("Student id: %c, ", id);
      printf("Student grade: %d\n", grade);
      lineno++;
    }
    fclose(fp);
  } else {
    printf("Failed to open file: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
  }
  exit(EXIT_SUCCESS);
}

File student.dat generated with

for i in `seq 1 1 100`;do character=$(printf \\$(printf '%03o' $((`shuf -i 40-99 -n 1`))));name=$(cat /dev/urandom | tr -dc 'a-zA-Z' | fold -w 3 | head -n 1); echo Stu:$i $name \($character\) - `shuf -i 10-99 -n 1`;done  > student.dat

(Yes, that generation can be done simpler, I'm pretty sure ;-) )

First 10 lines of input (new-line is \n everywhere):

Stu:1 qim (+) - 13
Stu:2 EcF (L) - 61
Stu:3 Ko1 (Q) - 50
Stu:4 Ve7 (,) - 23
Stu:5 NiX (;) - 28
Stu:6 4O8 (C) - 73
Stu:7 00m (]) - 79
Stu:8 uiw (C) - 45
Stu:9 47k (X) - 80
Stu:10 MmJ (A) - 38

(file ends with new-line \n!)

Upvotes: 1

chex
chex

Reputation: 249

Your while loop have incorrect condition, it'll never become false, File pointer never reaches to \n nor EOF, I had modified your code and now its working properly. Check while condition in code

 int num, grade;
    char id, name[35];

FILE *fp = NULL;

fp = fopen("student.dat", "r");
if (fp != NULL) {

    int ret;
    while((ret = fscanf(fp, "%d %s %c %d", &num, name, &id, &grade))!=EOF)
    {    printf(" Student Num: %d", num);
        printf(" Student Name: %s", name);
        printf(" Student id: %c", id);
        printf(" Student grade: %d\n", grade);
    }

    fclose(fp);
}else {
    printf("Failed to open file\n");
}

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84561

In C, you have 2 primary ways to read line-oriented input and then parse into individual values (really 3, but we will ignore walking a pair of pointers down the string for now).

The preferred manner is to use a line-oriented input function such as fgets or POSIX getline to read an entire line into a buffer, and then parse the buffer with sscanf which can be done in a more flexible manner than a single call to fscanf.

Nonetheless, you appear dedicated to using fscanf here. The key to using fscanf successfully is to provide a format string that accounts for all characters in the line to be read, or to craft the format string to take advantage of properties of the individual format specifiers to accomplish the same thing (e.g. %s (as well as your numerical conversions) will skip leading whitespace giving you some control to deal with line-endings that would otherwise be left in the input-buffer (either the file or stdin and therefore be the next character available on a subsequent call to fscanf, which if not properly handled, will throw a wrench into your read routine.

Another mandatory step is to validate that all conversions specified were successfully completed during each read. You do that by checking the return value for fscanf which is the match count (a count of the number of successful conversions that took place). If you do not check, you cannot have any type of confidence that your values actually hold the data you think they do.

Putting that together, using your input file, and taking the filename to open as the first argument to the program (and reading by default on stdin if no filename is given), you could do something like the following:

#include <stdio.h>

int main (int argc, char **argv) {

    int num =0, grade = 0, max = 0;         /* initialize all variables */
    char id = 0, name[35];
    const char *fmt = " Stu:%d %s (%c) - %d";  /* given format string */
    FILE *fp = NULL;

    if (!(fp = argc > 1 ? fopen (argv[1], "r") : stdin)) {
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }
    /* read each line and validate 4 successful conversions */
    while (fscanf (fp, fmt, &num, name, &id, &grade) == 4) {
        if (grade > max) max = grade;
        printf ("Student Num: %d  Name: %-12s  id: %c  grade: %d\n",
                num, name, id, grade);         
    }
    printf ("\n highest grade : %d\n\n", max);

    if (fp != stdin) fclose (fp);

    return 0;
}

Example Use/Output

$ ./bin/stdntread <dat/stdntread.dat
Student Num: 1  Name: abc           id: $  grade: 55
Student Num: 2  Name: pqr           id: ^  grade: 82

 highest grade : 82

Look over the code, and especially the slight tweak to the format specifier, and let me know if you have any additional questions.

Upvotes: 2

Related Questions