This is my first post here. I am making a program in C that handles Joker results (it's a game like Powerball). Below I include only the code that matters for my question. First, you input 1 to the program so that it reads the previous results file. I will include the file so that you can run it as well.Afterwards you input 3 so that you insert a result in the form of
id;day/month/year;num1;num2;num3;num4;num5;joker
After that you input 99 and you see the full result array.
The problem is that the first 2 results that are appended to the array(resArr) are displayed properly, but all the following appends are stored with pseudorandom numbers. Any clue why my code works only for 2 repetitions?
The file: link
#include <stdio.h>
#include <stdlib.h>
typedef struct results
{
int id,date[3],num[5],joker;
}Results;
Results *read()
{
FILE *fp=fopen("joker.csv","r");
Results *temp=(Results *)malloc(sizeof(Results));
Results *result=(Results *)malloc(sizeof(Results));
int i=0,size=1;
while(!feof(fp))
{
char *s=(char *)malloc(50*sizeof(char));
fgets(s,50,fp);
sscanf(s,"%d;%d/%d/%d;%d;%d;%d;%d;%d;%d",&result[i].id,&result[i].date[0],&result[i].date[1],&result[i].date[2],&result[i].num[0],&result[i].num[1],&result[i].num[2],&result[i].num[3],&result[i].num[4],&result[i].joker);
temp=(Results *)realloc(result,(++size)*sizeof(Results));
if (temp) result=temp;
else
{
result=NULL;
break;
}
i++;
}
fclose(fp);
return result;
}
int findLength()
{
FILE *fp=fopen("joker.csv","r");
int len,i=0;
while(!feof(fp))
{
char *s=(char *)malloc(50*sizeof(char));
fgets(s,50,fp);
i++;
}
fclose(fp);
len=i-1;
return len;
}
void eisagogi(Results *resArr,int *len)
{
Results result;
printf("id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker\n");
scanf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d",&result.id,&result.date[0],&result.date[1],&result.date[2],&result.num[0],&result.num[1],&result.num[2],&result.num[3],&result.num[4],&result.joker);
resArr=(Results *)realloc(resArr,(*len+1)*sizeof(Results));
resArr[*len]=result;
*len=*len+1;
}
void showResults(Results *resArr,int len)
{
int i;
for (i=0;i<len;i++)
{ printf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d\n",resArr[i].id,resArr[i].date[0],resArr[i].date[1],resArr[i].date[2],resArr[i].num[0],resArr[i].num[1],resArr[i].num[2],resArr[i].num[3],resArr[i].num[4],resArr[i].joker);
}
}
int menuChoose()
{
int choice;
printf("Load results 1\n");
printf("Append result 3\n");
printf("Result array 99\n");
printf("Exit 0\n");
scanf("%d",&choice);
return choice;
}
int main()
{
Results *resArr=(Results *)malloc(sizeof(Results));
int choice,len;
while(1)
{
choice=menuChoose();
switch(choice)
{
case 1:
resArr=read();
len=findLength();
break;
case 3:
eisagogi(resArr,&len);
break;
case 99:
showResults(resArr,len);
break;
case 0:
exit(0);
break;
}
}
return 0;
}
I changed your feof to fgets
char s[50];
while (fgets(s, sizeof(s), fp) != 0) {
and now it seems that you can add 3 results and display them.
#include <stdio.h>
#include <stdlib.h>
typedef struct results {
int id, date[3], num[5], joker;
} Results;
Results *read() {
FILE *fp = fopen("joker.csv", "r");
Results *temp = (Results *) malloc(sizeof(Results));
Results *result = (Results *) malloc(sizeof(Results));
int i = 0, size = 1;
char s[50];
while (fgets(s, sizeof(s), fp) != 0) {
sscanf(s, "%d;%d/%d/%d;%d;%d;%d;%d;%d;%d", &result[i].id, &result[i].date[0], &result[i].date[1],
&result[i].date[2], &result[i].num[0], &result[i].num[1], &result[i].num[2], &result[i].num[3],
&result[i].num[4], &result[i].joker);
temp = (Results *) realloc(result, (++size) * sizeof(Results));
if (temp) result = temp;
else {
result = NULL;
break;
}
i++;
}
fclose(fp);
return result;
}
int findLength() {
FILE *fp = fopen("joker.csv", "r");
int len, i = 0;
while (!feof(fp)) {
char *s = (char *) malloc(50 * sizeof(char));
fgets(s, 50, fp);
i++;
}
fclose(fp);
len = i - 1;
return len;
}
void eisagogi(Results *resArr, int *len) {
Results result;
printf("id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker\n");
scanf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d", &result.id, &result.date[0], &result.date[1], &result.date[2],
&result.num[0], &result.num[1], &result.num[2], &result.num[3], &result.num[4], &result.joker);
resArr = (Results *) realloc(resArr, (*len + 1) * sizeof(Results));
resArr[*len] = result;
*len = *len + 1;
}
void showResults(Results *resArr, int len) {
int i;
for (i = 0; i < len; i++) {
printf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d\n", resArr[i].id, resArr[i].date[0], resArr[i].date[1], resArr[i].date[2],
resArr[i].num[0], resArr[i].num[1], resArr[i].num[2], resArr[i].num[3], resArr[i].num[4],
resArr[i].joker);
}
}
int menuChoose() {
int choice;
printf("Load results 1\n");
printf("Append result 3\n");
printf("Result array 99\n");
printf("Exit 0\n");
scanf("%d", &choice);
return choice;
}
int main() {
Results *resArr = (Results *) malloc(sizeof(Results));
int choice, len;
while (1) {
choice = menuChoose();
switch (choice) {
case 1:
resArr = read();
len = findLength();
break;
case 3:
eisagogi(resArr, &len);
break;
case 99:
showResults(resArr, len);
break;
case 0:
exit(0);
break;
}
}
return 0;
}
Test
Load results 1
Append result 3
Result array 99
Exit 0
3
id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker
1768;18/12/2016;11;28;5;9;31;1
Load results 1
Append result 3
Result array 99
Exit 0
3
id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker
1769;18/12/2016;11;28;5;9;31;2
Load results 1
Append result 3
Result array 99
Exit 0
3
id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker
1770;18/12/2016;11;28;5;9;31;3
Load results 1
Append result 3
Result array 99
Exit 0
1
...
1768;18/12/2016;11;28;5;9;31;1
1769;18/12/2016;11;28;5;9;31;2
1770;18/12/2016;11;28;5;9;31;3
Load results 1
Append result 3
Result array 99
Exit 0
Your I/O strategy is one part fragile and one part flat wrong.
In the first place, while(!feof(fp)) is always wrong. As the answers to the linked question explain in some detail, the feof() function reports on whether end-of-file has already been detected on the specified stream. It cannot report on whether the next attempt to read will encounter EOF, so you need to watch for that on each read. In this case, you would want to verify that fgets() does not return NULL, as #DacSaunders already recommended:
char s[50];
while (fgets(s, sizeof(s), fp) != NULL) {
...
(The integer literal 0 also serves as a null pointer constant, but I find it clearer to use NULL.) Note also that Dac changed your s to an automatic array instead of having it dynamically allocated. That's not only easier to manage, but it cleans up the memory leak that your original code exhibited from dynamically allocating 50 bytes for each line and never freeing them. That also allows you to use sizeof(s) to get the capacity of s -- that works for arrays, but not for pointers.
In the second place, you do not check your inputs.
You should verify that each fgets() in fact reads a whole line (as can be determined by looking for a newline in the data read), for the next read will pick up where the last left off, and that could be in the middle of a line.
You should verify that each sscanf() in fact matches every expected field (by checking its return value), for if it does not do so then some or even all of the fields you are trying to populate will be uninitialized. You might have such a failure if the input is malformed, or even if an input line is just too long.
Bonus tip: you have another memory leak, in that you allocate memory for temp in its declaration, but you never free it, nor even use it. Just because you declare a pointer does not mean you have to allocate memory.
Thanks a lot! I repeated Dac's change in findLength as well and I also used temp() to free temp in read().
Was this bug happening because of memory leaks?
Related
I am writing a program in c that reads in text from a text file then randomly selects words from the file and if the words are greater than or equal to six it appends the words together, removes the spaces, and finally prints the new word. (I am using the redirect on linux "<" to read in the file)
Example input: "cheese and crackers"
New word should be: cheesecrackers
Here is the code:
int main (void)
{
int ch;
char *ptrChFromFile;
int strSize = 1;
int i;
int numberOfWords = 1;
ptrChFromFile = malloc (sizeof (char));
if (ptrChFromFile == NULL) {
puts ("COULDN'T ALLOICATE MEMORY");
exit (EXIT_FAILURE);
}
while ((ch = getchar ()) != EOF) {
ptrChFromFile =
realloc (ptrChFromFile, (strSize + 1) * sizeof (char));
if (ptrChFromFile == NULL) {
puts ("failed to allocate memory");
exit (EXIT_FAILURE);
}
if (ch == ' ') {
numberOfWords++;
}
ptrChFromFile[strSize] = ch;
strSize++;
}
ptrChFromFile[strSize] = 0;
char **ptrWords = malloc (sizeof (char *) * strSize);
for (i = 0; i < strSize; i++) {
if (ptrChFromFile[i] != ' ') {
ptrWords[i] = &ptrChFromFile[i];
}
else {
ptrWords[i] = 0;
}
}
free (ptrChFromFile);
free (ptrWords);
return 0;
}
The things that I am struggling with are:
1) Am I allocating the correct memory size for the pointers?
2) How can I parse each word by space without using any special methods from the string.h library (like strtok). Then how do I store those words in the pointer *ptrWords?
so ptrWords should look like this:
cheese | and | crackers
0 1 2
Then I want to loop through ptrWords and check if the length of each word in the pointer is greater than or equal to six. If they are store them in the pointer ptrOutputWord.
so then ptrOutputWord should look like this:
cheese | crackers
0 1
Finally, I want to print the values in ptrOutputWord as one word without spaces.
I tried to explain what I want to do exactly. Thank you to anyone that can help in advance.
EDIT: I changed the code to reflect only the piece that should read in the characters, and reallocate the size of the pointer by one each time a new character is read in, but the right amount of memory isn't being allocated.
You have a few issues:
#include <stdio.h>
#include <time.h>
Why this header?
#include <stdlib.h>
int main()
{
char ch, *ptrChFromFile;
int strSize;
This variable needs to have a useful start value.
ptrWordsFromFile = (char*)malloc(sizeof(char));
No need to cast.
if(ptrChFromFile == NULL)
{
puts("COULDN'T ALLOICATE MEMORY");
exit(EXIT_FAILURE);
}
while((ch = getchar()) != EOF)
getchar returns and int, not a char.
{
ptrChFromFile = (char*)realloc(ptrChFromFile, strSize * sizeof(char)+1);
We need one more character than before and extra space for the 0.
You should add the +2 (not +1) to the number of elements: (strSize+2) * sizeof(<any type>)
Normally you should not directly assign the result of realloc to the same pointer. In case it fails, you lose your old pointer value. Again: No cast needed.
if(ptrChFromFile == NULL)
{puts("failed to alloicate memory");}
If it fails, you cannot continue! Exit from the program just as above
*ptrChFromFile = ch;
You put the character to the start of the enlarged buffer. You should add at the end.
strSize++;
}
Now you have a bunch of characters in memory but no termination for the string.
free(ptrChFromFile);
return 0;
}
After fixing it it looks like this:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch;
char *ptrChFromFile;
int strSize = 0;
ptrWordsFromFile = malloc(sizeof(char));
if (ptrChFromFile == NULL)
{
puts("COULDN'T ALLOICATE MEMORY");
exit(EXIT_FAILURE);
}
while ((ch = getchar()) != EOF)
{
ptrChFromFile = realloc(ptrChFromFile, (strSize+2) * sizeof(char));
if (ptrChFromFile == NULL)
{
puts("failed to allocate memory");
exit(EXIT_FAILURE);
}
ptrChFromFile[strSize] = ch;
strSize++;
}
ptrChFromFile[strSize] = 0;
// Now add detection and storing of separate words
// (You might omit storing words that are too short)
// Select random words and add together.
free(ptrChFromFile);
return 0;
}
I had a program to check user input and make sure it's only integer and not character. In my main function, do while loop only executes once when the input is incorrect. But I want it to keep it executing until the user enter a valid input. My doAgain() function is to ask if the user want to Try again or not. The problem is with doAgain() function. It only executes once if leave it in the if statement. Everything works fine except this glitch. However, when i remove it, the loop keep executing until the user enter valid input like i wanted it to, but then doAgain() function would be useless
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* get boundary */
char* getBoundary(char str[]){
int i;
char c;
str = (char*) malloc(sizeof(char));
for (i = 0; (c = getchar()) != '\n'; i++) // The loop stop running after the second time
{
str = (char *) realloc(str, sizeof(char) + i);
str[i] = c;
}
str[i] = '\0';
return str;
}
/* check for valid string */
int checkStr(const char *check)
{
unsigned i;
size_t len = strlen(check);
for (i = 0; i < len; i++)
if(isalpha(check[i]))
{
printf("Invalid integer formatt!!!");
return 0;
}
return 1;
}
/* Ask if do again */
int doAgain(void)
{
char ans, c;
do {
printf("Do you want to try again?: ");
scanf(" %c", &ans);
switch (ans)
{
case 'y':
case 'Y':
case 'n':
case 'N':
return (ans == 'y') || (ans == 'Y') ? 1 : 0;
break;
default:
printf("Invalid answer!!! answer 'y' and 'Y' or 'n' and 'N' only\n");
do { /* flush input stream */
c = getchar();
}while (c != '\n');
}
}while (1);
}
/* Main */
int main(void)
{
char *l_boundRow;
l_boundRow = NULL;
do {
printf("Enter lower bound row: ");
l_boundRow = getBoundary(l_boundRow);
if (!checkStr(l_boundRow) && doAgain()) // problem start here, it works if I remove doAgain() function
continue; // if the string is invalid, the program asks user if they want to try again
else
break;
}while (1);
free(l_boundRow);
return 0;
}
Revised answer
The immediate problem is that when doAgain() exits with y or n, it doesn't read the newline after those characters, so when it re-enters getBoundary(), the first character it reads is whatever was after the y or n which was probably a newline, which terminates the input line. You need to gobble the rest of the line on a valid input as well as on the invalid ones.
This code mostly works — it is leak free, too (at least under my casual testing).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char* getBoundary(void);
int checkStr(const char *check);
int doAgain(void);
/* get boundary */
char* getBoundary(void)
{
int i;
int c;
char *str = (char*) malloc(sizeof(char));
for (i = 0; (c = getchar()) != '\n' && c != EOF; i++)
{
str = (char *) realloc(str, 2 + i);
str[i] = c;
}
str[i] = '\0';
return str;
}
/* check for valid string */
int checkStr(const char *check)
{
unsigned i;
size_t len = strlen(check);
for (i = 0; i < len; i++)
{
if (!isdigit(check[i]))
{
printf("Invalid integer format (%s)!!!\n", check);
return 0;
}
}
return 1;
}
static int gobble(void)
{
int c;
while ((c = getchar()) != EOF && c != '\n')
;
return c;
}
/* Ask if do again */
int doAgain(void)
{
char ans;
int c;
do {
printf("Do you want to try again?: ");
scanf(" %c", &ans);
switch (ans)
{
case 'y':
case 'Y':
c = gobble();
return 1;
case 'n':
case 'N':
c = gobble();
return 0;
default:
{
printf("Invalid answer!!! answer 'y' and 'Y' or 'n' and 'N' only\n");
c = gobble();
if (c == EOF)
{
printf("EOF detected\n");
return 0;
}
}
}
} while (1);
}
/* Main */
int main(void)
{
char *l_boundRow;
l_boundRow = NULL;
do {
printf("Enter lower bound row: ");
l_boundRow = getBoundary();
if (checkStr(l_boundRow))
break;
if (!doAgain())
break;
free(l_boundRow);
}while (1);
printf("Final bound row: %s\n", l_boundRow);
free(l_boundRow);
return 0;
}
If you select not to try again after an invalid input, the last invalid value is printed as the 'Final bound row'. You can easily hack the code to avoid that problem.
Incidentally, when I first compiled your code, I only got 3 warnings from it under my default stringent options — because I demand prototypes before (non-static) function definitions. That's extremely good; well done. Few people write code that's posted on SO that passes that level of scrutiny with as few complaints.
If it were my code, I would have few if any do … while loops (none in this code). They are occasionally useful, but occasionally is the operative term. Generally, it is best to use a top-testing while loop, or an explicit for loop.
Original answer
A real problem, but not the one immediately causing trouble.
In the code in getBoundary(), you first allocate one character. Then, in the body of the loop, you reallocate i + 1 characters. On the first iteration, you reallocate 1 byte; then 2, etc. And then when you exit the loop, you write one beyond the last character that was allocated, which leads to undefined behaviour. You need to use i + 2 as the size to reallocate. (There are those who'd rail against you for using sizeof(char) since that is guaranteed to be 1.)
That is probably the source of your trouble; writing beyond the end of an allocated buffer can easily lead to crashes.
If you ran the code under valgrind, it would tell you about this mistake.
Separately, it is not a good idea to allocate one more byte each time around the loop. It would be better to allocate, say, 20 bytes (big enough to hold any 64-bit integer value), or to double the size on each iteration when you need more space. It isn't going to be time critical in this context, but it can become a problem in bigger programs.
Note too that your checkstr() function only detects alpha characters; punctuation and control characters will also not convert to an integer. You should check that each character is a digit (isdigit(check[i])), and you might have to worry about plain char being signed — so isdigit((unsigned char)check[i]) is better still. Similar comments apply to the other isuvwxyz() functions.
In doAgain(), you should use int c; instead of char c; and you should check for EOF as well as newline. If you detect EOF, the answer is 'no' and you should return that.
Also, in your getBoundary() function again, you have:
str = (char *) realloc(str, sizeof(char) + i);
There are those who would castigate you for the cast; I am not of the mindset that does that. But be aware that you will get criticism for doing so from many people who answer questions on C at SO.
More significantly, you should not write the realloc() code this way. The idiom:
ptr = realloc(ptr, new_size);
leaks memory if the allocation fails. You've just had the only pointer to the memory wiped out with NULL, even though realloc() promises that it did not free the old memory. You should use:
void *new_ptr = realloc(ptr, new_size);
if (new_ptr == NULL)
…handle out of memory condition…ptr is still valid!
ptr = new_ptr;
You should also always check that memory allocations succeed. If they fail, you end up dereferencing a null pointer, which leads to crashes.
I'm very new to C and I'm still learning the basics. I'm creating an application that reads in a text file and breaks down the words individually. My intention will be to count the amount of times each word occurs.
Anyway, the last do-while loop in the code below executes fine, and then crashes. This loop prints memory address to this word (pointer) and then prints the word. It accomplishes this fine, and then crashes on the last iteration. My intention is to push this memory address into a singly linked list, albeit once it's stopped crashing.
Also, just a quick mention regarding the array sizes below; I yet figured out how to set the correct size needed to hold the word character array etc because you must define the size before the array is filled, and I don't know how to do this. Hence why I've set them to 1024.
#include<stdio.h>
#include<string.h>
int main (int argc, char **argv) {
FILE * pFile;
int c;
int n = 0;
char *wp;
char wordArray[1024];
char delims[] = " "; // delims spaces in the word array.
char *result = NULL;
result = strtok(wordArray, delims);
char holder[1024];
pFile=fopen (argv[1],"r");
if (pFile == NULL) perror ("Error opening file");
else {
do {
c = fgetc (pFile);
wordArray[n] = c;
n++;
} while (c != EOF);
n = 0;
fclose (pFile);
do {
result = strtok(NULL, delims);
holder[n] = *result; // holder stores the value of 'result', which should be a word.
wp = &holder[n]; // wp points to the address of 'holder' which holds the 'result'.
n++;
printf("Pointer value = %d\n", wp); // Prints the address of holder.
printf("Result is \"%s\"\n", result); // Prints the 'result' which is a word from the array.
//sl_push_front(&wp); // Push address onto stack.
} while (result != NULL);
}
return 0;
}
Please ignore the bad program structure, as I mentioned, I'm new to this!
Thanks
As others have pointed out, your second loop attempts to dereference result before you check for it being NULL. Restructure your code as follows:
result = strtok( wordArray, delims ); // do this *after* you have read data into
// wordArray
while( result != NULL )
{
holder[n] = *result;
...
result = strtok( NULL, delims );
}
Although...
You're attempting to read the entire contents of the file into memory before breaking it up into words; that's not going to work for files bigger than the size of your buffer (currently 1K). If I may make a suggestion, change your code such that you're reading individual words as you go. Here's an example that breaks the input stream up into words delimited by whitespace (blanks, newlines, tabs, etc.) and punctuation (period, comma, etc.):
#include <stdio.h>
#include <ctype.h>
int main(int argc, char **argv)
{
char buffer[1024];
int c;
size_t n = 0;
FILE *input = stdin;
if( argc > 1 )
{
input = fopen( argv[1], "r");
if (!input)
input = stdin;
}
while(( c = fgetc(input)) != EOF )
{
if (isspace(c) || ispunct(c))
{
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
n = 0;
}
}
else
{
buffer[n++] = c;
}
}
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
}
fclose(input);
return 0;
}
No warranties express or implied (having pounded this out before 7:00 a.m.). But it should give you a flavor of how to parse a file as you go. If nothing else, it avoids using strtok, which is not the greatest of tools for parsing input. You should be able to adapt this general structure to your code. For best results, you should abstract that out into its own function:
int getNextWord(FILE *stream, char *buf, size_t bufsize)
{
int c;
size_t n = 0;
while(( c = fgetc(input)) != EOF && n < bufsize)
{
if (isspace(c) || ispunct(c))
{
if (n > 0)
{
buf[n] = 0;
n = 0;
}
}
else
{
buffer[n++] = c;
}
}
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
}
if (n == 0)
return 0;
else
return 1;
}
and you would call it like
void foo(void)
{
char word[SOME_SIZE];
...
while (getNextWord(inFile, word, sizeof word))
{
do_something_with(word);
}
...
}
If you expect in your do...while code, that result could be null (this is the condition for loop break), how do you think this code-line:
holder[n] = *result;
must work? It seems to me, that it is the reason for crashing in your program.
Change do while loop to while
use
while (condition)
{
}
instead of
do {
}while(condition)
It is crashing because you are trying to derefrance a NULL pointer result in do while loop.
I work mostly with Objective-C and was just looking at your question for fun, but I may have a solution.
Before setting n=0; after your first do-while loop, create another variable called totalWords and set it equal to n, totalWords can be declared anywhere within the file (except within one of the do-while loops), but can be defined at the top to the else block since its lifetime is short:
totalWords = n;
then you can set n back to zero:
n = 0;
Your conditional for the final do-while loop should then say:
...
} while (n <= ++totalWords);
The logic behind the application will thus say, count the words in the file (there are n words, which is the totalWords in the file). When program prints the results to the console, it will run the second do-while loop, which will run until n is one result past the value of totalWords (this ensures that you print the final word).
Alternately, it is better practice and clearer for other programmers to use a loop and a half:
do {
result = strtok(NULL, delims);
holder[n] = *result;
wp = &holder[n];
printf("Pointer value = %d\n", wp);
printf("Result is \"%s\"\n", result);
//sl_push_front(&wp); // Push address onto stack.
if (n == totalWords) break; // This forces the program to exit the do-while after we have printed the last word
n++; // We only need to increment if we have not reached the last word
// if our logic is bad, we will enter an infinite loop, which will tell us while testing that our logic is bad.
} while (true);
This function is supposed to get a file of vectors. The first line
contains the dimension. All other line are in the form of
"P:3,5,2". The letter is P or N and the numbers are coordinates.
This function read a line each time it is being invoked and it saves the P/N char and the
coordinates into an array of double.
void readSamplesFile(FILE *sample_p, double result[])
{
if (dimension == 0)
{
fscanf(sample_p,"%lf",&dimension);
}
printf("dimentions: %lf\n",dimension);
printf("LINE \n");
int index;
for (index = 0; index<dimension; index++)
{
printf("%d",index);
fscanf(sample_p,"%lf%*[:,]",&result[index]);
printf("%lf",result[index]);
}
}
when i run it i get an endless loop. the dimension is read correctly but the
it prints
LINE
00.00000010.000000dimentions: 2.000000
endlessly. any ideas why?
hope i was clear
EDIT:
I've added the calling function:
void fillArray(FILE *sample_p,FILE *separators_p){
double coordinates[MAX_DIMENSION];
while (!feof(sample_p)){
readSamplesFile(sample_p,coordinates);
}
}
p.s.
fscanf is set to read : and , but to ignore them.
Neither 'P' nor 'N' is a valid double, nor are they ':' or ',', so the fscanf() fails. You should always check the return value from fscanf().
We can also debate whether you'd be better off using fgets() to read a line and sscanf() to parse it. Doing so avoids some issues; it is the way I'd code it automatically.
This code seems to work on the input file:
3
P:3,5,2
N:21.12,2.345e6,1.9132e-34
yielding the output:
dimension: 3.000000
LINE: P:3,5,2
P:offset=2:0=3(2):1=5(4):2=2(6):
LINE: N:21.12,2.345e6,1.9132e-34
N:offset=2:0=21.12(2):1=2.345e+06(8):2=1.9132e-34(16):
I'm still not keen on the (mis)use of a floating point dimension, but it works.
#include <stdio.h>
enum { MAX_DIMENSION = 6 };
enum { MAX_BUFFSIZE = 4096 };
static double dimension = 0.0;
static int get_dimension(FILE *fin)
{
char buffer[MAX_BUFFSIZE];
if (fgets(buffer, sizeof(buffer), fin) == 0)
return -1;
if (sscanf(buffer, "%lf", &dimension) != 1)
return -1;
printf("dimension: %lf\n", dimension);
return 0;
}
static int readSamplesFile(FILE *sample_p, double result[])
{
char buffer[MAX_BUFFSIZE];
if (fgets(buffer, sizeof(buffer), sample_p) == 0)
return -1;
printf("LINE: %s", buffer);
char c;
int offset;
if (sscanf(buffer, " %c:%n", &c, &offset) != 1)
return -1;
printf("%c:", c);
printf("offset=%d:", offset);
for (int index = 0; index < dimension; index++)
{
int newoff;
if (sscanf(&buffer[offset], "%lf%*[:,]%n", &result[index], &newoff) < 1)
return -1;
printf("%d=%g(%d):", index, result[index], offset);
offset += newoff;
}
putchar('\n');
return 0;
}
static void fillArray(FILE *sample_p)
{
double coordinates[MAX_DIMENSION];
while (readSamplesFile(sample_p, coordinates) == 0)
;
}
int main(void)
{
if (get_dimension(stdin) == 0)
fillArray(stdin);
return 0;
}
Note that the fillArray() function, as written, does not do anything with the line of data. There is no checking that the dimension specified is positive and not greater than MAX_DIMENSION (that would go in get_dimension()). It feels cleaner to separate get_dimension() into a separate function than to hide it inside readSampleFile(). There is an argument that readSampleFile() should be renamed readSampleLine() since it does only process one line at a time, not a whole file at a time.
The use of the %n format specifier is a little tricky, but the code needs to know where to resume reading the buffer on the next cycle.
Anyone got anything about reading a sequential number from text file per line and parsing it to an array in C?
What I have in a file:
12 3 45 6 7 8
3 5 6 7
7 0 -1 4 5
What I want in my program:
array1[] = {12, 3, 45, 6, 7, 8};
array2[] = {3, 5, 6, 7};
array3[] = {7, 0, -1, 4, 5};
I've been through several ways to read it, but the only matter is only when i want to tokenize it per line.
Thank you.
The following code will read a file a line at a time
char line[80]
FILE* fp = fopen("data.txt","r");
while(fgets(line,1,fp) != null)
{
// do something
}
fclose(fp);
You can then tokenise the input using strtok() and sscanf() to convert the text to numbers.
From the MSDN page for sscanf:
Each of these functions [sscanf and swscanf] returns the
number of fields successfully
converted and assigned; the return
value does not include fields that
were read but not assigned. A return
value of 0 indicates that no fields
were assigned. The return value is EOF
for an error or if the end of the
string is reached before the first
conversion.
The following code will convert the string to an array of integers. Obviously for a variable length array you'll need a list or some scanning the input twice to determine the length of the array before actually parsing it.
char tokenstring[] = "12 23 3 4 5";
char seps[] = " ";
char* token;
int var;
int input[5];
int i = 0;
token = strtok (tokenstring, seps);
while (token != NULL)
{
sscanf (token, "%d", &var);
input[i++] = var;
token = strtok (NULL, seps);
}
Putting:
char seps[] = " ,\t\n";
will allow the input to be more flexible.
I had to do a search to remind myself of the syntax - I found it here in the MSDN
What I would do is to make a function like this:
size_t read_em(FILE *f, int **a);
In the function, allocate some memory to the pointer *a, then start reading numbers from the f and storing them in *a. When you encounter a newline character, simply return the number of elements you've stored in *a. Then, call it like this:
int *a = NULL;
FILE *f = fopen("Somefile.txt", "r");
size_t len = read_em(f, &a);
// now a is an array, and len is the number of elements in that array
Useful functions:
malloc() to allocate an array.
realloc() to extend a malloc()ed array
fgets() to read a line of text (or as much as can be stored).
sscanf() to read data from a string (such as a string returned by fgets()) into other variables (such as an int array created by malloc() - hint hint)
I'd strongly suggest NOT to use sscanf and friends when the number of fields is variable.
Use strtok and atoi. Just make sure to read the strtok manpage well, many programmers I know find its syntax a bit surprising in the beginning. Also note that strtok will modify the input string, so you may want to work on a copy.
The following code may be what you're looking for. Hopefully you won't need too much of a description given the comments but, if you have questions, feel free to ask.
It basically uses an fgets loop to read each line in and strtok to separate that line into fields. It constructs a linked list of integer arrays which contain the actual data - you can see the use of that linked list in the code at the end that dumps out the table.
It also has a means by which it can handle arbitrary-sized lines in the input file without buffer overflow (subject to memory constraints of course). Keep in mind that the strtok only expects one space between each field on the line although that could be recoded to handle multiple spaces or even any amount of white space. I've kept that bit simple since the code was already getting a little big :-)
The atoi function is used to convert the individual word on each line into integers. If you want error checking on those, I'd call your own variant which also checks that all characters in the word are numeric.
Using your input file of:
12 3 45 6 7 8
3 5 6 7
7 0 -1 4 5
it produces output along the lines of:
0x97b5170, size = 6:
12 3 45 6 7 8
0x97b51d0, size = 4:
3 5 6 7
0x97b51e0, size = 5:
7 0 -1 4 5
Here's the code that produced that output:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
// This is the linked list of integer arrays.
typedef struct _tIntArray {
int size;
int *array;
struct _tIntArray *next;
} tIntArray;
static tIntArray *first = NULL;
static tIntArray *last = NULL;
// Add a line of integers as a node.
static int addNode (char *str) {
tIntArray *curr; // pointers for new integer array.
char *word; // word within string.
char *tmpStr; // temp copy of buffer.
int fldCnt; // field count for line.
int i;
// Count number of fields.
if ((tmpStr = strdup (str)) == NULL) {
printf ("Cannot allocate duplicate string (%d).\n", errno);
return 1;
}
fldCnt = 0;
for (word = strtok (tmpStr, " "); word; word = strtok (NULL, " "))
fldCnt++;
free (tmpStr);
// Create new linked list node.
if ((curr = malloc (sizeof (tIntArray))) == NULL) {
printf ("Cannot allocate integer array node (%d).\n", errno);
return 1;
}
curr->size = fldCnt;
if ((curr->array = malloc (fldCnt * sizeof (int))) == NULL) {
printf ("Cannot allocate integer array (%d).\n", errno);
free (curr);
return 1;
}
curr->next = NULL;
for (i = 0, word = strtok (str, " "); word; word = strtok (NULL, " "))
curr->array[i++] = atoi (word);
if (last == NULL)
first = last = curr;
else {
last->next = curr;
last = curr;
}
return 0;
}
int main(void) {
int lineSz; // current line size.
char *buff; // buffer to hold line.
FILE *fin; // input file handle.
long offset; // offset for re-allocating line buffer.
tIntArray *curr; // pointers for new integer array.
int i;
// Open file.
if ((fin = fopen ("qq.in", "r")) == NULL) {
printf ("Cannot open qq.in, errno = %d\n", errno);
return 1;
}
// Allocate initial line.
lineSz = 2;
if ((buff = malloc (lineSz+1)) == NULL) {
printf ("Cannot allocate initial memory, errno = %d.\n", errno);
return 1;
}
// Loop forever.
while (1) {
// Save offset in case we need to re-read.
offset = ftell (fin);
// Get line, exit if end of file.
if (fgets (buff, lineSz, fin) == NULL)
break;
// If no newline, assume buffer wasn't big enough.
if (buff[strlen(buff)-1] != '\n') {
// Get bigger buffer and seek back to line start and retry.
free (buff);
lineSz += 3;
if ((buff = malloc (lineSz+1)) == NULL) {
printf ("Cannot allocate extra memory, errno = %d.\n", errno);
return 1;
}
if (fseek (fin, offset, SEEK_SET) != 0) {
printf ("Cannot seek, errno = %d.\n", errno);
return 1;
}
continue;
}
// Remove newline and process.
buff[strlen(buff)-1] = '\0';
if (addNode (buff) != 0)
return 1;
}
// Dump table for debugging.
for (curr = first; curr != NULL; curr = curr->next) {
printf ("%p, size = %d:\n ", curr, curr->size);
for (i = 0; i < curr->size; i++)
printf (" %d", curr->array[i]);
printf ("\n");
}
// Free resources and exit.
free (buff);
fclose (fin);
return 0;
}
Does your file have a specific number of lines or do you need to be able to read an arbitrary number into random arrays?
Here's code to read in a file line by line.
#include <stdio.h>
int main()
{
char *inname = "test.txt";
FILE *infile;
char line_buffer[BUFSIZ];
infile = fopen(inname, "r");
if (!infile) {
printf("Couldn't open file %s for reading.\n", inname);
return 0;
}
while (fgets(line_buffer, sizeof(line_buffer), infile)) {
// process line
}
return 0;
}
You can use sscanf or any of a number of tokenizing/converting functions to extract the numbers. BUFSIZ is a good constant from stdio.h that is designed to make stream I/O efficient on a target system.
Use strtol() to parse each line:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
static char buffer[1024];
static long values[256];
while(fgets(buffer, sizeof buffer, stdin))
{
char *current = buffer;
size_t i = 0;
while(*current && *current != '\n' &&
i < sizeof values / sizeof *values)
{
char *tail = NULL;
errno = 0;
values[i] = strtol(current, &tail, 0);
if(errno || tail == current)
{
fprintf(stderr, "failed to parse %s\n", current);
break;
}
++i, current = tail;
}
// process values
printf("read %i values\n", i);
}
}