Reading text files with strtol in C line by line - c

OK so I've got this function which finds the average of all numbers in a file:
float averageOfNumbers(FILE *fp_in)
{
int n=0,S=0;
char red[1024];char *ptr;
int p_a_h;
float sr;
while(!feof(fp_in)){
if(fgets(red,1024,fp_in)!=NULL){
ptr =red;
while(p_a_h = strtol(ptr, &ptr, 10)){
if((p_a_h>0&&S>INT_MAX-p_a_h)||(p_a_h<0&&S<INT_MIN-p_a_h)){
printf("OVERFLOW\n");
break;
}
else{
S=p_a_h+S;
n++;}
}
}
}
sr=S/n;
return sr;
}
It works fine when there are only numbers in the file but if it finds anything other than a digit, the program will crash. How can I make it so that the program ignores other symbols. For example here is a text file:
wdadwa 321 1231 das 421124 1 wdasdad 4 1412515
sad14312 yitiyt453534 3554312 sad -2 -53 -12 -231 ##!
#!312 -2 241 -46343 sada 21312 65454
Average should be: 310422

Add an additional check in the if condition.
p_a_h==0 && (strlen(ptr)>1 || (strlen(ptr)==1 && ptr[0]!='0'))
I am making use of the fact that strtol returns 0L if the conversion is invalid(if the string contains non-digit characters). But checking for this alone, also skips if the actual string contains 0. I leave the rest to understand it yourself.

Related

A histogram of the length of words in its input. exercise 1_13, k&r pdf

I have just started learnign to program, and I'm having troubles writing a program from k&r second edition pdf, to write a a program histogram of the length of words in its input I imagined my program would be something like:
(words number)
1 XXX
2 XXXXX
3 XX
4
5 X
12345 (charcacters number)
Here is the code I have done so far:
#include <stdio.h>
#define out 0
#define in 1
int main()
{
char X, nc;
int state, nw, i, x_count[10], c;
i = 0;
nc = 0;
nw = 1;
for (i = 0; i < 10; ++i)
x_count[i] = 0;
while ((c = getchar()) != EOF) {
if (state == in && c != '\b' && c != ' ' && c != '\t')
++nc;
else {
++nw;
state = out;
}
if (state == out) {
for (i = 0; i < nc; i++) {
x_count[i] = X;
}
}
state = in;
}
printf("%d: %c", nw, x_count[i]);
return 0;
}
As pointed out by #kaylum in the comment, the immediate problem that breaks the defined behavior of your code is your use of state before it has been assigned a value in:
if (state == in && ...
state is a variable declared with automatic storage duration. Until the variable state is explicitly assigned a value, its value is indeterminate. Using state while its value is indeterminate results in Undefined Behavior. See: C11 Standard - 6.7.9 Initialization(p10) and J.2 Undefined Behavior
Once you invoke Undefined Behavior in your code, the defined execution is over and your program can do anything between appearing to run correctly or SegFault. See: Undefined, unspecified and implementation-defined behavior
The simple fix is to initialize int state = out; to begin with. (you will start in the out state in order to ignore leading whitespace before the first word)
You have similar problems with your variable X which is not initialized and is used when its value is indeterminate in x_count[i] = X; Moreover, it is unclear what you intend to do with int X to begin with. It is clear from your desired output:
(words number)
1 XXX
2 XXXXX
3 XX
4
5 X
12345 (charcacters number)
That you want to output one 'X' per-character (to indicate the word length for your histogram), but there is no need to store anything in a variable X to do that, you simply need to output one character 'X' for each character in the word. Additionally your output of 4 does not make much sense being empty as your state-variable state should prevent counting empty words. You would never have been in an empty word.
Compounding the confusion is your check for a backspace '\b' character when you check EOF and other whitespace characters for end of word. It looks more likely that you intended a '\n' but though an off-by-one-key typo you have '\b' instead of '\n'. That is conjecture that you will have to add details to clarify...
A Word-Length Histogram
K&R provides very good exercises and the use of a state-loop is a very good place to start. Rather than multiple-included loops to inch-worm over each word and skip over potentially multiple-included whitespace, you simply keep a state-variable state in your case to track whether you are in a word reading characters, or before the first word, between words or after the last word reading whitespace. While you can simply the check for whitespace by including ctype.h and using the isspace() macro, a manual check of multiple whitespace characters is fine.
While defining in and out macros of 1/0 is fine, simply using a variable and assigning 0 for out or non-zero for in works as well. Since you are keeping a character-count to output a length number of 'X' characters, you can just use your character count variable as your state-variable. It will be zero until you read the first character in a word, and then you would reset it to zero after outputting your length number of 'X's to prepare for the next word.
Initializing all variables, and reading either from the filename given as the first argument to the program, or from stdin by default if no argument is given, you can do something similar to:
#include <stdio.h>
int main (int argc, char **argv) {
int cc = 0, /* character count (length) */
wc = 0; /* word count */
/* 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;
}
for (;;) { /* loop continually */
int c = fgetc(fp); /* read character from input stream */
if (c == EOF || c == ' ' || c == '\t' || c == '\n') { /* check EOF or ws */
if (cc) { /* if characters counted */
printf ("%3d : ", wc++); /* output word count */
while (cc--) /* loop char count times */
putchar ('X'); /* output X */
putchar ('\n'); /* output newline */
cc = 0; /* reset char count */
}
if (c == EOF) /* if EOF -- bail */
break;
}
else /* otherwise, normal character */
cc++; /* add to character count */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
(note: the character-count cc variable is used as the state-variable above. You can use an additional variable like state if that is more clear to you, but think through way using cc above accomplishes the same thing. Also note the change and use of '\n' instead of '\b' as the literal backspace character is rarely encountered in normal input, though it can be generated -- while a '\n' is encountered every time the Enter key is pressed. If you actually want to check for teh backspace character, you can add it to the conditional)
Example Input File
$ cat dat/histfile.txt
my dog has fleas
my alligator has none
Example Use/Output
Using a heredoc for input:
$ cat << eof | ./bin/wordlenhist
> my dog has fleas
> my alligator has none
> eof
0 : XX
1 : XXX
2 : XXX
3 : XXXXX
4 : XX
5 : XXXXXXXXX
6 : XXX
7 : XXXX
Redirecting from a file for input:
$ ./bin/wordlenhist < dat/histfile.txt
0 : XX
1 : XXX
2 : XXX
3 : XXXXX
4 : XX
5 : XXXXXXXXX
6 : XXX
7 : XXXX
Or passing the filename as a argument and opening and reading from the file within your program are all options:
$ ./bin/wordlenhist dat/histfile.txt
0 : XX
1 : XXX
2 : XXX
3 : XXXXX
4 : XX
5 : XXXXXXXXX
6 : XXX
7 : XXXX
Lastly, you can input directly on stdin and generate a manual EOF by pressing Ctrl+d on Linux or Ctrl+z on windows. (note: you will have to press the key combination twice -- can you figure out why?) E.g.
$ ./bin/wordlenhist
my dog has fleas my alligator has none 0 : XX
1 : XXX
2 : XXX
3 : XXXXX
4 : XX
5 : XXXXXXXXX
6 : XXX
7 : XXXX
(also note where the first line of output is placed -- this will help you answer the last question)
If you would like to add a comment below and clarify your intent for int X; and x_count[i] = X; and the use of '\b' I'm happy to help further. Look things over and let me know if you have any questions.

Conditional statement in loop checks against variable set when fscanf returns a value different than 1 causes an infinite loop

I'm expecting the loop to end when the end of the file is reached. I know that when it reaches it the value returned from fscanf will be different than 1, as it returns 1 whenever it reads something.
If I set i=3 the loop is infinite, but if I set i=2 the loop ends, which I find very weird, as the controlling expression (i!=3) is supposed to be evaluated before the adjustment one, so when I set i=3, it breaks and should test than indeed i!=3 so the loop would end (which it doesn't). When I set it to 2, it ends, so it must be incrementing it one more time and then checking.
So, my first question is why is this happening?
My second question is that with the %[^\n]s it only saves the start of the file, but with only %s it saves the whole file, but wrongly as it only scans until the space, but I want it to scanf until the new line.
My file has 1 element per line (some with spaces)
for(int i=0;i!=3;i++){
switch(i){
case 0:
if(fscanf(recordsRegistry,"%[^\n]s", (recordsArray[recordsArrayPosition]).author)!=1){
i=3;//stop condition
}
break;
case 1:
if(fscanf(recordsRegistry,"%[^\n]s", (recordsArray[recordsArrayPosition]).title)!=1){
i=3;//stop condition
}
break;
case 2:
if(fscanf(recordsRegistry,"%hu", &((recordsArray[recordsArrayPosition]).numberOfSales))!=1){
i=3;//stop condition
}
i=-1;
recordsArrayPosition++;
totalRecords++;
recordsArray=realloc(recordsArray, totalRecords*recordStructSize) ;
if(recordsArray==NULL){
fprintf(stderr, "Could not reallocate memory at line %d.\n", __LINE__);
return 3;
}
break;
}
}
Example of the file being read:
LEANN RIMES
WHAT A WONDERFUL WORLD
4628
BLUE CHEER
WHAT DOESNT KILL YOU
9664
WITHIN TEMPTATION & KEITH CAPUTO
WHAT HAVE YOU DONE
3226
WITHIN TEMPATION
WHAT HAVE YOU DONE
8093
KOKO TAYLOR
WHAT IT TAKES (THE CHESS YEARS)
7160
DOOBIE BROTHERS
WHAT WERE ONCE VICES ARE NOW HABITS
2972
LIL'ED & THE BLUES IMPERIALS
WHAT YOU SEE IS WHAT YOU GET
9443
VARIOUS ARTISTS
WHAT'S SHAKIN
4473
The struct:
typedef struct{
char author[20], title[50];
short unsigned int numberOfSales;
} RECORD;
New for looop:
for(int i=0;i!=3;i++){
switch(i){
case 0:
if(fgets(recordsArray[recordsArrayPosition].author, totalRecords, recordsRegistry)==NULL){
//printf("aa\n");
i=2;//stop condition
}
break;
case 1:
if(fgets(recordsArray[recordsArrayPosition].title, totalRecords, recordsRegistry)==NULL){
//printf("aaa\n");
i=2;//stop condition
}
break;
case 2:
if(fscanf(recordsRegistry,"%hu", &((recordsArray[recordsArrayPosition]).numberOfSales))!=1){
//printf("aaaa\n");
i=2;//stop condition
}
i=-1;
recordsArrayPosition++;
totalRecords++;
recordsArray=realloc(recordsArray, totalRecords*recordStructSize) ;
if(recordsArray==NULL){
fprintf(stderr, "Could not reallocate memory at line %d.\n", __LINE__);
return 3;
}
break;
}
}
... when fscanf returns a value different than 1 causes an infinite loop
when you set i to 3 that value will not be tested in i!=3 because before the test the i++ will be done
set i to 2
with only %s ... it only scans until the space
I want it to scanf until the new line.
if you want to read line per line use fgets rather than fscanf, do not forget to remove the probable newline
in the scanf family 's' matches a sequence of non-white-space characters, spaces are separator
man scanf says :
s Matches a sequence of non-white-space characters; the next
pointer must be a pointer to the initial element of a character
array that is long enough to hold the input sequence and the
terminating null byte ('\0'), which is added automatically. The
input string stops at white space or at the maximum field width,
whichever occurs first.
warning you mix to read line and value, when you read the value the newline is not read, replace "%hu" by "%hu\n" or much secure read the line then extract the number from it (I do that in my proposal)
from your remark
why will i++ test before i!=3?
your :
for(int i=0;i!=3;i++){
<body without continue>
}
is equivalent to
{ int i = 0;
while (i != 3) {
<body without continue>
i++;
}
}
Here a proposal :
#include <stdio.h>
#include <ctype.h>
#include <string.h>
typedef struct{
char author[20], title[50];
short unsigned int numberOfSales;
} RECORD;
#define MAXRECORDS 100
void removeEndSpaces(char * s)
{
char * p = s + strlen(s);
while ((s != p) && isspace((unsigned char) *--p))
*p = 0;
}
int main()
{
FILE * fp = fopen("f", "r");
RECORD records[MAXRECORDS];
int nrecords;
char line[32];
if (fp == NULL){
perror("cannot read f");
return -1;
}
for (nrecords = 0; nrecords != MAXRECORDS; nrecords += 1) {
if (fgets(records[nrecords].author, sizeof(records[nrecords].author), fp) == NULL)
break;
removeEndSpaces(records[nrecords].author);
if (fgets(records[nrecords].title, sizeof(records[nrecords].title), fp) == NULL) {
fprintf(stderr, "invalid input file\n");
break;
}
removeEndSpaces(records[nrecords].title);
/* the more secure way to read the number is first to read the line then read the enumber in that line */
if ((fgets(line, sizeof(line), fp) == NULL) ||
(sscanf(line, "%hu", &records[nrecords].numberOfSales) != 1)) {
fprintf(stderr, "invalid input file\n");
break;
}
}
/* nrecords values the number of records read without error */
for (int i = 0; i != nrecords; i += 1)
printf("%s : %s / %hu\n",
records[i].author, records[i].title, records[i].numberOfSales);
return 0;
}
As you see it is useless to do your stuff with the index and the code is more clear
Supposing the file f contains your input, compilation and execution :
pi#raspberrypi:/tmp $ gcc -Wall -Werror -pedantic a.c -g
pi#raspberrypi:/tmp $ ./a.out
invalid input file
LEANN RIMES : WHAT A WONDERFUL WORLD / 4628
BLUE CHEER : WHAT DOESNT KILL YOU / 9664
pi#raspberrypi:/tmp $
As you see the file is invalid, the reason is the author "WITHIN TEMPTATION & KEITH CAPUTO" more the newline is too long to be saved in 20 characters, this is why you always need to check what happens and never suppose all is ok : in your initial code out of your other problems fscanf write out of the items with an undefined behavior. To read for instance up to 20 characters including the null character in a string with (f/s)scanf use the format "%20s"
If I resize the field author to 40 all is ok :
pi#raspberrypi:/tmp $ gcc -Wall -Werror -pedantic a.c -g
pi#raspberrypi:/tmp $ ./a.out
LEANN RIMES : WHAT A WONDERFUL WORLD / 4628
BLUE CHEER : WHAT DOESNT KILL YOU / 9664
WITHIN TEMPTATION & KEITH CAPUTO : WHAT HAVE YOU DONE / 3226
WITHIN TEMPATION : WHAT HAVE YOU DONE / 8093
KOKO TAYLOR : WHAT IT TAKES (THE CHESS YEARS) / 7160
DOOBIE BROTHERS : WHAT WERE ONCE VICES ARE NOW HABITS / 2972
LIL'ED & THE BLUES IMPERIALS : WHAT YOU SEE IS WHAT YOU GET / 9443
VARIOUS ARTISTS : WHAT'S SHAKIN / 4473
pi#raspberrypi:/tmp $

Leading Zeroes not showing up in c [duplicate]

This question already has answers here:
Extra leading zeros when printing float using printf?
(2 answers)
Closed 5 years ago.
I am trying recreate a sample LC-3 simulator as an assignment, and part of that is to have a 4 digit integer. My code is as follows:
while (read_success != NULL && !done) {
// If the line of input begins with an integer, treat
// it as the memory value to read in. Ignore junk
// after the number and ignore blank lines and lines
// that don't begin with a number.
//
words_read = sscanf(buffer, "%04d", &value_read);
// if an integer was actually read in, then
// set memory value at current location to
// value_read and increment location. Exceptions: If
// loc is out of range, complain and quit the loop. If
// value_read is outside -9999...9999, then it's a
// sentinel -- we should say so and quit the loop.
if (value_read < -9999 || value_read > 9999)
{
printf("Sentinel read in place of Memory location %d: quitting loop\n", loc);
break;
}
else if (value_read >= -9999 && value_read <= 9999)
{
cpu -> mem[loc] = value_read;
printf("Memory location: %02d set to %04d \n", loc, value_read);
cpu -> count++;
loc++;
value_read = NULL;
}
if (loc > 99)
{
printf("Reached Memory limit, quitting loop.\n", loc);
break;
}
read_success = fgets(buffer, DATA_BUFFER_LEN, datafile);
// Gets next line and continues the loop
}
fclose(datafile);
I am reading values from an sdc file with the following values:
1234
3456
-4567;
2353
3434
654
0345
7655
555
9999
10000
The problem is that 0345 shows up as 345, i want 645 to be 0645, and so on.
I tried formatting %d based on a post I saw related to this, but it is not working. Any professional insight?
Edit: I did use %04d to start, but that did not work.
If you want leading zeros to be displayed, use %04d in your printf format.
The 0 is a flag used with d (among others) that says to pad on the left with zeros.

C reading file using ./a.out<filename and how to stop reading

In my class today we were assigned a project that involves reading in a file using the ./a.out"<"filename command. The contents of the file look like this
16915 46.25 32 32
10492 34.05 56 52
10027 98.53 94 44
13926 32.94 19 65
15736 87.67 5 1
16429 31.00 58 25
15123 49.93 65 38
19802 37.89 10 20
-1
but larger
My issue is that any scanf used afterwards is completely ignored and just scans in what looks like garbage when printed out, rather than taking in user input. In my actual program this is causing an issue with a menu that requires input.
How do I get the program to stop reading the file provided by the ./a.out"<"filename command?
also I stop searching at -1 rather than EOF for the sake of not having an extra set of array data starting with -1
ex
-1 0 0 0
in my real program the class size is a constant that is adjustable and is used to calculate class averages, I'd rather not have a set of 0's skewing that data.
#include <stdio.h>
int main(void)
{
int i = 0,j = 1,d,euid[200],num;
int tester = 0;
float hw[200],ex1[200],ex2[200];
while(j)
{
scanf("%d",&tester);
if( tester == -1)
{
j = 0;
}
else
{
euid[i] = tester;
}
scanf("%f",hw+i);
scanf("%f",ex1+i);
scanf("%f",ex2+i);
i++;
}
for(d = 0;d < 50;d++) /*50 because the actual file size contains much more than example*/
{
printf("euid = %d\n",euid[d]);
printf("hw = %f\n",hw[d]);
printf("ex1 = %f\n",ex1[d]);
printf("ex2 = %f\n",ex2[d]);
}
printf("input something user\n");
scanf("%d",&num);
printf("This is what is being printed out -> %d\n",num);
return 0;
}
I'm having the exact same problem. Tried every method I could find to eat the remaining input in the buffer, but it never ends.
Got it to work using fopen and fscanf, but the prof. said he prefers the code using a.out < filename
Turns out this is in fact not possible.

Use fscanf to read two lines of integers

I want to ask something that I write in C.
I use the fopen() command to open and read a text file that contains only two lines. in
first line is an integer N number, and in the second line is the N integer numbers that the first line says.
Eg.
-------------- nubmers.txt --------------
8 <-- we want 8 numbers for the 2nd line
16 8 96 46 8 213 5 16 <-- and we have 8 numbers! :)
but I want to take restrictions when the file openend.
the number N should be between 1 ≤ Ν ≤ 1.000.000. If not then show an error message. If the file is ok then the programm continue to run with another code.
Here is what I done until now:
int num;
....
fscanf(fp,"%d",&num); // here goes the fscanf() command
if(num<1 || num>1000000) // set restrictions to integer
{
printf("The number must be 1<= N <= 1.000.000",strerror(errno)); // error with the integer number
getchar(); // wait the user press a key
return 0; // returning an int of 0, exit the program
}
else // if everything works.....
{
printf("work until now"); // Everything works until now! :)
getchar(); // wait the user press a key
return 0; // returning an int of 0, exit the program
}
But the problem is that the restriction checks only for the first line number , it's correct though, but don't read the numbers in the second line.
What I mean is that :
Lets say that I have the number 10 in the first line.
The code will analyze the number, will check for restrictions and will proceed to the 'else' part
else // if everything works.....
{
printf("work until now"); // Everything works until now! :)
getchar(); // wait the user press a key
return 0; // returning an int of 0, exit the program
}
..and it will said that everything is working.
But what if I have 20 numbers in the second line? -when I need only 10
Eg.
-------------- nubmers.txt --------------
10
16 8 96 46 8 213 5 16 8 9 21 5 69 64 58 10 1 7 3 6
So I hoped be as cleared as I could. My question is that I need a code in the program, besides the 1st restriction, that have also another one restriction under the first that will read the second line of the txt file with the numbers and check if there are as many numbers as the first line says!
How do I do that?
If you guys want any other declarations feel free to ask!
Hope I was clear with my problem :)
This will check the number of integers and report too many or not enough. The integers are not saved except for each one being read into the value. Do you want to store each integer?
fscanf(fp,"%d",&num); // here goes the fscanf() command
if(num<1 || num>1000000) // set restrictions to integer
{
printf("The number must be 1<= N <= 1.000.000",strerror(errno)); // error with the integer number
getchar(); // wait the user press a key
return 0; // returning an int of 0, exit the program
}
else // if everything works.....
{
int i = 0;
int value = 0;
while ( fscanf ( fp, "%d", &value) == 1) { // read one integer
i++; // this loop will continue until EOF or non-integer input
}
if ( i > num) {
printf ( "too many integers\n");
}
if ( i < num) {
printf ( "not enough integers\n");
}
getchar(); // wait the user press a key
return 0; // returning an int of 0, exit the program
}
use a loop that takes the first num and checks is is the number of integers in next line:
int z = num;
while(z--){
if (getchar() == EOF)
printf("err")
}
Do it like this:
fscanf(fp,"%d",&num);
// next lines of code (restrictions). Then place the below code before getchar in the else
int temp[num+1];// space to store num integers to temp and 1 space to check for extra number
for(i=0;i<num;i++)
{
if(fscanf(fp,"%d",&temp[i]) != 1)// fscanf will automatically read 2nd line and store them in temp array
//error ! Less numbers in file !
}
if(fscanf(fp,"%d",&temp[num]==1) //if still numbers can be scanned
//Extra numbers found in line 2

Resources