0

I am trying to run a simple C program that takes a file of random floating point values, automatically identify the length of the file and use the length to perform further computation. However, my compiler either hangs or I get erroneous results. Here is my code

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

int main() {
  FILE *fptr;
  int count = 0; // Line counter (result)
  char ch; // To store a character read from file

  if ((fptr = fopen("C:\\Users\\Evandovich\\Desktop\\White_Noise.txt", "r"))
      == NULL) {
    printf("Error! opening file");
    // Program exits if the file pointer returns NULL.
    exit(1);
  }

  // Extract characters from file and store in character ch
  for (ch = getc(fptr); ch != EOF; ch = getc(fptr)) {
    if (ch == '\n') // Increment count if this character is newline
      count = count + 1;
  }
  printf("The file has %d lines\n ", count);

  // use the value of "count" to be the length of the array.
  char arrayNum[count];
  char *eptr;
  double result, result1[count];

  for (int i = 0; i < count; i++) {
    fscanf(fptr, "%s", &arrayNum[i]);

    /* Convert the provided value to a double */
    result = strtod(&arrayNum[i], &eptr);
    result1[i] = pow(result, 2);
    printf("value %f\n", result1[i]);
  }

  fclose(fptr);
  return 0;
}

What particularly is the error? Your input is well appreciated.

INPUT file (N.txt) contains

0.137726
0.390126
-0.883234
0.006154
-0.170388
-1.651212
0.510328

OUTPUT The file has 7 files

value 0.000000
value 0.000000
value 0.000000
value 0.000000
value 0.000000
value 0.000000
value 0.000000

Expected The file has 7 files

value 0.018968
value 0.152198
value 0.780102
value 0.000038
value 0.029032
value 2.726501
value 0.260435
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Afro77
  • 3
  • 1
  • 3
    Change the type of `ch` to `int`. This is the return type of `getc` and is necessary to correctly detect `EOF` – Eugene Sh. Jan 06 '23 at 21:47
  • 4
    Well, this certainly is not correct - ` fscanf(fptr,"%s", &arrayNum[i]);` Why are you reading as a string then converting to a double? Just read in as a double to begin with with %f specifier – OldProgrammer Jan 06 '23 at 21:47
  • 1
    Note that `char arrayNum[count];` defines an array of *single characters*, not an array of strings. Not that you need an array of strings, just a plain single character array large enough to hold the largest "word" in your file. – Some programmer dude Jan 06 '23 at 21:49
  • 4
    You are reading from the file after reaching the end. Also use %lf instead of %s. Add rewind(fptr); before the second for loop. – ErlingHaaland Jan 06 '23 at 21:50
  • 1
    Really, you don't need the line-count at all, all you need is something like `double value; while (fscanf(fptr, "%lf", &value) == 1) { printf("value %lf\n", value * value); }` Besides the `fopen` and `fclose` calls that's really all you need in your program. – Some programmer dude Jan 06 '23 at 21:52
  • [Be kind, rewind](https://www.urbandictionary.com/define.php?term=be%20kind%20rewind): call `rewind(fpr)` after finding line count. – chux - Reinstate Monica Jan 06 '23 at 21:52
  • @OldProgrammer: When the scanned value is in the correct format but not representable in a `double`, the behavior of `scanf` is not defined by the C standard. The behavior of `strtod` is. So it is preferable to use `strtod`, although of course it would be better to limit the size with `%s` and to check the error indications provided by `strtod`. – Eric Postpischil Jan 06 '23 at 21:52
  • @EricPostpischil It depends on what kind of error recovery you want. If you need to keep going past erroneous input, reading strings then parsing is appropriate. If you just want to abort when `fscanf()` reports a failure, you can keep it simple. – Barmar Jan 06 '23 at 21:56
  • @Barmar: `fscanf` is not specified to report a failure when the scanned value is not representable in a `double`. As I wrote, it is not defined by the C standard. Whether you want to reliably abort or reliably have any other behavior in that case, you cannot use `fscanf` for it, because the behavior is not defined by the C standard. – Eric Postpischil Jan 06 '23 at 21:59
  • @EricPostpischil It returns the number of successfully scanned values. If the scanned value can't be parsed as a double, won't it return `0`? – Barmar Jan 06 '23 at 22:01
  • 1
    @Barmar: It is not about parsing. There are numerals that are in the correct format for `%lf` but that may be out of the `double` range. The C standard says then the behavior is not defined. (In cases where the `double` format supports infinities, one might hope nothing is out of range, but the standard is not clear about that. And an implementation is allowed not to support infinities.) – Eric Postpischil Jan 06 '23 at 22:02
  • @EricPostpischil That's stupid :) – Barmar Jan 06 '23 at 22:04
  • Thank you guys for your wonderful contributions and suggestions. Those inputs really helped – Afro77 Jan 11 '23 at 00:00

3 Answers3

2

At least these problems:

At end of file

Code fails as it attempts to read the floating point text from the end of the file. @ErlingHaaland

After determining line count, add:

 rewind(fptr);

Convoluted read

Read a line with fgets(). Avoid "%s" without a width limit - it might overflow. Use a line buffer that is based on max line length, not line count. Convert to a double by starting at the begining of the line.

#define LINE_SIZE 100
char arrayNum[LINE_SIZE];
if (fgets(arrayNum, sizeof arrayNum, fptr) == NULL) {
  break;
}
result = strtod(arrayNum, &eptr);

Check conversion

errno = 0;
result = strtod(arrayNum, &eptr);
if (arrayNum == eptr || errno) {
  break;
}

Too small a type

int getc(FILE *) typically returns 257 different values: EOF and [0...UCHAR_MAX]. Saving that in a char loses information. Save in an int.

Line count at risk

May be off by 1 as the last line might not have a '\n': @Adrian McCarthy.

Instead count line beginnings.

size_t count = 0;
int previous = '\n';
int ch;

while ((ch = getc(fptr) != EOF) {
  if (previous == '\n') {
    count++;
  }
  previous = ch;
}
printf("The file has %zu lines.\n ", count);

// Also
rewind(fptr);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

A couple problems:

  1. getc returns the value of the next char as an int or the special value EOF. Since your variable ch is a char, the EOF value may not be recognizable in which case the line counting loop might never end.

  2. You define arrays whose size is determined by a run time variable count. I use C++ regularly, but I have written in C in a very long time. When I did write in C, the size of arrays had to be determined at compile time. Perhaps this is a newer feature of C? I'd check. Make sure you have compiler warnings enabled.

  3. count might be short one line if the last line of the file doesn't end with '\n'. On Posix systems, a line is supposed to end with a newline character. On Windows, it's common for the newline sequence (typically CR+LF) to be treated as a line separator. Thus files may or may not end with the newline sequence at the end of the last line.

  4. arrayNum is an array of count characters rather than an array of pointers to character strings.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • 1
    Variable-length arrays were added in C99; is 23 years ago "new". They became optional in a later version, but all full-featured implementations have them. – Barmar Jan 06 '23 at 22:02
  • @Barmar: Thanks. I learned C in the K&R to ANSI transition and have been almost exclusively C++ since the mid 1990s. I think I'd heard C had added variable length arrays, but I never knew whether that was standard, and I never used it so it hadn't stuck. – Adrian McCarthy Jan 06 '23 at 22:07
0

The fastest, most efficient, and safest way to get file size is to ask the operating system via stat():

struct stat statbuf;
stat("C:\\Users\\Evandovich\\Desktop\\White_Noise.txt", &statbuf);
statbuf.st_size; /* The file size in bytes */
DYZ
  • 55,249
  • 10
  • 64
  • 93