savethesavior
savethesavior

Reputation: 9

Search a element in file and print whole line

I have a file with these contents:

Code                Name     Income    Allow      Pens     Ins    Depend    Charity    Taxable       Tax       Net
------------------------------------------------------------------------------------------------------------------------
 008                John     100000     4000      5000    1000      3200       1000      85800     20280     79720
 001                 Doe      50000     4000         0     500      1600          0      43900      7725     42275

I want to print a record if the input code is same as the code in the file.
This is my code:

fscanf(fp, " %3d%s%lf%lf%lf%lf%lf%lf%lf%lf%lf", &code_t, buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t);  
printf("\n");
printf(" 03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
                `

but it doesn't work.
So I'm thinking about using fscanf to read only the code and then print the line which contains that code. But how can I read the code without other contents like Name, Income, ... and how to read if the code has leading 0 like that?

Upvotes: 0

Views: 101

Answers (2)

bruno
bruno

Reputation: 32596

First you cannot read the file with its header using the scanf you give because Code is incompatible with the first %d, so you need to bypass the first 2 lines

Warning a % is missing in your printf to print the code, so the code is considered as the string by %s with an unspecified behavior (a crash generally)

But how can I read the code without other contents like Name, Income

Of course if you use your scanf you also read the other fields, is it a real problem ? You can also read line per line as a strings (fgets or getline) and look at the beginning if you have the expected code and in that case manage the rest of the string to extract the fields if needed etc

An other way if the content of the file is very formatted is to change the file pointer using fseek to only read the codes up to the expected one (see the proposal at the end of my answer).

how to read if the code has leading 0 like that?

I do not understand scanf read it well, this is not octal because there is 008. If the presence of the 0 at the left are important do not manage the code as a number but as a string both in the file and when the code to search is given


A code from yours reading well your input file :

#include <stdio.h>

int bypassLine(FILE * fp)
{
  int c;

  for (;;) {
    c = fgetc(fp);

    if (c == EOF)
      return 0;
    if (c == '\n')
      return 1;
  }
}

int main()
{
  FILE * fp = stdin;
  int code_t;
  char buffer[64];
  double inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t;

  if (!bypassLine(fp) || !bypassLine(fp))
    puts("too short file");
  else {
    while (fscanf(fp, " %3d%s%lf%lf%lf%lf%lf%lf%lf%lf%lf", &code_t, buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t) == 11) {
      printf(" %03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
    }
  }
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra f.c
pi@raspberrypi:/tmp $ cat f
Code                Name     Income    Allow      Pens     Ins    Depend    Charity    Taxable       Tax       Net
------------------------------------------------------------------------------------------------------------------------
008                John     100000     4000      5000    1000      3200       1000      85800     20280     79720
001                 Doe      50000     4000         0     500      1600          0      43900      7725     42275
pi@raspberrypi:/tmp $ ./a.out < f
 008                John     100000     4000      5000    1000      3200       1000      85800     20280     79720
 001                 Doe      50000     4000         0     500      1600          0      43900      7725     42275
pi@raspberrypi:/tmp $ 

Note there is no protection against an overflow when reading the string in scanf with just %s, if is better to use %63s because I sized buffer 64


A little change to search for the code, still using your scanf, giving the name of the file and the expected code in argument :

#include <stdio.h>

int bypassLine(FILE * fp)
{
  int c;

  for (;;) {
    c = fgetc(fp);

    if (c == EOF)
      return 0;
    if (c == '\n')
      return 1;
  }
}

int main(int argc, char ** argv)
{
  if (argc != 3)
    printf("usage : %s <file> <code>\n", *argv);
  else {
    FILE * fp;
    int code_t, expected;
    char buffer[64];
    double inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t;

    if ((fp = fopen(argv[1], "r")) == NULL) {
      fprintf(stderr, "cannot open '%f'\n", argv[1]);
      return -1;
    }

    if (!bypassLine(fp) || !bypassLine(fp)) {
      fprintf(stderr, "too short file '%s'\n", argv[1]);
      fclose(fp);
      return -1;
    }

    if (sscanf(argv[2], "%d%c", &expected, buffer) != 1) {
      fprintf(stderr, "invalid code '%s'\n", argv[2]);
    }
    else {
      while (fscanf(fp, " %3d%63s%lf%lf%lf%lf%lf%lf%lf%lf%lf", &code_t, buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t) == 11) {
        if (code_t == expected) {
          printf(" %03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
          fclose(fp);
          return 0;
        }
      }
      fprintf(stderr, "code %d not found in '%s'\n", expected, argv[1]);
    }
    fclose(fp);
    return -1;
  }
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra f.c
pi@raspberrypi:/tmp $ ./a.out ./f 2
code 2 not found in './f'
pi@raspberrypi:/tmp $ ./a.out ./f 8
 008                John     100000     4000      5000    1000      3200       1000      85800     20280     79720
pi@raspberrypi:/tmp $ 

An other way using fseek to move directly from code to code in the file :

#include <stdio.h>

int bypassLine(FILE * fp)
{
  int c;

  for (;;) {
    c = fgetc(fp);

    if (c == EOF)
      return 0;
    if (c == '\n')
      return 1;
  }
}

int main(int argc, char ** argv)
{
  if (argc != 3)
    printf("usage : %s <file> <code>\n", *argv);
  else {
    FILE * fp;
    int code_t, expected;
    char buffer[64];
    double inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t;

    if ((fp = fopen(argv[1], "r")) == NULL) {
      fprintf(stderr, "cannot open '%f'\n", argv[1]);
      return -1;
    }

    if (!bypassLine(fp) || !bypassLine(fp)) {
      fprintf(stderr, "too short file '%s'\n", argv[1]);
      fclose(fp);
      return -1;
    }

    if (sscanf(argv[2], "%d%c", &expected, buffer) != 1) {
      fprintf(stderr, "invalid code '%s'\n", argv[2]);
    }
    else {
      long offset = ftell(fp);

      while (fscanf(fp, " %03d", &code_t) == 1) {
        if (code_t == expected) {
          /* extract the other fields */
          if (fscanf(fp, "%63s%lf%lf%lf%lf%lf%lf%lf%lf%lf", buffer, &inc_t, &personal, &pension_t, &health_t, &depend_t, &gift_t, &taxable_t, &tax_t, &net_t) == 10) {
            printf(" %03d%20s%11.0lf%9.0lf%10.0lf%8.0lf%10.0lf%11.0lf%11.0lf%10.0lf%10.0lf\n", code_t, buffer, inc_t, personal, pension_t, health_t, depend_t, gift_t, taxable_t, tax_t, net_t);
            fclose(fp);
            return 0;
          }
          else {
            fprintf(stderr, "code %d found but cannot read next fields\n", code_t);
            fclose(fp);
            return -1;
          }
        }
        /* the lines are supposed having all the times 114 characters newline included */
        offset += 114;
        if (fseek(fp, offset, SEEK_SET) == -1) {
          fprintf(stderr, "error when going at offset %d of '%s'\n", offset, argv[1]);
          fclose(fp);
          return -1;
        }
      }
      fprintf(stderr, "code %d not found in '%s'\n", expected, argv[1]);
    }
    fclose(fp);
    return -1;
  }
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra f.c
pi@raspberrypi:/tmp $ cat f
Code                Name     Income    Allow      Pens     Ins    Depend    Charity    Taxable       Tax       Net
------------------------------------------------------------------------------------------------------------------------
008                John     100000     4000      5000    1000      3200       1000      85800     20280     79720
001                 Doe      50000     4000         0     500      1600          0      43900      7725     42275
pi@raspberrypi:/tmp $ ./a.out ./f 8
 008                John     100000     4000      5000    1000      3200       1000      85800     20280     79720
pi@raspberrypi:/tmp $ ./a.out ./f 1
 001                 Doe      50000     4000         0     500      1600          0      43900      7725     42275
pi@raspberrypi:/tmp $ ./a.out ./f 11
code 11 not found in './f'

Upvotes: 1

4386427
4386427

Reputation: 44329

I want to print a record if the input code is same as the code in the file.

If your goal is simply to print the records (aka lines) where "Code" matches some value given by the user, your approach seems a bit too complex as there is no need for scanning all the fields.

Simply use fgets to read the line, then check the Code value and do the print if it matches.

Something like:

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


int main(int argc, char* argv[]){
  if (argc != 2)
  {
    printf("Wrong usage...\n");
    return 1;
  }
  int code_to_print = atoi(argv[1]);
  int code_read;

  FILE* fp = fopen("db.txt", "r");
  if (!fp)
  {
    printf("File error...\n");
    return 1;
  }
  char buf[1024];

  while (fgets(buf, sizeof buf, fp))
  {
    if (sscanf(buf, "%d", &code_read) == 1 && code_read == code_to_print)
    {
      printf("%s", buf);
    }
  }
  fclose(fp);
}

Use the program like:

./prog 8

.. how to read if the code has leading 0 like that?

If the leading zeros are important then you can't scan using %d as that will "remove" the zeros. Instead you need to scan the Code as a word. Like:

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


int main(int argc, char* argv[]){
  if (argc != 2)
  {
    printf("Wrong usage...\n");
    return 1;
  }

  char code_read[4] = {0};

  FILE* fp = fopen("db.txt", "r");
  char buf[1024];

  while (fgets(buf, sizeof buf, fp))
  {
    if (sscanf(buf, "%3s", code_read) == 1 && strcmp(code_read, argv[1]) == 0)
    {
      printf("%s", buf);
    }
  }
  fclose(fp);
}

Use the program like:

./prog 008

Upvotes: 0

Related Questions