I need to sum up the numbers from each line in the file like this e.g.:

1 2 3

10 -1 -3

and the result I should write to another file in each line likes this:

6

6

And I have the problem when in each line after the last number in reading file have more spaces, for example, maybe I use the '_' to show this problem:

When my function works:

10_11_12 '\n'

1_2_3 '\n'

and when my function doesn't work:

10_11_12_ _ _ '\n'

1_2_3 '\n'

I think I know where is the problem, but I have no idea how to fix it. It's my function here:

int num=0;
char s;  
while(fscanf(file, "%d", &num)==1){
   fscanf(file, "%c", &s);
   sum+=num;

   if(s=='\n'){
      fprintf(res_file, "%d\n", sum);
      sum=0;
   }
}

3 Answers

0
Arthur Green On Best Solutions

The problem is that fscanf is expecting a pointer to a char. Within your function, you are using a regular char, s.

char s;  

You can fix your issue by making s a pointer. First, Allocate memory.

char *s = malloc(sizeof(char) + 1);

Now we can properly scan into the variable, s, and then check for the newline character. The only difference here is now we check for the newline by dereferencing s.

if (*s == '\n')

Don't forget to clean up the memory leak with free()!

free(s);

I was able to get the desired output using the code below.

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

int processInputFile(char *filename)
{
    FILE *ifp;
    int buffer = 0;
    char *newline = malloc(sizeof(char) + 1);
    int sum = 0;

    if ((ifp = fopen(filename, "r")) == NULL)
    {
      fprintf(stderr, "Failed to open \"%s \" in processInputFile.\n", filename);
      return -1;
    }

    while(fscanf(ifp, "%d", &buffer) == 1)
    {
        fscanf(ifp, "%c", newline);
        sum += buffer;

        if (*newline == '\n')
        {
            printf("%d\n", sum);
            sum = 0;
        }
    }

    free (newline);
    fclose(ifp);
}


int main(int argc, char **argv)
{
    if (argc < 2)
    {
        printf("Proper syntax: ./a.out <n>\n");
        return -1;
    }

    processInputFile(argv[1]);

    return 0;
}
0
James K. Lowden On

Any kind of line-by-line processing in C is easier done by reading the line first, and then processing it. fgets(3) handles end-of-line for you; then you just need to scan what it read. Plus, in the real world, some lines won't scan: either they'll have errors, or your scan won't be general enough. When that happens, it's awfully handy to write the input to standard error, so you can see what you're looking at.

Here's a complete program that does what you want. It assumes lines are less than 80 bytes long and doesn't protect against invalid input, though.

#include <stdio.h>
#include <err.h>

int main( int argc, char *argv[] ) {
  char line[80];
  static const char *filename = "sum.dat";
  FILE *input;

  if( (input = fopen(filename, "r")) == NULL ) {
    err(1, "could not open %s", filename);
  }

  for( int nlines = 0;
       fgets(line, sizeof(line), input) != NULL;
       nlines++ )
  {
    double value, sum = 0;
    int n;
    for( char *p = line; sscanf(p, "%lf%n", &value, &n) > 0; p += n ) {
      sum += value;
    }
    printf( "line %d: sum = %lf\n", nlines, sum );
  }

  return 0;
}
0
David C. Rankin On

Reading with a line-oriented input function like fgets() or POSIX getline() ensures that a complete line of input is consumed on each call. (don't skimp on buffer size). strtol was created to convert an unknown number of values per-line into long. You walk-a-pointer down your buffer by utilizing the endptr parameter filled by strtol after a successful conversion to point to the next character after the last digit converted.

This allows a simple method to use a pair of pointers, p your start-pointer and ep your end-pointer to work through an entire line converting values as you go. The basic approach is to call strtol, validate it succeeded, and then set p = ep; to advance to the start of your next conversion. strtol ignores leading whitespace.

Putting it altogether, you could do:

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

#define MAXC 1024      /* if you need a constant, #define one (or more) */
                       /* (don't skimp on buffer-size) */

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

    char buf[MAXC];    /* buffer to hold each line read */
    size_t n = 0;      /* line-counter */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {     /* read each line */
        char *p = buf, *ep = p;         /* pointer and end-pointer */
        int sum = 0;                    /* variable to hold sum */

        if (*buf == '\n')               /* ignore empty lines */
            continue;

        while (*p && *p != '\n') {
            errno = 0;
            long tmp = strtol (p, &ep, 0);  /* convert to temp long */
            if (p == ep) {      /* validate digits were converted */
                fputs ("error: no digits extracted.\n", stderr);
                break;
            }
            else if (errno) {   /* validate no under/overflow occurred */
                fputs ("error: underflow/overflow occurred.\n", stderr);
                break;
            }
            else if (tmp < INT_MIN || INT_MAX < tmp) { /* validate in range */
                fputs ("error: tmp exceeds range of int.\n", stderr);
                break;
            }
            sum += tmp;     /* add tmp to sum */
            p = ep;         /* set p to end-ptr (one past last digit used) */
        }
        n++;                /* advance line counter */
        printf ("sum line [%2zu] : %d\n", n, sum);  /* output sum */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

(note: the if (*buf == '\n') which tests if the first character in the line is a newline character and simple skips to the next line, no need to worry about converting values in a empty line)

Example Use/Output

Using your data in dat/sumlines.txt produces the expected results.

$ ./bin/sumline dat/sumlines.txt
sum line [ 1] : 6
sum line [ 2] : 6

Let me know if you have further questions.