How do you declare temporary strings for reuse in C? - c

I have a while loop that takes a user input until the user says quit but i am not sure how to declare the strings properly. If the user only inputs one word, the second variable will stay the same value from the previous loop.
int main(int argc, char * argv[]){
char user_input[25];
char var1[25], var2[25];
while(strcmp(var1, "quit") != 0){
clear_buffer(user_input);
fgets(user_input, 30, stdin);
sscanf(user_input, "%s %s", var1, var2);
do_stuff(var1, var2);
}
printf("%s", "Done\n");

i am not sure how to declare the strings properly
Strings aren't a type. They're a value pattern, and like other values we don't declare them; we assign them (using strcpy and other functions, in this case). If you say an int stores a multiple of ten, then it ends in 0... If you say an array of char stores a string, then it ends at the first '\0'. Do you see the pattern yet? We can store multiples of ten in different kinds of integer variables, and likewise for strings we can choose different types of character arrays. Similarly for storing numeric values in integer variables, when declaring an array to store a string, you'll want to be sure the array has enough space to store all of the characters for the string plus a '\0' at the end.
If the user only inputs one word, the second variable will stay the same value from the previous loop.
Check the return value of sscanf.
For example, when considering int x = sscanf(user_input, "%s %s", var1, var2); you may want to be sure the 2 arguments you give sscanf are assigned to, in which case you'll be checking that x == 2. If you only care about the first argument being assigned, then you'll be happy when x == 1, too. However, if x <= 0 then you can't trust either var1 or var2

Multiple problems
You never initialize var1 but you use strcmp(), you need to initialize var1 before trying to use strcmp(), if you want to initialize to an empty string just
var1[0] = '\0';
You pass 30 to fgets() but user_input can only hold 25 characters.
Now to why is the program behaving the way it is, you need to check that sscanf() did read both strings, if it doesn't it does not alter the passed parameters.
The scanf() function returns the number of specifiers matched by the input, so if
scanf() returns 0, it means both parameters are not initialized/modified.
scanf() returns 1 one of the parameters is initialized/modified.
When both were initialized/modified, then it will return 2, so checking for that would let you know what is going wrong, and you can print the input string to verify that scanf() was right, and the input was wrong..

Here is a modified version taking comments into account and protecting sscanf arguments:
int main(int argc, char *argv[]) {
char user_input[80];
char var1[25], var2[25];
while (fgets(user_input, (int)(sizeof user_input), stdin)) {
*var2 = '\0';
if (sscanf(user_input, "%24s %24s", var1, var2) > 0) {
if (!strcmp(var1, "quit"))
break;
do_stuff(var1, var2);
}
}
printf("Done\n");
return 0;
}

char user_input[50+1];
char var1[25], var2[25];
while(fgets(user_input, sizeof(user_input), stdin)){
int state = sscanf(user_input, "%24s %24s", var1, var2);
if(state == 1 && strcmp(var1, "quit") == 0)
break;
else if(state == 2)
do_stuff(var1, var2);
}

Related

How on earth to use char and if statements?

I'm a rookie programmer trying to run a simple code on VS code.
#include<stdio.h>
int main()
{
char* a;
printf("Enter a char");
scanf("%s",&a);
if (a = "yes")
{
printf("Number is 30");
}
else if (a = "no")
{
printf("Number is 50");
}
else{
printf("oops");
}
return 0;
}
I guess looking at the code you guys can figure out what I'm trying to do, if the user enters "yes", a specific sentence need to be displayed and similarly for "no".
The problem here is whatever I write in the input, it will always print the first statement, "Number is 30". I've tried running similar codes but ended up with the same output.
If possible, please explain me how to use char,strings,arrays with if-else statements.
There are several misunderstandings in the posted code.
First there is a misunderstanding of char versus string. A char is for instance a single letter, a single special character like ., ;, etc. (see note1) while a string is a serie of chars. So
'y' is a char
"yes" is a string
You print "Enter a char" but from the code it's obvious that you really want "Enter a string".
This leads to the next problem. To input a string using scanf you need to pass a "pointer to char". Your code pass "a pointer to pointer to char" due to the &. Further the passed pointer must point to some memory. So you need:
char a[10]; // Make it an array of char so that it can hold a string
printf("Enter a string, max 9 characters");
scanf("%9s", a); // No & before a and width specifier used to avoid buffer overflow
Now this part
if (a = "yes")
is not the way to compare two strings in C. For that you need the function strcmp - like:
if (strcmp(a, "yes") == 0)
Putting it together it's like:
int main()
{
char a[10];
printf("Enter a string, max 9 characters");
scanf("%9s", a);
if (strcmp(a, "yes") == 0){
printf("Number is 30");
}
else if (strcmp(a, "no") == 0)
{
printf("Number is 50");
}
else
{
printf("oops");
}
return 0;
}
That said, I don't understand why you print stuff like: "Number is 30" but that's kind of irrelevant here.
note1: The type char is actually an integer type, i.e. a number, but the common use is to map these numbers to characters using ASCII encoding.
There are different ways to initialize a variable to access C string.
char *char_ptr = "Hello";
This initializes char_ptr to point to the first character of the read-only string "Look Here".A C string initialized through a character pointer cannot be modified. When a C string is initialized this way, trying to modify any character pointed to by char_ptr is undefined behaviour. An undefined behaviour means that when a compiler encounters anything that triggers undefined behaviour, it is allowed to do anything it seems appropriate.
A more convenient way to define strings that can be modified is to use:
char str[];
This way you can modify any character in the C string
p.s you also need to use strcmp() for the if statement
You can take string input in C using
scanf(ā€œ%sā€, str);
And to compare the string you need to use:
strcmp(str1, "yes");

How to convert a string value to numerical value?

I have tried this code to separate my Str[] string into 2 string, but my problem is "I want to separate John(name) as string and 100(marks) as integer",How can I do it, any suggestion?
#include <stdio.h>
#include <string.h>
void main()
{
char Str[] = "John,100";
int i, j, xchange;
char name[50];
char marks[10];
j = 0; xchange = 0;
for(i=0; Str[i]!='\0'; i++){
if(Str[i]!=',' && xchange!=-1){
name[i] = Str[i];
}else{
xchange = -1;
}
if(xchange==-1){
marks[j++] = Str[i+1];
}
}
printf("Student name is %s\n", name);
printf("Student marks is %s", marks);
}
How to separate "John,100" into 2 strings?
There are three common approaches:
Use strtok() to split the string into individual tokens. This will modify the original string, but is quite simple to implement:
int main(void)
{
char line[] = "John,100;passed";
char *name, *score, *status;
/* Detach the initial part of the line,
up to the first comma, and set name
to point to that part. */
name = strtok(line, ",");
/* Detach the next part of the line,
up to the next comma or semicolon,
setting score to point to that part. */
score = strtok(NULL, ",;");
/* Detach the final part of the line,
setting status to point to it. */
status = strtok(NULL, "");
Note that if you change char line[] = "John,100"; then status will be NULL, but the code is otherwise safe to run.
So, in practice, if you required all three fields to exist in line, it would be sufficient to ensure the last one was not NULL:
if (!status) {
fprintf(stderr, "line[] did not have three fields!\n");
return EXIT_FAILURE;
}
Use sscanf() to convert the string. For example,
char line[] = "John,100";
char name[20];
int score;
if (sscanf(line, "%19[^,],%d", name, &score) != 2) {
fprintf(stderr, "Cannot parse line[] correctly.\n");
return EXIT_FAILURE;
}
Here, the 19 refers to the number of chars in name (one is always reserved for the end-of-string nul char, '\0'), and [^,] is a string conversion, consuming everything except a comma. %d converts an int. The return value is the number of successful conversions.
This approach does not modify the original string, and it allows you to try a number of different parsing patterns; as long as you try them the most complex one first, you can allow multiple input formats with very little added code. I do this regularly when taking 2D or 3D vectors as inputs.
The downside is that sscanf() (all functions in the scanf family) ignores overflow. For example, on 32-bit architectures, the largest int is 2147483647, but scanf functions will happily convert e.g. 9999999999 to 1410065407 (or some other value!) without returning an error. You can only assume the numerical inputs are sane and within the limits; you cannot verify.
Use helper functions to tokenise and/or parse the string.
Typically, the helper functions are something like
char *parse_string(char *source, char **to);
char *parse_long(char *source, long *to);
where source is a pointer to the next character in the string to be parsed, and to is a pointer to where the parsed value will be stored; or
char *next_string(char **source);
long next_long(char **source);
where source is a pointer to a pointer to the next character in the string to be parsed, and the return value is the value of the extracted token.
These tend to be longer than above, and if written by me, then quite paranoid about the inputs they accept. (I want my programs to complain if their input cannot be reliably parsed, rather than silently produce garbage.)
If the data is some variant of CSV (comma-separated values) read from a file, then the proper approach is a different one: instead of reading the file line by line, you read the file token by token.
The only "trick" is to remember the separator character that ended the token (you can use ungetc() for this), and use a different function to (read and ignore the rest of the tokens in the current record, and) consume the newline separator.

How to get the tuple form out of the one line input?

I got a code below.
If the user input (12,34,56,78),(1,2,3),
it will only output: 12,34,56,78,1,2,3
but I need to output (12,34,56,78) (1,2,3)
what should I do?
#include <string.h>
#include <stdio.h>
int main(){
char enter[100];
int a;
printf("Enter a string:");
scanf("%s",enter);
char *tuples=strtok(enter,"()");
while(tuples!=NULL){
printf("%s",tuples);
tuples=strtok(NULL,"()");
}
}
strchr() is an additional parsing tool that may help you. It provides a method to quickly locate a specific character within a string, and return a pointer to that location. This capability, along with sprintf() can be used to reconstruct the string without the middle ",". Following is an illustration.
int main(void)
{
char string[40] = {"(12,34,56,78),(1,2,3)"};
char *ret;
int len1,len2;
char *first = strdup(string);//a 2nd copy is needed to preserve the 1st half of string.
char last[20];
len1 = strlen(string);
//in your example, the ')' character is the closest unique char to the
//target ',', so select this unique character to get close...
ret = strchr(string, ')');//brings pointer to first ')'
ret += 2; //increment pointer past next occurring "," to get second half of string
strcpy(last, ret);
len2 = strlen(last);
first[len1-(len2+1)] = 0;//use length information to get first half
sprintf(string, "%s %s", first, last);
printf(string);
free(first);
return 0;
}
Outputs:
(12,34,56,78) (1,2,3)
A note on user input:
It occurred to me that legal syntax (you have stated no rules) for this seemingly simple scenario can easily result in variations that will make a general solution for parsing and string re-construction difficult. eg, input could be any of the following:
(12,34,56,78),(1,2,3)
( 12 , 34 , 56 , 78 ) , ( 1 , 2 , 3 )
(12,34,56,78) , (1,2,3)
(and many more)
Keep in mind, to build a general approach to handling user input, the complexity of the code needed to process the input will be inversely proportional to the bounds (rules) put on input. i.e. if there are zero rules, the complexity will be infinite. If there are many rules governing allowed input, the complexity of the code will be proportionally smaller.
A good example of unbounded input is the way people talk. There is very little convention followed in the way people choose to convey their thoughts. (natural language is similar in it variations to user input) A few examples illustrating the complexity needed to process natural language are listed here.
(The input rules in your post are not clearly stated, so any attempt at a trivial solution here, will not likely satisfy any but those with input having syntax similar to your example)
You need to use more advanced parsing than strtok. strtok, as you learn if you read the manual page (man strtok), will return the strings between each occurance of one of the delimiter characters, which for you is "()".
To achieve the result you want, you need code that:
Finds the opening parenthesis
Collects all numbers until a closing parenthesis
repeat from 1
This parsing can be done with calls to strtok, strstr, other string functions, or manually by parsing the string char for char.
When you have your list of touple contents you can print them surrounded by parentheses (without comma in between if you so desire).
if tuples do not contain subtuples:
void printTuples(char *s)
{
char *str;
char *tmps = s;
int flag = 0;
while (1)
{
str = strtok(tmps, "()");
tmps = NULL;
if (str == NULL) break;
if((flag = !flag)) printf("( %s )", str);
}
}
If the input format is constrained to two tuples, as in the example, then you can use a simple sscanf() construction to parse this. Note that in the solutions below, valid input consists of possible leading whitespace, followed by a ( character, followed by a non-empty string, followed by a ) character, possibly followed by more whitespace or a comma. Further input validation would be needed to verify that the strings between parentheses are indeed tuples.
#include <stdio.h>
int main(void)
{
char input[] = "(12, 34, 56, 78),(1, 2, 3)";
char tuple1[100];
char tuple2[100];
if (sscanf(input, " (%[^)]) , (%[^)]", tuple1, tuple2) == 2) {
printf("(%s) (%s)\n", tuple1, tuple2);
}
return 0;
}
Program output:
(12, 34, 56, 78) (1, 2, 3)
But, if the number of input tuples is unknown (a large number of tuples must not be expected, since enter has been declared char enter[100] in the example code), this method can be extended by using the %n scanf() directive. This directive stores the number of characters read thus far in its argument. A pointer to the next segment of the input string to be read can be formed by adding this offset value to a pointer to the initial input string.
The program below expects the tuples to be separated by commas, and will simply stop parsing if two tuples occur without a comma between them, or otherwise malformed input is encountered. Some care is required to handle this case, since the assignment to offset is not made if the final comma of the format string is not matched. Here, offset is initialized to zero, and reset to zero at the end of each loop iteration. Before the reset, if offset is already zero, then no assignment was made, meaning that the final comma was not matched (signalling the end of input), and the loop is exited.
#include <stdio.h>
int main(void)
{
char input[] = "(12, 34, 56, 78),(1, 2, 3), (4, 5, 6, 7)";
char tuple[100];
char *next = input;
int offset = 0;
int ret_val = 0;
do {
ret_val = sscanf(next, " (%[^)]) ,%n", tuple, &offset);
if (ret_val == 1) {
printf("(%s) ", tuple);
next += offset;
}
if (offset) {
offset = 0;
} else {
break;
}
} while (ret_val == 1);
putchar('\n');
return 0;
}
Program output:
(12, 34, 56, 78) (1, 2, 3) (4, 5, 6, 7)

Entering different types (in C)

I'm attempting to prompt the user to enter several numbers and, when the user enters a string, the program calculates the sum of those numbers. I'm having difficulty because I want to keep the program as simple as possible without creating other variables to store strings, etc.
int menu(int choice){
int total = 0, values = 0;
char *string = (char*) &values;
switch(choice){
case 1: printf("Enter your values separated by a whitespace: ");
while(string != "compute") {
scanf("%d",&values);
total = total + values;
}
}
return total;
}
I want the user to enter as many numbers as s/he wants (obviously within memory limits), so I have to continually anticipate an int (or other "number"), so what's the most effective way to also anticipate a string?
I know that the following line is a bit sketchy, but why exactly if I want the variable "string" to treat "values" like a string/char type?
char *string = (char*) &values;
To read a string you have to allocate some space for it. You cannot read it into an integer.
To support reading input that could be either an integer or a string, you have to read a string; and then you can try to convert the string to integer.
For example:
char buffer[50];
scanf("%49s", buffer);
if ( 0 == strcmp(buffer, "compute") )
return 0; // they typed "compute"
if ( 0 == sscanf(buffer, "%d", &number) )
break; // they typed something that was not a number
total += number;
When you write like this
int total = 0, values = 0;
char *string = (char*) &values;
You set the pointer string to point to the integer value values so if the user enters a value that is larger than sizeof(values) i.e. sizeof(int), the program will crash.
Instead use a dedicated buffer for the input string
char string[128] = {0};
scanf can be used for input but it is safer to use fgets() to minimize the risk of a buffer overrun:
fgets( string, sizeof(string), stdin );
if you need to hold the individual values entered declare an array of ints e.g.
int values[100];
when the user has enters something check the content of 'string' and see if it
contains compute - it may be enough checking the first char - e.g. if ( string[0] == 'c' ) else convert the string to int and place it in the array of values:
values[i++] = atoi(string);
EDIT:
As McNabb pointed out fgets() adds a \n to the string so if you want to compare the whole string you must take that into account e.g.
if ( !strncmp( "compute", string, strlen("compute") )
{...}
The most efficient method would be to read in a string (use fgets()), then try to decide what it is. If it's an integer, you can use atoi or strtol to convert it. If it's a float, you can use strtod. Otherwise, you can parse the string however you want.
So you'll end up with something like this:
char str[15];
long sum = 0, val;
char* ptr;
while (1)
{
fgets(str, 15, stdin);
if (0 == strcmp(str, "compute"))
{
printf("sum: %d\n", sum);
break;
}
val = strtol(str, &ptr, 10);
// error-check here.
sum += val;
}
Another, simpler option, might be to read integers (using scanf, as in your code above) until end-of-file, then print the sum. That approach has some limitations: you need to give your input through some channel that has a defined EOF, and you can't receive more input after the end of your list of integers. Use a specific value (such as 0) as a sentinel, as Havenard suggested, does not have these drawbacks, but doesn't allow the sentinel value to appear in your list of numbers either.
Not sure why are you trying to compare strings at this point. If you just want to read whitespace separated integers until a non-integer value is entered, let scanf do the work for you:
int menu(int choice){
int total = 0;
int value;
switch(choice){
case 1: printf("Enter your values separated by a whitespace: ");
while(scanf("%d",&value) > 0) {
total += value;
}
}
/* Here, use scanf to get the first non-integer value entered */
return total;
}

Comparing 2 Strings, one in a struct other not C programming

I have this database and I Need to check whether a Product Name is already in the database otherwise I ask the user to input another one.
The problem is this:
I'm trying to compare a string (the Product Name) found inside the struct with the string the user inputs.
The coding of the struct, the user input part and the search method are here below:
product Structure
typedef struct
{
char pName[100];
char pDescription [100];
float pPrice;
int pStock;
int pOrder;
}product;
the checkProduct method:
int checkProduct (char nameCheck[100])
{
product temp;
p.pName = nameCheck;
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName))
{
return 1;
}
}
return 0;
}
and the user input part [part of the code]:
char nameCheck[100];
gets (nameCheck);
checkProduct (nameCheck);
while (checkProduct == 1)
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
{
continue;
}
}
p.pName = nameCheck;
Now I am having the following errors (I Use ECLIPSE):
on the line
while (checkProduct == 1) [found in the user input] is giving me:
"comparison between pointer and integer - enabled by default" marked by a yellow warning triangle
p.pName = nameCheck; is marked as a red cross and stopping my compiling saying:
"incompatible types when assigning to type 'char [100] from type 'char*'
^---- Is giving me trouble BOTH in the userinput AND when I'm comparing strings.
Any suggestions how I can fix it or maybe how I can deference it? I can't understand why in the struct the char pName is being marked as '*' whereas in the char[100] it's not.
Any brief explanation please?
Thank you in advance
EDIT: After emending the code with some of below:
THIS Is the INPUT NAME OF PRODUCT section;
char *nameCheck;
nameCheck = "";
fgets(nameCheck,sizeof nameCheck, stdin);
checkProduct (nameCheck);
int value = checkProduct (nameCheck);
while (value == 1)
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
{
}
}
strcpy (p.pName, nameCheck);
this is the new checkName method
int checkProduct (char *nameCheck)
{
product temp;
strcpy (p.pName, nameCheck);
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName) == 0)
{
return 1;
}
}
return 0;
}
p.pName = nameCheck;
is wrong as you try to assign address of one array to another. What you probably want is to copy it.
Use strcpy() instead.
strcpy(p.pName, nameCheck);
while (checkProduct == 1)
Since checkProduct is a function, the above condition will always be false as the address of function won't be equal to 1. You can store the return value in another integer like this:
int value = checkProduct(nameCheck);
while (value == 1)
/* rest of the code */
Or rather simply:
while ( checkProduct(nameCheck) == 1 ) {
...
Note - I've not checked entire code, there might be other bugs apart from this one. Btw, if you are new to programming, you can start with small examples from textbooks and then work towards slightly complex stuff.
int checkProduct (char nameCheck[100])
Note that the type signature is a lie. The signature should be
int checkProduct(char *nameCheck)
since the argument the function expects and receives is a pointer to a char, or, to document it for the user that the argument should be a pointer to the first element of a 0-terminated char array
int checkProduct(char nameCheck[])
Arrays are never passed as arguments to functions, as function arguments, and in most circumstances [the exceptions are when the array is the operand of sizeof, _Alignof or the address operator &] are converted to pointers to the first element.
{
product temp;
p.pName = nameCheck;
Arrays are not assignable. The only time you can have an array name on the left of a = is initialisation at the point where the array is declared.
You probably want
strcpy(p.pName, nameCheck);
there.
rewind (pfp);
while (fread(&temp,STRUCTSIZE,1,pfp)==1)
{
if (strcmp (temp.pName,p.pName))
strcmp returns a negative value if the first argument is lexicographically smaller than the second, 0 if both arguments are equal, and a positive value if the first is lexicographically larger than the second.
You probably want
if (strcmp(temp.pName, p.pName) == 0)
there.
gets (nameCheck);
Never use gets. It is extremely unsafe (and has been remoed from the language in the last standard, yay). Use
fgets(nameCheck, sizeof nameCheck, stdin);
but that stores the newline in the buffer if there is enough space, so you have to overwrite that with 0 if present.
If you are on a POSIX system and don't need to care about portability, you can use getline() to read in a line without storing the trailing newline.
checkProduct (nameCheck);
You check whether the product is known, but throw away the result. Store it in a variable.
while (checkProduct == 1)
checkProduct is a function. In almost all circumstances, a function designator is converted into a pointer, hence the warning about the comparison between a pointer and an integer. You meant to compare to the value of the call you should have stored above.
{
printf ("Product Already Exists!\n Enter another!\n");
while (getchar() !='\n')
You read in characters without storing them. So you will never change the contents of nameCheck, and then be trapped in an infinite loop.
{
continue;
}
If the only statement in a loop body is continue;, you should leave the body empty.
}
p.pName = nameCheck;
Once again, you can't assign to an array.
Concerning the edit,
char *nameCheck;
nameCheck = "";
fgets(nameCheck,sizeof nameCheck, stdin);
you have changed nameCheck from an array to a pointer. That means that sizeof nameCheck now doesn't give the number of chars you can store in the array, but the size of a pointer to char, which is independent of what it points to (usually 4 on 32-bit systems and 8 on 64-bit systems).
And you let that pointer point to a string literal "", which is the reason for the crash. Attempting to modify string literals is undefined behaviour, and more often than not leads to a crash, since string literals are usually stored in a read-only segment of the memory nowadays.
You should have left it at
char nameCheck[100];
fgets(nameCheck, sizeof nameCheck, stdin);
and then you can use sizeof nameCheck to tell fgets how many characters it may read, or, alternatively, you could have a pointer and malloc some memory,
#define NAME_LENGTH 100
char *nameCheck = malloc(NAME_LENGTH);
if (nameCheck == NULL) {
// malloc failed, handle it if possible, or
exit(EXIT_FAILURE);
}
fgets(nameCheck, NAME_LENGTH, stdin);
Either way, after getting input, remove the newline if there is one:
size_t len = strlen(nameCheck);
if (len > 0 && nameCheck[len-1] == '\n') {
nameCheck[len-1] = 0;
}
// Does windows also add a '\r' when reading from stdin?
if (len > 1 && nameCheck[len-2] == '\r') {
nameCheck[len-2] = 0;
}

Resources