This question already has an answer here:
How to use sscanf in loops?
(1 answer)
Closed 1 year ago.
I was trying to create a AVLtree using stdin input in C, the format I was trying to in put is: (7,19) (5,16) (9,13). The code I am using is
char str[1024];
int key,value;
while (fgets(str,256,stdin)!=NULL)//stop read files when reached end of the line
{
if(*str == "\n")
{
break;
}
if(sscanf(str,"(%d, %d)",&key, &value) == 2)// if the function can address both number
{
//InsertNode(AVLtree,key,value);
printf("%d, %d\n", key,value);
}
else
{
printf("cannot get correct key & Value");
exit(1)
}
Basically, I am saying that read input from stdin using "fgets" function, in (%d, %d) format. Once it reached the end of the line, then stop. But by using this code, it only allows me to type in those nodes one per line, instead of typing all of them and end with enter. Is there any way to fix this?
As you don't know how many value pairs the user will enter you have to use a loop.
After a successful sscanf you can search for a closing parenthesis ')' and start the next sscanf from the position after it.
To avoid an error message in case of additional spaces between or after the data I added code to skip whitespace and changed the format string to be more flexible.
I use a function for skipping space and checki9ng for end-of-string because I need it at two places.
Edit:
As shown in the answer to a similar question How to use sscanf in loops? you can get the offset to the next position directly from sscanf using the %n format specifier, so the strchr is not necessary. I won't change this answer here. See the other question and adapt the code accordingly.
(There are more ways to solve this problem.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* skip whitespace and check for end of string */
int skip_space(char **pos)
{
while(isspace(**pos))
{
(*pos)++;
}
return (**pos == '\0');
}
int main()
{
char str[1024];
char *pos;
int key,value;
printf("enter key-value pairs of integer numbers like (a,b)(c,d): ");
while (fgets(str,sizeof(str),stdin)!=NULL)//stop read files when reached end of the line
{
pos = str;
if(skip_space(&pos))
{
break;
}
do
{
/* added spaces to format string for more flexibility */
if(sscanf(pos," (%d , %d )",&key, &value) == 2)// if the function can address both number
{
//InsertNode(AVLtree,key,value);
printf("%d, %d\n", key,value);
}
else
{
printf("cannot get correct key & Value at position %d: %s\n", (int)(pos - str), pos);
exit(1);
}
/* since scanf was successful we must have a ')' in the input, so NULL should not occur */
pos = strchr(pos, ')');
/* skip ')' */
pos++;
} while(skip_space(&pos) == 0);
}
return 0;
}
Result:
enter key-value pairs of integer numbers like (a,b)(c,d): (1,2) ( 3 , 4 ) (5,6)(7.0,8)
1, 2
3, 4
5, 6
cannot get correct key & Value at position 21: (7.0,8)
enter key-value pairs of integer numbers like (a,b)(c,d): (1,2) ( 3 , 4 ) (5,6)
1, 2
3, 4
5, 6
(7,8)
7, 8
Unrelated error: if(*str == "\n") is wrong.
If you want to check for a newline character at the beginning of the string, use
if(*str == '\n')
If you want to check if the whole string is equal to a string that consists of a single newline, use
if(strcmp(str, "\n") == 0)
or simplified
if(!strcmp(str, "\n"))
Related
Hi I'm having a really hard time reading user input in C a specific way...
I want to read a users input, in which case my program will verify the first three characters as characters and the last four digits as digits.
And I don't know how to make sure that their input is 7 characters in total (3 chars, 4 ints) ex: ULI0788.
I don't want to use arrays, ex arr[12];
Currently I am at the point where I'm learning memory allocation & pointers, thus I am encouraged to use this rather than arrays if possible
for example
char itemID;
printf("Enter an ID of ITEM. (max 3 characters, 4 digits)");
scanf("%s", itemID);
I've done some googling and tried user suggestions but none do both of what I'm looking for, verifying each character/digit and setting a max of 7 total characters/digits. Or I just don't understand properly how to use the suggestions
googled
googled2
googled3
googled4
googled5
googled6
googled7
googled8
googled9
googled10
"I want to read a users input, in which case my program will verify the first three characters as characters and the last four digits as digits. And I don't know how to make sure that their input is 7 characters in total (3 chars, 4 ints)...I don't want to use arrays"
Without the ability to use C strings, the above is constrained to simply inputting a series of characters, then treating and testing them as discrete items:
bool test7char(void)
{
int Char;
for(int i=0;i<7;i++)
{
Char = getc(stdin);
if(i<3)
{
if(!isalpha(Char)) return false;
}
else if(!isdigit(Char)) return false;
}
return true;
}
Usage:
int main(void)
{
printf("Enter an ID of ITEM. (max 3 characters, 4 digits)");
while(!test7char())
{
printf("Mistake - Re-enter an ID of ITEM. (max 3 characters, 4 digits)");
}
return 0;
}
EDIT - "I am just trying to figure out how to answer my question using memory allocation & maybe pointers"
Using pointer: (In memory, this pointer will point to a series of alphanumeric characters, terminated by \0, i.e. a string.)
#define STR_SIZE 7 + 1
BOOL test7char(const char *str);
int main(void)
{
char *str = calloc(STR_SIZE, 1);
if(str)
{
printf("Enter an ID of ITEM. (max 3 characters, 4 digits)");
if(fgets(str, STR_SIZE, stdin))
{
while(!test7char(str))
{
printf("\nMistake - Re-enter an ID of ITEM. (max 3 characters, 4 digits)");
if(!fgets(str, STR_SIZE, stdin))
{
//message user of error
break;
}
}
}
free(str);
}
return 0;
}
bool test7char(const char *str)
{
if(!str) return false;
if(strlen(str) != STR_SIZE -1) return false;
for(int i=0;i<7;i++)
{
if(i<3)
{
if(!isalpha(str[i])) return false;
}
else if(!isdigit(str[i])) return false;
}
return true;
}
I would advise you to use both fgets and sscanf:
fgets allows you to read a certain number of characters (which can be read from stdin).
sscanf allows you to read formatted input from a string (which you got from fgets).
By combining those, you can read 7 characters from standard input (8 if you add the \0) and then parse those to get the two values you're looking for.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
// 8 chars long for string and terminating `\0`
char *ID = calloc(8, 1);
// extra char for same reason as above
char *IDchar = calloc(4, 1);
int IDnum, processed;
// get the string and verify it's 7 characters long
if (fgets(ID, 8, stdin) && strlen(ID) == 7)
sscanf(ID, "%3s%4d%n", IDchar, &IDnum, &processed);
if (processed == 7)
printf("success");
else
printf("failure");
}
The %n will collect the number of characters processed by the sscanf, ensuring you parsed the right number of characters.
note that this is a VERY dangerous parameter, and you should always verify your input length before using it.
Edit:
If you do not want to use arrays at all, and only want to verify the input format without storing or reusing it, you can use getc to read the characters one at a time and verify their value:
#include <stdio.h>
#include <ctype.h>
int isEnd(int c)
{
return c == '\n' || c == EOF;
}
void main()
{
int tmp;
int valid = 1;
//check the first 3 characters
for(int v = 0; v < 3 && valid; v++)
{
// read a char on standard input
tmp = fgetc(stdin);
// check if tmp is a letter
valid = islower(tmp) || isupper(tmp);
}
//check the next 4 characters
for(int v = 0; v < 4 && valid; v++)
{
// read a char on standard input
tmp = fgetc(stdin);
// check if tmp is a numeral
valid = isdigit(tmp);
}
if (valid)
{
printf("format OK\n");
// Check that the input is finished (only if format is OK)
tmp = fgetc(stdin);
if (isEnd(tmp))
printf("length OK\n");
else
printf("length KO: %d\n", tmp);
}
else
{
printf("format KO\n");
}
}
As I said before, this will only check the validity of the input, not store it or allow you to reuse it. But it does not use arrays.
Edit 2:
Another thing to watch out for with fgetc or getc is that, though it will manage longer inputs properly, it will get stuck waiting for the chars to be provided if there aren't enough. Thus make sure to exit the moment you read an incorrect character (if it's an end-of-line char, there won't be any more coming)
Edit 3:
Of course I'd forgotten malloc.
Edited the first answer.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
What I want to do is to input two words in C but one word can be omitted sometimes.
To be more precise there is exactly one word necessarily and the other number of words may vary. which can be zero ,one , two or three
As an example sometimes the input can be
Hello world
while sometimes it can be just
Hello
the reason why i want this to be done is I'm taking inputs in a while loop
(I have to take input continuously and process according to them)
I have used
"%s %[^\n]%*c"
but it waits until I enter another word. (Because its expecting a another word after space)
I tried googling and all the aspects of scanf but didn't find any solution.
Any help appreciated ! Thanks...
This is a case where not using scanf() is astonishingly simple:
char line[1024];
while (fgets(line, 1024, stdin))
{
char *word = strtok(line, " \t\n");
while (word)
{
// do something with word
word = strtok(0, " \t\n"); // <- next word
}
}
As for scanf() and how/why not to use it, I suggest reading my beginners' guide away from scanf().
Side note: strtok() isn't thread-safe. If this is a concern, there are alternatives like the POSIX strtok_r() or the C11 strtok_s(). Beware Microsoft also has a strtok_s() which is different from the standard C version.
scanf() does not seem to be the right tool for your purpose, you should instead read one line at a time with fgets() and process it according to the words that are present.
If you insist on using scanf(), "%s %[^\n]%*c" does not work because the space matches newlines. Assuming array sizes of 20 bytes, you could use "%19[^ \t\n]%*[ \t]%19[^ \t\n]" and check the return value to see how many words have been read on a single line (0, 1, 2 or EOF), but if you expect a variable number of words, fgets() is a much preferred solution.
Here is an example for up to 3 words per line:
int scan_input(void) {
char word1[20], word2[20], word3[20];
int n, c;
for (;;) {
switch (n = scanf("%19[^ \t\n]%*[ \t]%19[^ \t\n]%*[ \t]%19[^ \t\n]",
word1, word2, word3)) {
case EOF:
return EOF;
case 0:
/* no word on the line, skip the empty line */
break;
case 1:
/* one word on the line */
printf("1 word: %s\n", word1);
break;
case 2:
/* two word on the line */
printf("2 words: %s %s\n", word1, word2);
break;
case 3:
/* three word on the line */
printf("3 words: %s %s\n", word1, word2, word3);
break;
}
/* ignore the rest of the line */
while ((c = getchar()) != EOF && c != '\n')
continue;
}
}
Here is an example using fgets():
int scan_input(void) {
char buf[256];
int n, pos, len;
while (fgets(buf, sizeof buf, stdin)) {
/* scan the line, skipping whitespace */
for (n = 1, pos = 0; buf[pos += strspn(buf, " \t\n")] != '\0'; n++) {
/* compute the word length */
len = strcspn(buf + pos, " \t\n");
/* found word at offset `pos` of length `len` bytes */
printf("word %d: %.*s\n", n, len, buf + pos);
pos += len;
}
}
return EOF;
}
It is not a problem if you want omit one word sometimes. But i would recommend you not using scanf()use fgets()instead and process your string after you have read it.
Syntax
char chr[MAX];
while(i<10){
fgets(chr,MAX, stdin);
}
If you want to omit your second word, just check for a whitespace in your string and terminate it on that place/index the String with \0.
I agree with the others that scanf() is probably not the right tool here.
Another way could be to use fgets() and strchr() and a pointer as in
char str[100], *ptr;
while( (fgets(str, sizeof(str), stdin))!=NULL ){
if( (ptr=strchr(str, '\n'))!=NULL )
{
*ptr=0;
}
if( (ptr=strchr(str, ' '))!=NULL )
{
*ptr=0;
ptr++;
if( strchr(ptr, ' ')==NULL )
{
printf("\nTwo words: %s, %s", str, ptr);
}
else
{
printf("\nMore than two words!");
}
}
else//only one word
{
printf("\nOne word: %s", str);
}
}
fgets() is used to read in the input into a character array str. fgets() returns NULL on error.
Since fgets() also reads in any trailing \n, we check the string for \n if it's there, it's replaced with a \0 to denote end of string in the
if( (ptr=strchr(str, '\n'))!=NULL )
{
*ptr=0;
}
part.
strchr() returns NULL if the searched character is not present in the string.
strchr() is again used to check for a space in str. If none is found, only one word was read and otherwise multiple words were input.
If more than one word is there, the space is replaced with a \0 using the pointer returned by strchr() so that doing a
printf("%s", str);
will now print only the first word and the pointer is incremented so that it now points to the beginning of the second word. This is done in
if( (ptr=strchr(str, ' '))!=NULL )
{
*ptr=0;
ptr++;
Now, strchr() is once again used to check for another case in which case more than two words were input. Otherwise only two words were input and the second word is output using
printf("%s", ptr);
You could use a similar approach to read more words.
I'm writing a C program that parses user input into a char, and two strings of set length. The user input is stored into a buffer using fgets, and then parsed with sscanf. The trouble is, the three fields have a maximum length. If a string exceeds this length, the remaining characters before the next whitespace should be consumed/discarded.
#include <stdio.h>
#define IN_BUF_SIZE 256
int main(void) {
char inputStr[IN_BUF_SIZE];
char command;
char firstname[6];
char surname[6];
fgets(inputStr, IN_BUF_SIZE, stdin);
sscanf(inputStr, "%c %5s %5s", &command, firstname, surname);
printf("%c %s %s\n", command, firstname, surname);
}
So, with an input of
a bbbbbbbb cc
the desired output would be
a bbbbb cc
but is instead the output is
a bbbbb bbb
Using a format specifier "%c%*s %5s%*s %5s%*s" runs into the opposite problem, where each substring needs to exceed the set length to get to the desired outcome.
Is there way to achieve this by using format specifiers, or is the only way saving the substrings in buffers of their own before cutting them down to the desired length?
In addition to the other answers, never forget when facing string parsing problems, you always have the option of simply walking a pointer down the string to accomplish any type parsing you require. When you read your string into buffer (my buf below), you have an array of characters you are free to analyze manually (either with array indexes, e.g. buffer[i] or by assigning a pointer to the beginning, e.g. char *p = buffer;) With your string, you have the following in buffer with p pointing to the first character in buffer:
--------------------------------
|a| |b|b|b|b|b|b|b|b| |c|c|\n|0| contents
--------------------------------
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 index
|
p
To test the character pointed to by p, you simply dereference the pointer, e.g. *p. So to test whether you have an initial character between a-z followed by a space at the beginning of buffer, you simply need do:
/* validate first char is 'a-z' and followed by ' ' */
if (*p && 'a' <= *p && *p <= 'z' && *(p + 1) == ' ') {
cmd = *p;
p += 2; /* advance pointer to next char following ' ' */
}
note:, you are testing *p first, (which is the shorthand for *p != 0 or the equivalent *p != '\0') to validate the string is not empty (e.g. the first char isn't the nul-byte) before proceeding with further tests. You would also include an else { /* handle error */ } in the event any one of the tests failed (meaning you have no command followed by a space).
When you are done, your are left with p pointing to the third character in buffer, e.g.:
--------------------------------
|a| |b|b|b|b|b|b|b|b| |c|c|\n|0| contents
--------------------------------
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 index
|
p
Now your job is simply, just advance by no more than 5 characters (or until the next space is encountered, assigning the characters to firstname and then nul-terminate following the last character:
/* read up to NLIM chars into fname */
for (n = 0; n < NMLIM && *p && *p != ' ' && *p != '\n'; p++)
fname[n++] = *p;
fname[n] = 0; /* nul terminate */
note: since fgets reads and includes the trailing '\n' in buffer, you should also test for the newline.
When you exit the loop, p is pointing to the seventh character in the buffer as follows:
--------------------------------
|a| |b|b|b|b|b|b|b|b| |c|c|\n|0| contents
--------------------------------
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 index
|
p
You now simply read forward until you encounter the next space and then advance past the space, e.g.:
/* discard remaining chars up to next ' ' */
while (*p && *p != ' ') p++;
p++; /* advance to next char */
note: if you exited the firstname loop pointing at a space, the above code does not execute.
Finally, all you do is repeat the same loop for surname that you did for firstname. Putting all the pieces of the puzzle together, you could do something similar to the following:
#include <stdio.h>
enum { NMLIM = 5, BUFSIZE = 256 };
int main (void) {
char buf[BUFSIZE] = "";
while (fgets (buf, BUFSIZE, stdin)) {
char *p = buf, cmd, /* start & end pointers */
fname[NMLIM+1] = "",
sname[NMLIM+1] = "";
size_t n = 0;
/* validate first char is 'a-z' and followed by ' ' */
if (*p && 'a' <= *p && *p <= 'z' && *(p + 1) == ' ') {
cmd = *p;
p += 2; /* advance pointer to next char following ' ' */
}
else { /* handle error */
fprintf (stderr, "error: no single command followed by space.\n");
return 1;
}
/* read up to NLIM chars into fname */
for (n = 0; n < NMLIM && *p && *p != ' ' && *p != '\n'; p++)
fname[n++] = *p;
fname[n] = 0; /* nul terminate */
/* discard remaining chars up to next ' ' */
while (*p && *p != ' ') p++;
p++; /* advance to next char */
/* read up to NLIM chars into sname */
for (n = 0; n < NMLIM && *p && *p != ' ' && *p != '\n'; p++)
sname[n++] = *p;
sname[n] = 0; /* nul terminate */
printf ("input : %soutput : %c %s %s\n",
buf, cmd, fname, sname);
}
return 0;
}
Example Use/Output
$ echo "a bbbbbbbb cc" | ./bin/walkptr
input : a bbbbbbbb cc
output : a bbbbb cc
Look things over an let me know if you have any questions. No matter how elaborate the string or what you need from it, you can always get what you need by simply walking a pointer (or a pair of pointers) down the length of the string.
One way to split the input buffer as OP desires is to use multiple calls to sscanf(), and to use the %n conversion specifier to keep track of the number of characters read. In the code below, the input string is scanned in three stages.
First, the pointer strPos is assigned to point to the first character of inputStr. Then the input string is scanned with " %c%n%*[^ ]%n". This format string skips over any initial whitespaces that a user might enter before the first character, and stores the first character in command. The %n directive tells sscanf() to store the number of characters read so far in the variable n; then the *[^ ] directive tells sscanf() to read and ignore any characters until a whitespace character is encountered. This effectively skips over any remaining characters that were entered after the initial command character. The %n directive appears again, and overwrites the previous value with the number of characters read until this point. The reason for using %n twice is that, if the user enters a character followed by a whitespace (as expected), the second directive will find no matches, and sscanf() will exit without ever reaching the final %n directive.
The pointer strPos is moved to the beginning of the remaining string by adding n to it, and sscanf() is called a second time, this time with "%5s%n%*[^ ]%n". Here, up to 5 characters are read into the character array firstname[], the number of characters read is saved by the %n directive, any remaining non-whitespace characters are read and ignored, and finally, if the scan made it this far, the number of characters read is saved again.
strPos is increased by n again, and the final scan only needs "%s" to complete the task.
Note that the return value of fgets() is checked to be sure that it was successful. The call to fgets() was changed slightly to:
fgets(inputStr, sizeof inputStr, stdin)
The sizeof operator is used here instead of IN_BUF_SIZE. This way, if the declaration of inputStr is changed later, this line of code will still be correct. Note that the sizeof operator works here because inputStr is an array, and arrays do not decay to pointers in sizeof expressions. But, if inputStr were passed into a function, sizeof could not be used in this way inside the function, because arrays decay to pointers in most expressions, including function calls. Some, #DavidC.Rankin, prefer constants as OP has used. If this seems confusing, I would suggest sticking with the constant IN_BUF_SIZE.
Also note that the return values for each of the calls to sscanf() are checked to be certain that the input matches expectations. For example, if the user enters a command and a first name, but no surname, the program will print an error message and exit. It is worth pointing out that, if the user enters say, a command character and first name only, after the second sscanf() the match may have failed on \n, and strPtr is then incremented to point to the \0 and so is still in bounds. But this relies on the newline being in the string. With no newline, the match might fail on \0, and then strPtr would be incremented out of bounds before the next call to sscanf(). Fortunately, fgets() retains the newline, unless the input line is larger than the specified size of the buffer. Then there is no \n, only the \0 terminator. A more robust program would check the input string for \n, and add one if needed. It would not hurt to increase the size of IN_BUF_SIZE.
#include <stdio.h>
#include <stdlib.h>
#define IN_BUF_SIZE 256
int main(void)
{
char inputStr[IN_BUF_SIZE];
char command;
char firstname[6];
char surname[6];
char *strPos = inputStr; // next scan location
int n = 0; // holds number of characters read
if (fgets(inputStr, sizeof inputStr, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
exit(EXIT_FAILURE);
}
if (sscanf(strPos, " %c%n%*[^ ]%n", &command, &n, &n) < 1) {
fprintf(stderr, "Input formatting error: command\n");
exit(EXIT_FAILURE);
}
strPos += n;
if (sscanf(strPos, "%5s%n%*[^ ]%n", firstname, &n, &n) < 1) {
fprintf(stderr, "Input formatting error: firstname\n");
exit(EXIT_FAILURE);
}
strPos += n;
if (sscanf(strPos, "%5s", surname) < 1) {
fprintf(stderr, "Input formatting error: surname\n");
exit(EXIT_FAILURE);
}
printf("%c %s %s\n", command, firstname, surname);
}
Sample interaction:
a Zaphod Beeblebrox
a Zapho Beebl
The fscanf() functions have a reputation for being subtle and error-prone; the format strings used above may seem a little bit tricky. By writing a function to skip to the next word in the input string, the calls to sscanf() can be simplified. In the code below, skipToNext() takes a pointer to a string as input; if the first character of the string is a \0 terminator, the pointer is returned unchanged. All initial non-whitespace characters are skipped over, then any whitespace characters are skipped, up to the next non-whitespace character (which may be a \0). A pointer is returned to this non-whitespace character.
The resulting program is a little bit longer than the previous program, but it may be easier to understand, and it certainly has simpler format strings. This program does differ from the first in that it no longer accepts leading whitespace in the string. If the user enters whitespace before the command character, this is considered erroneous input.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define IN_BUF_SIZE 256
char * skipToNext(char *);
int main(void)
{
char inputStr[IN_BUF_SIZE];
char command;
char firstname[6];
char surname[6];
char *strPos = inputStr; // next scan location
if (fgets(inputStr, sizeof inputStr, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
exit(EXIT_FAILURE);
}
if (sscanf(strPos, "%c", &command) != 1 || isspace(command)) {
fprintf(stderr, "Input formatting error: command\n");
exit(EXIT_FAILURE);
}
strPos = skipToNext(strPos);
if (sscanf(strPos, "%5s", firstname) != 1) {
fprintf(stderr, "Input formatting error: firstname\n");
exit(EXIT_FAILURE);
}
strPos = skipToNext(strPos);
if (sscanf(strPos, "%5s", surname) != 1) {
fprintf(stderr, "Input formatting error: surname\n");
exit(EXIT_FAILURE);
}
printf("%c %s %s\n", command, firstname, surname);
}
char * skipToNext(char *c)
{
int inWord = isspace(*c) ? 0 : 1;
if (inWord && *c != '\0') {
while (!isspace(*c)) {
++c;
}
}
inWord = 0;
while (isspace(*c)) {
++c;
}
return c;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
If the user enters any character other than ACGT (sequence does not matter) then it should print "INVALID". The user just keeps entering the sequence not more than 250 characters.
#include <stdio.h>
#include <string.h>
int main(void)
{
char dna[250];
int i;
for(i=0; i<250; i++)
{
scanf("%c", dna[i]);
}
fgets(*dna, 250, scanf("%c", dna));
int k;
for(k=0; k<250; k++)
{
if(dna[k] == 'A' || 'C' || 'G'|| 'T')
{
i++;
//printf("%c\n", dna[0])
}
}
if(i > 0)
{
printf("VALID sequence \n");
}
else
{
printf("INVALID sequence \n");
}
}
like this
#include <stdio.h>
int main(void){
char dna[250+1], rest;
if(scanf("%250[ACGT]%c", dna, &rest) == 2 && rest == '\n')
printf("VALID sequence \n");
else
printf("INVALID sequence \n");
}
There are a large number of ways to approach the problem. You can use scanf with character classes as shown, or you can use any other method to read the input (e.g. getchar, fgets, POSIX getline) and then simply analyze the characters entered for anything other than "ACGT".
Additionally, in your problem statement you state "The user just keeps entering the sequence not more than 250 characters". Obviously you will need to loop to handle entry of multiple strings, but beyond that, you will also need to protect against and handle the remainder of any strings greater than 250 characters. It is unclear whether in that case, you want to keep the first 250 valid characters entered (seems logical you would), and then discard any over the 250 character limit.
Your tools for validating the input are, in the case of using character-oriented input (e.g. using getchar) are simply to check each character input against your ACGT. When using line-oriented input, (e.g. fgets or getline) the C library provides a number of tools to check for characters within a string, or substrings within a string (strchr is applicable here) or you can simply walk a pointer down the entire string of input checking each character. (you also need to check for, and remove the '\n' the line-oriented functions read and include in the buffer)
Putting the pieces together into a short example using fgets for input and strchr to check whether each character is one of ACGT, you could do something like the following. Here the user can enter as many strings as desired. The program terminates when EOF is read (manually generated with Ctrl + D on Linux, Ctrl + Z on windoze). In the event an invalid string is entered, the code identifies the position of the first invalid character in the entry:
#include <stdio.h>
#include <string.h>
#define MAXC 250
int main (void) {
char str[MAXC+1] = "", *valid = "ACGT";
printf ("Enter sequences [ctrl+d] to quit:\n");
while (fgets (str, MAXC+1, stdin)) /* read input */
{
size_t len = strlen (str), good = 1; /* get length, set flag */
char *p = str; /* pointer to str */
int c;
if (str[len-1] == '\n') /* trim '\n' char */
str[--len] = 0; /* overwrite with nul */
else /* line > 250, discard extra */
while ((c = getchar()) != '\n' && c != EOF) {}
for (; *p; p++) /* for each char in str */
if (!strchr (valid, *p)) { /* check against valid */
good = 0; /* not found - invalid */
break;
}
if (good)
printf ("VALID\n");
else
fprintf (stderr, "INVALID ('%c' at character '%ld'\n", *p, p - str);
}
return 0;
}
Example Use/Output
$ ./bin/acgtvalid
Enter sequences [ctrl+d] to quit:
ACGTTTGGCCCATTAGGC
VALID
ACCGGTTCCGGAITT
INVALID ('I' at character '12')
I am writing a program for fun (not for school), and am having a hard time figuring out why the scanf function isn't executing on every iteration of my loop - I've toyed with both 'for' loops and 'while' loops.
I know that depending on how I write the scanf function (i.e. scanf("%s", &variablename); VS scanf("%99[^\n]s", &variablename);) makes a difference, but I have tried everything and I'm desperate!
When I do a printf check on my input from the scanf, on every iteration it is only intaking one string per iteration, so if I enter two words in my first input, then it takes two iterations to process - one word per. Here is the segment of code I'm describing:
int main(void){
int tries = 0;
int score = 0;
char question[100];
char useranswer[100];
const char *phrase = {"our favorite saying\0"};
printf("\nQuestion #3 (10 points): What is our secret saying?\n");
sleep(1);
tries = 1;
while (tries<=3){
printf("YOUR ANSWER:");
scanf("%s[^\n]", useranswer);
if(strncmp(useranswer, phrase, 15) != 0){
printf ("Nope, try again!\n");
printf("You have used %d out of 3 tries!\n", tries);
if (tries == 2){
printf("Here's your final hint:xxx...\n");
}
if (tries == 3){
printf("You didn't get it. The answer is: our favorite saying!\n");
}
tries++;
}
if (strncmp(useranswer, phrase, 15) == 0){
printf("Damn, you're good. Well done.\n");
score += 10;
break;
}
}
The output of this code is:
Question #3 (10 points): What is our secret saying?
YOUR ANSWER:our favorite saying
Nope, try again!
You have used 1 out of 3 tries!
YOUR ANSWER:Nope, try again!
You have used 2 out of 3 tries!
Here's your final hint:xxx...
YOUR ANSWER:Nope, try again!
You have used 3 out of 3 tries!
You didn't get it. The answer is: our favorite saying!
(It only allowed me to input once, and I typed "our favorite saying".)
In comments you could find why your format specifier in scanf doesn't work.
An alternative is to use fgets instead, maybe in an helper function which takes care of some of the corner cases that can arise while reading user input:
#include <ctype.h>
char *read_line( char *buf, size_t n, FILE *pfin )
{
ssize_t length = 0;
int ch;
if ( !buf || n == 0 )
return NULL;
/* Consume trailing control characters, like '\0','\n', '\r', '\f'...
but also '\t'. Note that ' ' is not skipped. */
while ( (ch = fgetc(pfin)) != EOF && iscntrl(ch) ) { }
if ( ch == EOF )
return NULL;
/* At least one char is printable */
*buf = ch;
++length;
/* Read from file till a newline or up to n-2 chars. The remaining chars
are left in the stream buffer. Return NULL if no char is read. */
if ( fgets(buf + 1, n - 1, pfin) )
{
/* Trim the string to the first control character */
while ( !iscntrl(buf[length]) )
{
++length;
}
buf[length] = '\0';
}
return buf;
}
I'd change the following logic too. OP uses strncmp(useranswer, phrase, 15) multiple times, but that magic number 15 is lower then phrase's size so it ends up comparing only a substring.
while ( tries <= 3 ) {
printf("YOUR ANSWER:");
if ( !read_line(useranswer, sizeof useranswer, stdin) ) {
printf("Error: Unexpected end of input.\n");
exit(EXIT_FAILURE);
}
if( strcmp(useranswer, phrase) == 0 ) {
printf("Damn, you're good. Well done.\n");
score += 10;
break;
} else {
printf ("Nope, try again!\n");
printf("You have used %d out of 3 tries!\n", tries);
if (tries == 2) {
printf("Here's your final hint:xxx...\n");
}
if (tries == 3) {
printf("You didn't get it. The answer is: our favorite saying!\n");
}
tries++;
}
}
As a final note, I found OP declaration of phrase a bit weird (maybe a typo):
const char *phrase = {"our favorite saying\0"};
// string literals are already ^^ null terminated...
While we can use a simple array declaration, like:
const char phrase[] = "our favorite saying";
Consider also what values sizeof phrase returns in those two different cases.
Thanks to #chux for all the valuable hints and the interesting links provided:
https://stackoverflow.com/a/27729970/4944425
https://stackoverflow.com/a/28462221/4944425
And to #Dmitri for having pointed out in his comment that once we are sure that both the strings are null terminated, we can use strcmp instead of strncmp.