What do I check for in this if statement? - c

I am using sscanf to read user input and save to several outputs. As far as my understanding goes, if I have sscanf(iput, %s %s %s %s, arr[0], arr[1], arr[2], arr[3]) I can have up to 4 items entered, but it does not require 4; meaning I could just put two and arr[2] ad arr[3] would have no value. However, I think I am not understanding something since later when I check the values of arr[2]/arr[3] they are not null, when I thought strings were just initialized with \0. Look at my code please and tell me what the if statement should be checking for I guess.
printf("Enter a command: ");
char input[20];
char line[5][20];
fgets(input, 20, stdin);
sscanf(input, "%s %s %s %s %s", line[0], line[1], line[2], line[3], line[4]);
....
else {
int status;
if(line[4] != NULL) {
char *args[6];
args[0] = line[0];
args[1] = line[1];
args[2] = line[2];
args[3] = line[3];
args[4] = line[4];
args[5] = NULL;
if ( fork() == 0 ) {
execv( args[0], args ); // child: call execv with the path and the args
}
else {
wait( &status );
}

Using the comment Jerry Jeremiah said, instead of changing the declaration to a pointer, I changed the line[x] in the if statement to a pointer and checked for NULL.

scanf will attempt to get everything you asked for. You ask for five, it tries to get five.
sscanf(input, "%19s %19s %19s %19s %19s", line[0], line[1], line[2], line[3], line[4]);
Without the specification, it will take as many chars as the user is inclined to submit, thus resulting in a buffer overflow. Bad idea.
I would accept one argument at a time, or take it all as one lump.

Related

Why fscanf is giving segmentation fault while reading the data into a text file?

I am trying to read data from a text file with some data in it.
My text file:
1 dior Asfiya 20 abcde 12345
2 abde Sabah 17 saar 5757657
My code:
typedef long long int ulong;
struct Customer
{
char name[25];
int acc_no;
int age;
char gender[2];
char address[60];
ulong phone;
char password[10];
};
struct Customer add,check;
int new_acc()
{
int c;
FILE *p;
FILE *q;
p = fopen("test.txt","a");
q = fopen("test.txt","r");
if(p == NULL)
{
puts("Could't open file\n");
return(-1);
}
if(q == NULL)
{
puts("Couldn't open file\n");
return(-1);
}
account_no:
printf("\nAdd record");
printf("\nEnter account number: ");
scanf("%d",&check.acc_no);
while(c = fscanf(q,"%d %s %s %d %s %s %llu%[^\n]",&add.acc_no,add.password,add.name,&add.age,add.gender,add.address,&add.phone) == 7)
{
if(check.acc_no==add.acc_no)
{
printf("\nAccount no. already in use!");
goto account_no;
}
}
if(c != EOF){
goto details;
}
details:
add.acc_no = check.acc_no;
printf("\nEnter password: ");
scanf("%s",add.password);
printf("\nFull Name: ");
scanf("%s",add.name);
printf("\nEnter your age: ");
scanf("%d",&add.age);
printf("\nEnter your gender: ");
scanf(" %c",&add.gender);
printf("\nEnter address: ");
scanf("%s",add.address);
printf("\nEnter phone number: ");
scanf("%llu",&add.phone);
fprintf(p,"%d %s %s %d %s %s %lld\n",add.acc_no,add.password,add.name,add.age,add.gender,add.address,add.phone);
fclose(p);
fclose(q);
}
While debugging the code it gives segmentation fault when the while loop starts. It worked perfectly fine before adding the first line of data into the file. I am not able to figure out why is this happening.
Remove goto.
Refactor this part of your code please.
About your arrays, which await to store a string - did you take in account the string NULL terminator, that also needs a cell of the array to be stored?
In any case, I see gender has a size of 2, but your input suggests that it needs more.
Your input has 7 tokens:
1 dior Asfiya 20 abcde 12345
that means that gender will overflow, invoking Undefined Behavior (UB), since you will attempt to store a 4-character length string into an array of size 2. That might explain the crash.
You would have seen it by your check for 7 values, but there is also another problem in your code, as you can read in the next section.
You probably meant:
1 dior Asfiya 20 abcde f 12345
Change this:
while(c = fscanf(q,"%d %s %s %d %s %s %llu%[^\n]",&add.acc_no,add.password,add.name,&add.age,add.gender,add.address,&add.phone) == 7)
to this:
while((c = fscanf(q,"%d %s %s %d %s %s %llu%[^\n]",&add.acc_no,add.password,add.name,&add.age,add.gender,add.address,&add.phone)) == 7)
since, because of operators priority, the comparison happens before the assignment, which is not what you want. You want first to assign to your variable, and then compare, so use parentheses to specify the desired priority.
You're not setting c correctly when you write:
while(c = fscanf(q,"%d %s %s %d %s %s %llu%[^\n]",&add.acc_no,add.password,add.name,&add.age,add.gender,add.address,&add.phone) == 7)
This is setting c to the result of the == comparison, because == has higher precedence than =.
Also, you don't need [^\n] at the end of the format string. %d at the beginning will skip over any whitespace, including the newline at the end of the previous line.
It should be:
while((c = fscanf(q,"%d %s %s %d %s %s %llu",&add.acc_no,add.password,add.name,&add.age,add.gender,add.address,&add.phone)) == 7)
I'm not sure what you intend with
if(c != EOF){
goto details;
}
details:
Since details: is immediately after the if statement, you go there whether or not the condition succeeds.
You should get out of the habit of using goto, it's generally considered poor programming style. Put the code that should run when the condition is true inside the if. You can use continue and break to restart and end a loop.

Passing argument 2 of strcmp makes pointer from integer without a cast

void viewonechar(){
char name[25], c[25];
int n;
fp = fopen("Phonebook.txt","r");
printf ("\n\n Enter Character : ");
scanf ("%s",c);
fscanf (fp, "%s %d", name, &n);
while (!feof(fp)){
if ((strcmp(c, name[0])) == 0){ \\ Warning in here
printf (" %s +880%d\n",name, n);
}
fscanf (fp, "%s %d", name, &n);
}
printf ("\n\n");
fclose(fp);
menu();
}
When i compile the code, on the marked line this warning appears, "Passing argument 2 of strcmp makes pointer from integer without a cast". What exactly am i doing wrong?
int strcmp ( const char * str1, const char * str2 );
Since name is an array of char, name[0] is a char. strcmp takes char pointers as an arguments, so the char you have supplied is implicitly cast to an int-type and then - to the pointer, which produces an undefined behavior and most likely will lead to the segfault.
There are a number of problems.
Avoid scanf(). See: Why does everyone say not to use scanf? What should I use instead?. Use fgets() instead. Since you are reading just one char, char c[2] is sufficient (and also will not read in the \n character).
Always do the error checking for all the standard functions.
Your loop condition is wrong. See: Why is “while ( !feof (file) )” always wrong?
You don't need to scan once outside the loop if you fix the loop.
Since you only need to compare the first chars, you can simply use c[0] == name[0].
The below code fixes the above issues:
void viewonechar(void)
{
char name[25], c[25];
int n;
FILE fp = fopen("Phonebook.txt","r");
if (!fp) {
perror("fopen");
exit(1);
}
printf ("\n\n Enter Character : ");
if (fgets(c, sizeof c, stdin) == NULL) {
fprintf(stderr, "Input error\n");
exit(1);
}
while (fscanf (fp, "%24s %d", name, &n) == 2) {
if (c[0] == name[0]) {
printf (" %s +880%d\n",name, n);
}
}
printf ("\n\n");
fclose(fp);
menu();
}

Scanf input for char works once then is blank

I want to reference the input of name throughout the code I am writing, but for some reason after I use it successfully in the first printf(), the second printf() does not print the name.
int main()
{
char name[50];
char q1[1];
printf( " What is your name?\n");
scanf("%s", name);
printf( " Hi %s, Do you want to have some fun? [Y/N]\n",name);
scanf("%s",q1);
if(strcmp(q1,"Y") == 0||strcmp(q1,"y")==0)
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine"); goto endgame;
}
printf( "So %s, it's time to get started\n", name);
endgame:
getchar();
return 0;
}
The output for the entry 'Nick' is:
Hi Nick, do you want to have some fun? Awesome let's play So ,
let's get started
I would expect it to say:
So Nick, let's get started.
but for some reason char name is blank after it is used correctly the first time.
The problem, (as I assumed correctly) is with char q1[1]; and then using it like
scanf("%s",q1);
It is causing memory boundary overrun, because, a one-char array is not sufficient to hold a string of having only one element, as it lacks the space for the null-terminator required for the string. This invokes undefined behaviour.
Instead,
change char q1[1]; to char q1;
change scanf("%s",q1); to scanf(" %c", &q1);
change if(strcmp(q1,"Y") == 0||strcmp(q1,"y")==0) to if((q1 =='Y') || q1 == 'y')
That said, as a note,
The recommended signature of main() is int main(void).
To avoid the possibility of buffer overflow by longer input(s), it's better to limit the length of the input with scanf() by writing like
scanf("%49s", name);
Expand the size of your q1 buffer. scanf("%s", q1) doesn't have enough room to store the input. Remember that C uses a null character '\0' to terminate strings. If you don't account for that, the buffer could overrun into other memory causing undefined behavior. In this instance, it's probably overwriting memory allocated to name, so name ends up pointing to "\0ick". This causes printf(%s), which looks for '\0' to know when to stop printing, to think that the string is shorter than it really is.
The code works perfectly if you expand the buffer:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char name[50];
char q1[50];
printf( " What is your name?\n");
scanf("%49s", name);
printf( " Hi %s, Do you want to have some fun? [Y/N]\n",name);
scanf("%49s",q1);
if(strcmp(q1,"Y") == 0||strcmp(q1,"y")==0)
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine");
}
printf( "So %s, it's time to get started\n", name);
getchar();
return 0;
}
Output:
What is your name?
Nick
Hi Nick, Do you want to have some fun? [Y/N]
y
Awesome, let's play!
So Nick, it's time to get started
Note that I've added the qualifier %49s to avoid buffer overruns like this.
You could also circumvent the need for another string entirely by changing char q1[50] and scanf("%49s") to simply char q1 and scanf("%c%*c", &q1) (note the "address of" operator because q1 is no longer a pointer).
You'll probably even get a performance gain from this (albeit small), because strings are notorious memory hoggers. Comparing a single character is usually preferred over comparing strings.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char name[50];
char q1;
printf( " What is your name?\n");
scanf("%49s%*c", name);
printf( " Hi %s, Do you want to have some fun? [Y/N]\n",name);
scanf("%c%*c",&q1);
if(q1 == 'Y' || q1 == 'y')
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine");
}
printf( "So %s, it's time to get started\n", name);
getchar();
return 0;
}
if(q1 == 'Y' || q1 == 'y')
{
printf("Awesome, let's play!\n");
}
else
{
printf("Fine");
}
printf( "So %s, it's time to get started\n", name);
getchar();
return 0;
}
If you go this route, you have to ignore the enter key using the format specifier %*c because pressing enter sends a key to the stream as well.

Getting values from a mixed string

I am trying to get the values I need from a string using sscanf, but I can't get it done. Here is what I am trying to do:
I have a string which has this pattern
2 7 A BUL
(integer space integer space character space string of 3 elements)
I have to get each value separated by spaces and store it into variables.
This is my code:
sscanf(string[i],"%d %d %s %s",&e,&m,id,modelo);
The problem I'm having is that it only stores the first integers, ignoring the chars.
How can I fix this?
Thank you.
EDIT:
Here's the whole code for the function:
void le_lista(lista *l) {
int e,m;
char id[1],modelo[3],frase[20][12];
int linha=0;
while (1) {
fgets(frase[linha],12,stdin);
//
if (feof(stdin)) {
break;
}
//
linha++;
}
int i;
for(i=0;i<=linha;i++) {
sscanf(frase[i],"%d %d %s %s",&e,&m,&id,&modelo);
enfila(e,m,id,modelo,l);
//printf("%s",frase[i]);
}
printf("Linhas: %d",linha+1);
return;
}
char mystring[] = "2 7 A BUL"
x = strtok(mystring, " "); //separates by spaces
More here Split string with delimiters in C
Given that you want to recognize 2 7 A BUL, you cannot safely use:
int e,m;
char id[1], modelo[3];
sscanf(frase[i], "%d %d %s %s", &e, &m, &id, &modelo);
First, you shouldn't pass char (*)[] values where a char * is expected; do not take the address of an array; pass id and modelo only. GCC will warn you about that if you turn on warnings. If you're learning C, you can't afford not to have the warnings turned on (use -Wall at minimum; -Wall -Wextra if at all possible).
Next, the first %s will read an arbitrary number of characters and null-terminate the result. This is going to overwrite the end of the id array. And you can't safely read 3 characters into modelo either. Because of this, you have two stack overflow problems.
You should write either:
int e, m;
char id[2], modelo[4];
if (sscanf(frase[i], "%d %d %1s %3s", &e, &m, id, modelo) != 4)
...oops...
or perhaps:
int e, m;
char id;
char modelo[4];
if (sscanf(frase[i], "%d %d %c %3s", &e, &m, &id, modelo) != 4)
...oops...
Or, you could use char id[1]; and %c, but that is dangerous; the result is not a null-terminated string.
Your primary input loop is suspect too. You can use feof() as you did, immediately after the fgets(), but it is much more conventional to test the result of fgets() itself; it tells you whether it succeeded or not. That code should probably be:
char frase[20][12];
for (int linha = 0; i < sizeof(frase) / sizeof(frase[0]); i++)
{
if (fgets(frase[linha] ,sizeof(frase[linha]), stdin) == 0)
break;
}
This avoids repeating the 20 or the 12 but protects you from too many lines. It does not protect you from overlong lines; you could add:
size_t len = strlen(frase[linha]);
assert(len != 0);
if (frase[len-1] != '\n')
...input was too long...
to the loop.
You could also think about doing the sscanf() and call to enfila() in the main input loop; you would not then need the frase array to be multi-dimensional.
Putting all the changes together:
char frase[4096];
while (fgets(frase, sizeof(frase), stdin) != 0)
{
int e;
int m;
char id[2];
char modelo[4];
if (sscanf(frase, "%d %d %1s %3s", &e, &m, id, modelo) == 4)
enfila(e, m, id, modelo, l); // l is the parameter to the function
else
...report error...
}
Using fgets() and sscanf() was definitely the correct way to go. It means that error reporting can show the whole line of input, rather than whatever mangled remains scanf() left behind as unreadable.
use instead strtok which is more simpler and IMHO more solid since you can do a simple add error handling
e.g.
int j = 0;
for (char* token = strtok(frase[i]," "; token != NULL; token = strtok(NULL," ")
{
switch(j++)
{
case 0:
e = atoi(token);
break;
case 1:
m = atoi(token);
break;
case 2:
strncpy(id,token,sizeof(id)); // or strcpy_s
break;
case 3:
strncpy(modelo,token,sizeof(modelo)); // or strcpy_s
break;
default:
printf( "invalid number of arguments in line %d\n", i );
break;
}
}

Read int and char from the same file in C

I have a text file that looks like this:
i 3755
i 3633
i 4435
i 1434
how would I go about reading this as an input, I've tried using fscanf, but it keeps on giving me a random character after the 'i'
for example output would look like
i% 3755
i5 3633
etc.
Here is what I've been trying:
int data = 0;
char command;
if(fptr==NULL)
printf("File Cannot Be Read");
fscanf(fptr,"%c %d\n", &command, &data);
printf("%c " , command);
printf("%d\n" , data);
fscanf(fptr,"%s %d\n", &command, &data);
printf("%c " , command);
printf("%d\n" , data);
fscanf(fptr,"%s %d\n", &command, &data);
printf("%s " , command);
printf("%d\n" , data);
none of them seem to work. Thanks for your help in advance!
edit: Heres the working code for anybody that was having the same problem:
int data = 0;
char command;
fptr = fopen(argv[1], "r");
if(fptr==NULL)
printf("File Cannot Be Read");
while(fscanf(fptr,"%c %d \n", &command, &data) == 2)
{
if(command == 'i')
{
printf("insert found\n");
}
if(command == 'd')
{
printf("delete found\n");
}
}
}
fscanf(fptr,"%c %d", command, &data);
should be:
fscanf(fptr,"%c %d", &command, &data);
getchar(); // consume the newline character that fscanf left.
Assuming you defined:
char command;
int data;
For simplicity, I would recommend you to, in a loop, read a line using fgets() and then use strtok() to get different 'strings' and then cast according to your parsing methodology. But Mike is right as well, you can use fscanf(fptr, "%c %d\n", &command, &data) to read. When you're using a string, you can ignore the usage of "&" before the variable name. But when its a character or an integer or a float.. you will need to use the address of operator("&")
at this line :
fscanf(fptr,"%c %d", command, &data);
you should give it a pointer to command. like this :
fscanf(fptr,"%c %d", &command, &data);
the next line can't work because the reading cursor is already at the end of the file.
This is incorrect and will result in a buffer overrun:
fscanf(fptr,"%s %d", &command, &data);
as command has only enough space for a single char but fscanf() with format specifier "%s" will read until next whitespace and then write a terminating null character into command. Use a char[] to read and restrict the number of chars to be read.
Note that the NULL check just prints an error message, it does not actually prevent use of a NULL file pointer.
To ensure that each line is of the correct format use fgets() to read a line and then use sscanf() to read the fields. If fscanf() is used to read directly from the file then these two lines:
i
3755
are treated identically to the single line:
i 3755
as a new-line character is also whitespace.
Example:
if (fptr)
{
char line[1024];
while (fgets(line, 1024, fptr))
{
char command;
int data;
if (2 == sscanf(line, "%c %d", &command, &data))
{
/* Use 'command' and 'data'. */
}
}
}
in your fscanf function you have to expect the return to line by adding space at the end of format. Like this
fscanf(fptr,"%c %d ", &command, &data);
The space character replaces return to new line, tabulation and spaces
BTW I see that you use %s to read a char &command and this is incorrect
fscanf(fptr,"%s %d\n", &command, &data);
you have to use this instead
fscanf(fptr,"%c %d ", &command, &data);

Resources