C struct values garbled - c

Having the problem with the sender and receiver of the checksum. where I am using the struct to define the variables to be written into the pipe.
struct ANSWER
{
int arr[BUFFER];
int counter;
int ans;
}a;
The mechanism used for checksum is :
int chck(int value[],int count)
{
int i,sum=0,checksum;
for ( i=0; i<count; i++)
{
sum+=value[i];
}
printf("SUM IS :%d \n",sum);
checksum=checksum^sum;
printf("CHECKSUM IS : %d",checksum);
return checksum;
}
The main problem is in writing in the pipe as
write(pipe,a,sizeof(a));
This writes all the contains using struct.
While at receiver side having problem in retrieving the specific values
when I retrieve the values at receiver side I get garbled values in count and ans variables.
read(pipe,a,sizeof(a));
printf("COUNT : \n ",a.counter);
the values turn out to be garbled.
Main area:
void main(int argc, char * argv[])
{
int pipe,i;
pipe = open("devil",O_WRONLY);
if ( pipe == -1 )
{
printf("ERROR NO PIPE FOUND \n");
}
printf("ENTER NUMBER OF ELEMENT : ");
scanf("%d",a.counter);
for ( i=0; i<a.counter; i++)
{
printf("ENTER NUMBER :");
scanf("%d",&a.arr[i]);
}
a.ans=chck(a.arr,a.counter);
printf("CHECKSUM IS : %d \n",a.ans);
write(pipe,&a,sizeof(a));
}

Turning comment to answer, I suggest you add error checking to all your system and standard library calls (except printing to stdout/stderr, that's more clutter than it's worth for almost any app), something like:
int main(int argc, char * argv[])
{
int pipe, i;
pipe = open("devil",O_WRONLY);
if ( pipe == -1 )
{
perror("open pipe");
return EXIT_FAILURE;
}
printf("ENTER NUMBER OF ELEMENT : ");
if (scanf("%d",&a.counter) != 1)
{
fprintf(stderr, "scanf count IO or parse error, or EOF\n");
return EXIT_FAILURE;
}
for ( i=0; i<a.counter; i++)
{
printf("ENTER NUMBER :");
if (scanf("%d",&a.arr[i]) != 1)
{
fprintf(stderr, "scanf number IO or parse error, or EOF\n");
return EXIT_FAILURE;
}
}
a.ans=chck(a.arr,a.counter);
printf("CHECKSUM IS : %d \n",a.ans);
i = write(pipe,&a,sizeof(a));
if (i == -1)
{
perror("write to pipe");
return EXIT_FAILURE;
}
else if (i != sizeof(a))
{
fprintf(stderr, "write to pipe, partial write!\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Notes: For pedantic code, write actually returns type ssize_t. I changed return type of main to int as that is required by standard. Please read manual page / docs of perror to understand what it does and how it prints the actual error message. EXIT_FAILURE and EXIT_SUCCESS are constants for the two standard-defined return values or exit codes, usually 1 and 0 respectively. And in a larger program, you would probably write helper functions or macros for error handling, and split the entire main function into multiple functions, to make the code look less cluttered.
Additionally, you use checksum variable uninitialized in chck function. You should probably initialize it to 0, something like:
int chck(int value[],int count)
{
int i = 0, sum = 0, checksum = 0;
....
In general, you should avoid having uninitialzied variables, it's too easy to forget when first using them, like demonstrated here... Also, turn on compiler warnings, so you will usually get a warning if you still do that accidentally.
One more bug: your first scanf was missing & to get pointer to a.counter (required for scanf, to actually modify the original variable). A good compiler may also be smart enough to warn about passing suspicious arguments to scanf.

Related

Loop Problem while reading a text file in C

Hi guys i wanna read a text file word by word and count the words while doing it and then pass the words and numbers to my linked list.
But i have a loop problem. I can't escape from the infinite loop.
Here is my code:
int main()
{
char kelime[100];
char kelime2[100];
long a = 0;
FILE * dosya = fopen("oku.txt", "r");
while(1)
{
fseek(dosya,a,SEEK_CUR);
if(feof(dosya))
{
break;
}
while(fscanf(dosya, "%99[^ \n]", kelime) == 1)
{
printf("%s \n",kelime);
a = ftell(dosya);
}
while(1)
{
fscanf(dosya, "%s" , kelime2);
printf("%s \n",kelime2);
if((strcmp(kelime, kelime2))== 0)
{
// Things to do...
}
memset(kelime2,0,sizeof(kelime2));
if(feof(dosya))
{
rewind(dosya);
break;
}
}
}
fclose(dosya);
return 0;
}
To read any kind of space-delimited word, all you need is something like:
while (fscanf(dosya, "%99s", kelime) == 1)
{
// Do something with the "word" in kelime
}
The scanf family of functions all return the number of conversions it successfully made. With a single conversion specifier it can only return 1 on success, 0 if the specifier could not be matched (should never happen in this case) or -1 on error or end of file.
The loop will simply read all words in the input file until the end of the file.
Putting it together in a program that reads and print all words, it would look something like this:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *dosya = fopen("oku.txt", "r");
if (dosya == NULL)
{
perror("Could not open file");
return EXIT_FAILURE;
}
char kelime[100];
size_t counter = 0;
while (fscanf(dosya, "%99s", kelime) == 1)
{
printf("Read word #%zu: %s\n", ++counter, kelime);
}
fclose(dosya);
printf("There was a total of %zu \"words\" in the file\n", counter);
}
A little explanation for the %zu format specifier for printf:
The u is the base format (it's really %u), and stands for unsigned integer. The z prefix tells printf that the corresponding argument is really a size_t value.
The type size_t is a standard C type that is used for all kinds of sizes, counters and indexes. It's an unsigned integer of unspecified size.

How to improve my code to fill an array from a text table?

I'm a research assistant at a mental health clinic, and I'm trying to write a C program which will convert raw scores (on four subtests) into T scores. C, for better or for worse, is the language I'm most familiar with, but it's been a while since I've written anything, so please excuse my ignorance.
Each raw score on each subtest only corresponds to one T score. However, the converse doesn't hold: Each T score can correspond to multiple raw scores (especially at either extreme of the scale), and some T scores don't correspond to any raw scores.
My solution to the problem of representing this data in a text file is to represent the data in eight columns, every odd column (indices [0], [2], [4], and [6]) representing the T score and every even column representing the raw score that corresponds to it, which seems to solve this for now. If a T score doesn't have a corresponding raw score, I've inserted -1 to keep fscanf happy.
However, I was hoping that maybe you can help me work out a more intelligent solution. I'm also wondering if there's a better data structure that will allow me to intelligently represent and manipulate the asymmetric relationship between T scores and raw scores, maybe so that there would be one entry for each T score and a range of possible values for the subtests that could yield that T score? The file looks like this:
20,0,20,0,20,-1,20,0
20,1,20,1,21,-1,20,1
20,2,20,2,22,0,20,2
20,3,20,3,23,1,20,3
20,4,20,4,24,2,20,4
20,5,20,5,25,3,20,5
20,6,20,6,26,4,20,6
20,7,20,7,27,5,20,7
20,8,20,8,28,6,20,8
20,9,20,9,29,7,20,9
You can see what I have so far below. It does the following:
Using a for loop and a two-dimensional integer array, it retrieves the data from the file and enters it into the array.
The table has 83 rows, so I capped the loop at 83 iterations, but I'm sure there must be a better way to do this so I won't have to hard-code it into the program (I was thinking maybe a while loop that stops when a certain number, like -2, is detected?).
The -1 trick seems to work for now, but I'm also hoping there's a better way to process the table input so that I can use it as is. But I don't really understand the syntax for fscanf well enough to implement something better. Could you give me any pointers on that?
Using further for loops, it runs through the raw score rows and checks for identity between the score the user has entered and its place in the table. I was wondering if there's a clever way to nest these operations into a superordinate for loop?
When the numbers match up, a T score is assigned and the results are printed.
Here's the code:
#include <stdio.h>
int main(int argc, char *argv[]) {
int tab[8][83];
int r_v, r_s, r_bd, r_mr;
int t_v, t_s, t_bd, t_mr;
int i;
FILE *input;
input = fopen(argv[1], "r");
printf("Subtest A score?\n");
scanf("%i", &r_v);
printf("Subtest B score?\n");
scanf("%i", &r_s);
printf("Subtest C score?\n");
scanf("%i", &r_bd);
printf("Subtest D score?\n");
scanf("%i", &r_mr);
for (i = 0; i < 83; i++) {
fscanf(input, "%i,%i,%i,%i,%i,%i,%i,%i", &tab[0][i], &tab[1][i], &tab[2][i], &tab[3][i], &tab[4][i], &tab[5][i], &tab[6][i], &tab[7][i]);
}
for (i = 0; i < 83; i++) {
if (r_v == tab[1][i]) {
t_v = tab[0][i];
}
}
for (i = 0; i < 83; i++) {
if (r_s == tab[3][i]) {
t_s = tab[2][i];
}
}
for (i = 0; i < 83; i++) {
if (r_bd == tab[5][i]) {
t_bd = tab[4][i];
}
}
for (i = 0; i < 83; i++) {
if (r_mr == tab[7][i]) {
t_mr = tab[6][i];
}
}
printf("The participant had the following raw scores: %d, %d, %d, and %d.\n", r_v, r_s, r_bd, r_mr);
printf("Which corresponds to the following T scores: %d, %d, %d, and %d.\n", t_v, t_s, t_bd, t_mr);
return 0;
}
Error Handling
To start, the posted code lacks error-checking. The user may fail to provide a file name, or may provide a non-existent file name. The file may fail to open. The user may enter non-numeric input. The input values may not be found in the table.
Failure to provide a file name can be caught by checking argc at the beginning of the program. Failure to open the file can be handled by checking the value returned from fopen() and exiting with a message when a null pointer is returned.
If an input value is not found in the table, no value is stored in the associated variable. One way of handling this problem is to initialize the associated variable to a sentinel value which is not expected in the table. I have chosen -1 in the code below.
To handle user input, a get_integer() function could be added which takes an input prompt message as an argument and returns the input number. If the user enters malformed input, the function continues to prompt for the input until correct input is given. This function can also validate that input is a non-negative integer, to guard against assigning a value based on a user input value of -1.
One refinement might be to place an upper limit on how many times a user may provide bad input before exiting the program with a message.
Data Structure
Instead of storing the table as a 2d array and accessing the individual fields of the table through indices, I would suggest creating a structure for the lines of the table, and a table[] array of these structs. This would allow for descriptive field names and make the code much more clear.
Reading the File
I would suggest using fgets() to fetch lines from the file. This function returns a null pointer when end-of-file has been reached, allowing control with a while loop. The sscanf() function can be used to parse each line and to store values in the appropriate locations. As with all functions which return meaningful values, the value returned from sscanf() should be checked to be sure that input is as expected. If not, the situation can be handled, perhaps by exiting the program with an error message. I would also consider constructing the table from the file before asking for more user input.
As each line is successfully added to the table, a line counter can be incremented to keep track of the number of rows in the table[] array.
Searching the Table
Only one loop is needed here. The line counter can be used to establish the upper bound on the number of rows to be checked. As OP has suggested that each search value is unique, once it has been found and stored the associated variable will not be changed again. If a search value is not found in the table, the associated variable continues to hold the initial value (-1).
A refinement would be to add a flag to indicate when all search values have been found, and to break out of the loop early when this occurs.
Here is an example program:
#include <stdio.h>
#include <stdlib.h>
#define MAX_ROWS 100
#define BUF_SZ 1024
struct Record
{
int t_v;
int r_v;
int t_s;
int r_s;
int t_bd;
int r_bd;
int t_mr;
int r_mr;
};
int get_integer(const char *prompt);
int main(int argc, char *argv[])
{
struct Record table[MAX_ROWS];
int r_v, r_s, r_bd, r_mr;
int t_v = -1;
int t_s = -1;
int t_bd = -1;
int t_mr = -1;
FILE *input;
if (argc < 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
input = fopen(argv[1], "r");
if (input == NULL) {
fprintf(stderr, "Unable to open file: %s\n", argv[1]);
exit(EXIT_FAILURE);
}
/* Read table into array */
int line = 0;
char buffer[BUF_SZ];
while (fgets(buffer, sizeof buffer, input) != NULL) {
if (sscanf(buffer, "%i,%i,%i,%i,%i,%i,%i,%i",
&table[line].t_v,
&table[line].r_v,
&table[line].t_s,
&table[line].r_s,
&table[line].t_bd,
&table[line].r_bd,
&table[line].t_mr,
&table[line].r_mr)
!= 8) {
fprintf(stderr, "Format error in line %d\n", line);
exit(EXIT_FAILURE);
}
++line;
}
fclose(input);
r_v = get_integer("Subtest A score?\n");
r_s = get_integer("Subtest B score?\n");
r_bd = get_integer("Subtest C score?\n");
r_mr = get_integer("Subtest D score?\n");
for (int i = 0; i < line; i++) {
if (r_v == table[i].r_v) {
t_v = table[i].t_v;
}
if (r_s == table[i].r_s) {
t_s = table[i].t_s;
}
if (r_bd == table[i].r_bd) {
t_bd = table[i].t_bd;
}
if (r_mr == table[i].r_mr) {
t_mr = table[i].t_mr;
}
}
printf("The participant had the following raw scores: "
"%d, %d, %d, and %d.\n",
r_v, r_s, r_bd, r_mr);
printf("Which corresponds to the following T scores: "
"%d, %d, %d, and %d.\n",
t_v, t_s, t_bd, t_mr);
return 0;
}
int get_integer(const char *prompt)
{
char buf[BUF_SZ];
int ret;
do {
printf("%s", prompt);
fflush(stdout);
if (fgets(buf, sizeof buf, stdin) == NULL) {
fprintf(stderr, "Input error in get_integer()\n");
exit(EXIT_FAILURE);
}
} while (sscanf(buf, "%i", &ret) != 1 || ret < 0);
return ret;
}
And here is an example interaction using the posted input file:
λ> ./tvals t_vals.dat
Subtest A score?
3
Subtest B score?
4
Subtest C score?
5
Subtest D score?
6
The participant had the following raw scores: 3, 4, 5, and 6.
Which corresponds to the following T scores: 20, 20, 27, and 20.
Uhmm.. i didn't get that code very well.
As i can see, if A score is 5, t_v is 20 (either if a score was 0).
I don't see you loaded the values in the file to the tab matrix.
So tab is unitialized too.
Anothed thing if you want to improve your code.
You made 5 loops.. when you can simply merge all these into one.
Aforloop with at the top there's fscanf and after it all your conditions.
#include <stdio.h>
#include <stdlib.h>
#define NO_OF_ROW 100 //You can change if no of rows increases
#define EACH_LINE_SIZE 100
#define SUBTEST_A 1
#define SUBTEST_B 3
#define SUBTEST_C 5
#define SUBTEST_D 7
int main(int argc, char *argv[])
{
int tab[8][NO_OF_ROW] = {{0}};
int r_v, r_s, r_bd, r_mr;
int t_v=0, t_s=0, t_bd=0, t_mr=0;
int i;
FILE *input;
int no_rows =0;
char* line = malloc(EACH_LINE_SIZE);
input = fopen("User.txt", "r");
if(NULL == input)
{
return 0;
}
printf("Subtest A score?\n");
scanf("%i", &r_v);
printf("Subtest B score?\n");
scanf("%i", &r_s);
printf("Subtest C score?\n");
scanf("%i", &r_bd);
printf("Subtest D score?\n");
scanf("%i", &r_mr);
while (fgets(line, EACH_LINE_SIZE, input) != NULL)
{
sscanf(line, "%d,%d,%d,%d,%d,%d,%d,%d", &tab[0][i], &tab[1][i], &tab[2][i], &tab[3][i], &tab[4][i], &tab[5][i], &tab[6][i], &tab[7][i]);
//printf("%d,%d,%d,%d,%d,%d,%d,%d\n", tab[0][i], tab[1][i], tab[2][i], tab[3][i], tab[4][i], tab[5][i], tab[6][i], tab[7][i]);
i++;
if(NO_OF_ROW <= i)
{
printf("Stop file reading \n");
break; //if the file has more line -Just end it here
}
}
no_rows = i;
for(i=0 ; i < no_rows ; i++)
{
if (r_v == tab[SUBTEST_A][i])
{
t_v = tab[SUBTEST_A-1][i];
}
if (r_s == tab[SUBTEST_B][i])
{
t_s = tab[SUBTEST_B-1][i];
}
if (r_bd == tab[SUBTEST_C][i])
{
t_bd = tab[SUBTEST_C-1][i];
}
if (r_mr == tab[SUBTEST_D][i])
{
t_mr = tab[SUBTEST_D-1][i];
}
}
printf("The participant had the following raw scores: %d, %d, %d, and %d.\n", r_v, r_s, r_bd, r_mr);
printf("Which corresponds to the following T scores: %d, %d, %d, and %d.\n", t_v, t_s, t_bd, t_mr);
free(line);
return 0;
}
Instead of constant 83, tried with different method, so you can vary the number of lines
Also instead of 4 loops, tried to fit in single for loop

File Input with strings and ints compared to console input as a guessing game

I have an assignment I'm trying to finish. I'm new to C, and things aren't working as well as I'd hoped.
Our assignment is to write a small program to take input from a user through the command line, asking their name and then to take a guess at the magic number. We have a .txt document that has three names with associated numbers. Its three lines, on each line has a name, followed by a space, then the number. If the user's name doesn't equal one of those three, the magic number defaults to 12345.
After each guess, the program should say whether the number is too high or too low, and then quit (yeah, I don't know why either).
I've been debugging by printf statements, but can't figure out why it won't go into the comparison part of the code.
Any help or hints is greatly appreciated!
#include <stdio.h>
#include <string.h>
typedef struct {
char list_name[20];
int list_num;
}list;
char user_name[20];
int user_magic_num;
main(){
FILE *fp;
int i;
list answers;
//
fp = fopen("answers.txt", "rb");
if(fp == NULL)
{
perror("Error opening file");
return(-1);
}
printf("\n Hi, what's your name? \n \n");
scanf("%s",user_name);
printf("\n Hello, %s! \n \n What's the magic number? \n \n",user_name);
scanf("%d", &user_magic_num);
printf("You guessed %d.\n\n", user_magic_num);
for (i=0; i<=4;i++)
{
fgets(answers.list_name, 20, fp);
puts(answers.list_name);
if (strcmp(answers.list_name,user_name)==0)
{
printf("entered first if statement");
if(user_magic_num==answers.list_num)
{
printf("You guess correctly! %d is the magic number!",
answers.list_num);
break;
}
else if(user_magic_num>answers.list_num)
{
printf("Too high.");
}
else if(user_magic_num<answers.list_num)
{
printf("Too low");
}
}
else
{
user_magic_num = 12345;
}
}
return 0;
}
**************************************EDIT:
#include
#include
typedef struct list{
char list_name[20];
int list_num;
}list;
char user_name[20];
int user_magic_num;
main(){
FILE *fp;
int i;
list answers;
//
fp = fopen("answers.txt", "rb");
if(fp == NULL)
{
perror("Error opening file");
return(-1);
}
printf("\n Hi, what's your name? \n \n");
scanf("%s",user_name);
printf("\n Hello, %s! \n \n What's the magic number? \n \n",user_name);
scanf("%d", &user_magic_num);
printf("You guessed %d.\n\n", user_magic_num);
while (fscanf(fp, "%s %d",answers.list_name, &answers.list_num) != EOF)
{
//printf("%s\n", answers.list_name);
if(strcmp(user_name, answers.list_name)==0)
{
if (user_magic_num == answers.list_num)
{
printf("\nYes! %d is the magic number!\n", user_magic_num);
break;
}
else if (user_magic_num > answers.list_num)
{
printf("\nNope. Too high.\n");
break;
}
else
{
printf("\nNope. Too low.\n");
break;
}
}
else
{
answers.list_num = 12345;
printf("\nUser not recognized. Default magic number set.\n");
if (user_magic_num == answers.list_num)
{
printf("Yes! %d is the magic number!", user_magic_num);
break;
}
else if (user_magic_num > answers.list_num)
{
printf("\nNope. Too high.\n");
break;
}
else
{
printf("\nNope. Too low.\n");
break;
}
}
}
return 0;
}
I just did this. It works now, but only for the first name in the txt file. The text file looks like this:
Bob 123
Mike 23
Rachel 345
But it only works if I type in Bob and then 123. But if I try Mike or Rachel, it treats it like on unknown and resets the magic number to 12345. I'm assuming this is something with fscanf that I'm doing wrong.
There are some error in your loop block,First read the name and magic_number separately. then you have given extra else if check your last
else if statement.
else if(user_magic_num<answers.list_num)
{
printf("Too low");
}
should be
else
printf("Too low");
Some of modification in for loop:
for (i=0; i<2;i++)
{
fscanf(fp,"%s %d",answers.list_name,&answers.list_num);
puts(answers.list_name);
if (strcmp(answers.list_name,user_name)==0)
{
if(user_magic_num==answers.list_num)
{
printf("You guess correctly! %d is the magic number!\n",
answers.list_num);
break;
}
else if(user_magic_num>answers.list_num)
{
printf("Too high.\n");
}
else
{
printf("Too low\n");
}
}
else
user_magic_num = 12345;
}
You can do:
#include <stdio.h>
#include <string.h>
typedef struct {
char list_name[20];
int list_num;
}list;
char user_name[20];
int user_magic_num;
int main(){
FILE *fp;
int i;
list answers;
int matched = 0;
fp = fopen("answers.txt", "rb");
if(fp == NULL)
{
perror("Error opening file");
return -1;
}
printf("\n Hi, what's your name? \n \n");
scanf("%s",user_name);
printf("\n Hello, %s! \n \n What's the magic number? \n \n",user_name);
scanf("%d", &user_magic_num);
printf("You guessed %d.\n\n", user_magic_num);
/*
* i<3 - as per your description, your text document suppose to
* 3 names associated with numbers.
* You can change it as per your requirement.
*/
for (i=0; i<3; i++)
{
fscanf (fp, "%s %d", answers.list_name, &answers.list_num);
if(feof(fp))
{
puts("EOF");
break;
}
if (strcmp(answers.list_name,user_name)==0)
{
if(user_magic_num==answers.list_num)
{
printf("You guess correctly! %d is the magic number!\n", answers.list_num);
matched = 1;
break;
}
else if(user_magic_num>answers.list_num)
{
printf("Too high.\n");
}
else
{
printf("Too low.\n");
}
}
}
if (!matched)
{
user_magic_num = 12345;
printf ("Not matched\n");
/*
* Considering not matched as error and returning -1
*/
return -1;
}
return 0;
}
Mike, there are a number of subtle issues to address, all a normal part of learning C. First a few generalities:
char user_name[20]; /* avoid global variables unless absolutely */
int user_magic_num; /* required (that is almost never) */
Declare your variables in main and pass them as parameters to any functions as required. Declaring globals raises the risk of name-collisions and variable shadowing that can be very difficult to debug as your code grows.
main(){
main is a function of type int. It has two valid declarations recognized by the C standard:
int main (void) /* passes no arguments to main */
and
int main (int argc, char *argv[]) /* passes argc arguments in argv - use them */
(You will also see the equivalent int main (int argc, char **argv))
For a further discussion on main see: C11 Standard §5.1.2.2.1 Program startup (draft n1570). See also: What should main() return in C and C++?
Next:
return(-1); /* don't return negative values to the shell */
C provides two defined returns for main,
EXIT_SUCCESS /* 0 */
EXIT_FAILURE /* 1 */
(while it is shell dependent, returning negative values should be avoided)
Validate ALL input - period. Especially if it comes from a user (for all you know a cat could be stepping on the keyboard). All input functions have a return. At minimum, you must validate the return to know whether or not input has been successfully received. Failure to do so is an invitation for Undefined Behavior (that's a really bad thing...) Also, if you require input values within a certain range, then validate that the input is within that range.
Now to your problems. All line-oriented input functions (e.g. fgets and POSIX getline) read up to and including the trailing '\n'. It is included in the buffer they fill. Using fgets is the proper and recommended way to get lines of input (including user input). While the scanf family has its place, it is full or pitfalls, just waiting to trap new C-programmers. Read a line at a time into a buffer, and parse the values you need from the buffer. (using sscanf for that purpose is fine)
Your comparisons were failing because you didn't recognize your use of fgets was reading the entire line (or at least up to 19 chars) into answers.list_name, e.g.
fgets(answers.list_name, 20, fp);
Don't be stingy on buffers. Sure make them reasonable, but I'd rather be 256-chars to big, than 1-char too short... (Below I simply declare a 128-byte buffer to read each line of the file into.)
Logic. A quick logic diagram of how you will get input, what tests are needed on each variable, when to ask for additional input, how to respond to input, etc.. sketched out on an 8.5x11 with a pencil can save you hours of staring at the screen hoping inspiration strikes. (it rarely does when your just sitting at the screen guessing at changes and repeatedly recompiling -- only to have it fail again). Clear logic and a clear road-map of what you need to accomplish will save you time.
Putting the above together, and grabbing the trusty 8.5x11 and a pencil, it appears you were attempting to:
take a user name as user input
open and read through your file to match the user name, and read a
magic number (error if name not found)
take a magic number as user input
prompt the user for a maximum of 5 guesses to guess the number
providing too high and too low as hints for the next guess
Unanswered were, what to do if the name isn't found?
Below is a stab at what I took your intent to be. Look through the code, understand each line, if you don't -- then ask. note: the program expects the filename to read as the first argument to the program. Make use of that ability rather than hard-coding a path/filename in your code.
Also note the use of constants below. Never hardcode values (ironically called magic-numbers) in your code. Defining a constant or two up top provides a single convenient place where changes can be made if needed -- without having to pick through all variable and loop declarations to make needed changes.
(note: for scanf field width limits, you are just stuck -- those must be hard coded, which is another reason fgets (or getline) are preferred)
(note2: I intentionally left one line from your code commented with // and a question for you to answer)
#include <stdio.h>
#include <string.h>
#define MAXC 20 /* define constants when you need one */
#define MAX_TRIES 5
#define BUFSZ 128
typedef struct {
char list_name[MAXC];
int list_num;
} list;
int main (int argc, char **argv) {
int i = 0, /* always good to initialize variables */
name_found = 0,
user_magic_num = 0;
char user_name[MAXC] = "",
buf[BUFSZ] = "";
size_t len = 0;
list answers = { "", 0 };
FILE *fp = NULL;
if (argc != 2) { /* validate argument given on command-line */
fprintf (stderr, "error: insufficient input.\n"
"usage: %s filename\n", argv[0]);
return 1;
}
/* open / validate file open for reading */
if (!(fp = fopen (argv[1], "r"))) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
printf ("\n Hi, what's your name?: ");
if (scanf ("%19s", user_name) != 1) { /* validate ALL input */
fprintf (stderr, "error: invalid input or EOF - user_name.\n");
return 1;
}
while (fgets (buf, BUFSZ, fp)) { /* read each line from file into buf */
/* fgets reads and *includes* the trailing '\n' in buf, remove it */
len = strlen (buf); /* get length */
if (len && buf[len - 1] == '\n') /* check trailing '\n' */
buf[--len] = 0; /* overwite with nul-char */
else {
/* handle error - string too long */
}
/* separate buffer into answers.list_name and answers.list_num */
if (sscanf (buf, "%19s %d", answers.list_name, &answers.list_num) != 2) {
fprintf (stderr, "error: failed to parse name and num from line.\n");
return 1;
}
if (strcmp (answers.list_name, user_name) == 0) {
name_found = 1; /* flag - name found in file */
break;
}
}
fclose (fp); /* close file - you are done with it */
if (!name_found) {
fprintf (stderr, "error: name '%s' not found in file.\n", user_name);
return 1;
}
printf ("\n Hello, %s! \n \n What's the magic number?: ",user_name);
if (scanf ("%d", &user_magic_num) != 1) { /* ditto */
fprintf (stderr, "error: invalid input or EOF - user_magic_num.\n");
return 1;
}
printf (" You guessed: %d\n\n", user_magic_num);
for (i = 0; i < MAX_TRIES; i++) /* avoid <= (unless required) */
{
if (user_magic_num == answers.list_num) {
printf ("You guessed correctly! %d is the magic number!\n",
answers.list_num);
return 0;
}
else if (user_magic_num > answers.list_num)
printf ("Too high.\n\n");
else
printf ("Too low.\n\n");
printf (" What's your next guess %s?: ", user_name);
if (scanf ("%d", &user_magic_num) != 1) {
fprintf (stderr, "error: invalid input - user_magic_num\n");
return 1;
}
putchar ('\n');
}
fprintf (stderr, "Oops: tries exceed max (%d)\n", MAX_TRIES);
return 0;
}
Example Use/Output
$ ./bin/magicnum2 dat/magicnum.txt
Hi, what's your name?: Mike
Hello, Mike!
What's the magic number?: 100
You guessed: 100
Too high.
What's your next guess Mike?: 10
Too low.
What's your next guess Mike?: 20
Too low.
What's your next guess Mike?: 25
Too high.
What's your next guess Mike?: 23
You guessed correctly! 23 is the magic number!
What if the user_name is not in the file?
$ ./bin/magicnum2 dat/magicnum.txt
Hi, what's your name?: Frank
error: name 'Frank' not found in file.
Let me know if I failed to glean your intent from the question and I'm happy to help further.
Lastly, don't forget to enable compiler warnings each time you compile. That means at minimum -Wall -Wextra for gcc or /W3 (or /W4) for cl.exe (VS on windoze). For fuller warnings you can add -pedantic with gcc or use /Wall for cl.exe. Do not accept code until it compiles cleanly, without warning. You can learn more C just by reading and understanding what the compiler is telling you than you can from most tutorials...

Debug Assertion Error in C

got some code here that won't compile correctly because it is saying that my pointer is already null when i am testing for a not null expression in my main function. here is the code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCODE 53
#define MAXMESSAGE 256
void getCode(char *codeIn, char *filename) {
FILE *codeFile;
/* Open the file with the code */
codeFile = fopen(filename, "r");
if (codeFile == NULL) {
printf("Error opening the code file - program terminated\n");
exit(1);
}
/* Read the first (and assumed only) line from the file */
fgets(codeIn, MAXCODE, codeFile);
/* Terminate the string with /0 */
codeIn[MAXCODE] = '\0';
/* Close the file */
fclose(codeFile);
return;
}
int getMessage(int *message, char *filename) {
FILE *messageFile;
int counter = 0;
/* Open the file with the message */
messageFile = fopen(filename, "r");
if (messageFile == NULL) {
printf("Error opening the message file - program terminated\n");
exit(1);
}
/* Read one number at a time from the file and store it */
while (!feof (messageFile))
{
fscanf (messageFile, "%d", (message+counter));
counter++;
}
/* Close the file */
fclose(messageFile);
return (counter);
}
void sortMessage(int *message, int size) {
int i, j, temp;
for (i=0; i<size-1; i++) {
for (j=i; j<size; j++) {
if (message[i]>message[j]) {
temp = message[i];
message[i] = message[j];
message[j] = temp;
}
}
}
return;
}
void decodeMessage(char *codeIn, int *message, int size) {
FILE *outputFile;
int i = 0;
/* Open the output file */
outputFile = fopen("csis.txt", "w");
if (outputFile == NULL) {
printf("Error opening the output file - program terminated\n");
exit(1);
}
for (i=0; i< size; i++) {
fprintf(outputFile, "%c", codeIn[message[i]%100]);
printf("%c", codeIn[message[i]%100]);
}
printf("\n");
/* Close the file */
fclose(outputFile);
return;
}
int main(int argc, char *argv[])
{
char code[MAXCODE];
int msg[MAXMESSAGE];
int msgSize;
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
}
getCode(code, argv[1]);
msgSize = getMessage(msg, argv[2]);
sortMessage(msg, msgSize);
decodeMessage(code, msg, msgSize);
return;
}
So basically my code is using two files called codefile.txt and msgfile.txt to decode the secret message and write the decoded sequence to a new text file called csis.
As woolstar pointed out in the comments, you don't need to NUL terminate your codeIn array following fgets, because fgets will do that for you. In fact, this constitutes an overflow which we can best see by considering what happens when MAXCODE is 1: codeIn contains only one element: codeIn[0], and accessing codeIn[1] is an error.
Similarly, since MAXCODE is 53 and that's how many elements pointed to by codeIn, codeIn[message[i]%100] is suspicious because there's a potential for message[i]%100 to be an invalid index. While we're on this note, it might be wise to make message[i] an unsigned int so that it can't be negative. The format specifier (for printf and scanf) corresponding to unsigned int is %u.
while ( !feof(messageFile) ) is wrong because the EOF flag isn't set until an attempt is made at reading. Between attempting to read and your EOF test, however, you've incremented counter which means you've counted one too many items. Perhaps your loop should look like this:
while (fscanf(messageFile, "%d", (message+counter)) == 1)
{
counter++;
}
Note that this code assumes you've chosen to keep message[i] as an int. If you've chosen to use unsigned int instead, of course you'll want to use the %u format specifier.
You can probably see that feof is mostly superfluous... You can usually test for erroneous reads by checking the return value. Try to avoid feof in the future.
Your main function has a return type of int, yet at the end of it you have a return; statement which doesn't return an int value. Remove that. It's probably causing errors during compilation.
Presumably, when argv != 3 you want to return from main so you don't end up processing invalid arguments... Make sure you return an int value, e.g.
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
return 0;
}

Program not accepting all values from file

I posted a problem yesterday regarding a certain segment of my code. The aim was to basically scan in data values from a .dat file into an array, print the values whilst also counting how many values were in the file.
Sounds pretty simple, but my program only seemed to print a certain number of the values. More specifically, of a data file containing over 300000 values, it would only print the last 20000 and nothing else.
So I left it, finished the rest of my code and now it's the last part I have to sort. I've made a few changes and tried actually printing an output .dat file now so I can see what I'm getting. The code is below by the way.
Initally I assumed perhaps it was something related to the memory allocation of my array (was getting a segmentation error? when putting the whole code together) so I created an external function that counted the number of values instead (that works).
My only problem now is that it still only chooses to print 20000 values and then the rest are 0s. I was thinking perhaps it had something to do with the type but they all contain 7 dps in scientific notation. Here's a sample of some of the values:
8.4730000e+01 1.0024256e+01
8.4740000e+01 8.2065599e+00
8.4750000e+01 8.3354644e+00
8.4760000e+01 8.3379525e+00
8.4770000e+01 9.8741315e+00
8.4780000e+01 9.0966478e+00
8.4790000e+01 9.4760274e+00
8.4800000e+01 7.1199807e+00
8.4810000e+01 7.1990172e+00
Anyone see where I'm going wrong? I'm sorry for the long question, it's just been bugging me for the last day or so and no matter what I change nothing seems to help. Any kind of input would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
int count(int);
const char df[]="data_file.dat";
const char of[]="output_file.dat";
int main(int argc, char *argv[])
{
FILE *input, *output;
int i, N;
float *array;
N = count(i);
input = fopen(df, "r");
output = fopen(of, "w");
array = (float*)malloc(N*sizeof(float));
if((input != (FILE*) NULL) && (output != (FILE*) NULL))
{
for(i = 0; i < N; i++)
{
fscanf(input, "%e", &array[i]);
fprintf(output, "%d %e\n", i, array[i]);
}
fclose(input);
fclose(output);
}
else
printf("Input file could not be opened\n");
return(0);
}
int count(int i)
{
FILE *input;
input = fopen(df, "r");
int N = 0;
while (1)
{
i = fgetc(input);
if (i == EOF)
break;
++N;
}
fclose(input);
return(N);
}
Your biggest problem is that count() doesn't count float values; it counts how many characters are in the file. Then you try to loop and call fscanf() more times than there are values in the file. The first times, fscanf() finds a float value and scans it; but once the loop reaches the end of file, fscanf() will be returning an EOF status. It seems possible that fscanf() will set the float value to 0.0 when it returns EOF.
I suggest you rewrite so that you don't try to pre-count the float values. Write a loop that just repeatedly calls fscanf() until it returns an EOF result, then break out of the loop and close the files.
P.S. If you are going to write a function like count(), you should pass in the filename as an argument instead of hard-coding it. And your version of count() takes an integer argument but just ignores the value; instead, just declare a temp variable inside of count().
EDIT: Okay, here is a complete working program to solve this problem.
#include <stdio.h>
int
main(int argc, char **argv)
{
FILE *in_file, *out_file;
unsigned int i;
if (argc != 3)
{
fprintf(stderr, "Usage: this_program_name <input_file> <output_file>\n");
return 1; // error exit with status 1
}
in_file = fopen(argv[1], "r");
if (!in_file)
{
fprintf(stderr, "unable to open input file '%s'\n", argv[1]);
return 1; // error exit with status 1
}
out_file = fopen(argv[2], "w");
if (!out_file)
{
fprintf(stderr, "unable to open output file '%s'\n", argv[2]);
return 1; // error exit with status 1
}
for (i = 0; ; ++i)
{
int result;
float x;
result = fscanf(in_file, "%e", &x);
if (1 != result)
break;
fprintf(out_file, "%d %e\n", i, x);
}
return 0; // successful exit
}
Note that this version doesn't need to allocate a large array; it just needs a single temporary float variable. Maybe your program will need to store all the float values. In that case, write a count() function that uses a loop similar to the above loop, using fscanf() to count float values.
Also note that this program checks for errors after calling fopen() and fscanf().
You are allocating far more floats in memory (N) than you need because your N is the number of characters in the file, not the number of values in it.
Also, how did you determine that there are 300000 values in the file?

Resources