I'm making a program in which i ask for the username name, and i'd like to only accept strings with valid characters only (alphabetic).
I found that i can either use
do{
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n);
}while(1);
or
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name; i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
But i'm not sure if any of those work, and what would be better to check if there isn't anything except alphabetic letters.
Also though about making all input letters (after this verification) on lower case and make the first letter upper case with
//all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
Would this work?
if(scanf("%s", &name) reads in all non-white-space, not just letters, into name and does not return if input is only "\n".
if(isalpha(name[i]) == 0){ loop is not bad, but scanf("%s", &name) still does not return if input is only "\n" or just white-space.
for(i=1; i<strlen(name); i++) name[i] = tolower(name[i]) works to make all following letters lower case, but if inefficient as code repeatedly calculates the string length.
Separate reading data and parsing data. Use fgets() to read the data and various code to test the data for correctness.
char buf[200];
fgets(buf, sizeof buf, stdin);
int n = 0;
// Skip leading white-space
// Look for A-Z, a-z or space (like a space between first & last)
// Skip white-space like \n
// Save into 'n' the current scan position
sscanf(buf, " %*[A-Za-z ] %n", &n);
if (n > 0 && buf[n] == '\0') Success(); // #user3121023
Should code need to rid buf of a potential trailing "\n", suggest:
buf[strcspn(buf, "\n")] = 0;
Let's look at each option.
First option:
do {
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n");
} while(1);
This won't quite work the way you expect. First off, what exactly is name? I'm almost sure that you want scanf("%s", name) instead (name instead of &name), unless you declared it as char name;, which would be catastrophic anyway.
Anyway, the problem I see with this approach is that you don't really validate the string. Read the man page section about %s:
s - Matches a sequence of non-white-space characters; the next pointer
must be a pointer to character array that is long enough to hold
the input sequence and the terminating null byte ('\0'), which is
added automatically. The input string stops at white space or at the
maximum field width, whichever occurs first.
Nothing says that the string is composed of alphabetic characters only.
Second option:
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name); i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
Again, you probably want name rather than &name. You also shouldn't be calling strlen() in the for loop condition, because it's inefficient (strlen() is O(n)). A smart compiler may optimize it away, but it's hard for the compiler to know when it is safe to do so. Just call strlen() before the loop and store the result in a variable.
isalpha() expects an integer as an argument, which is expected to be either EOF or an unsigned char converted to int. Again, you don't show the declaration for name, but assuming that it is a character array, you should cast name[i] to unsigned char before calling isalpha(), so that you don't get any sign extension surprises:
if (isalpha((unsigned char) name[i]) == 0) { /* ... */ }
In fact, gcc nowadays will most likely give you a warning if you call any of the ctype family macros / functions with a plain char. The macros are deliberately written in such a way that a warning is shown, precisely because this is a common mistake. It is implementation-defined whether a plain char is signed or unsigned. You would get problems in a platform with signed chars because of sign extension (this is because typically, things like isalpha() are implemented using lookup tables, and extending the sign yields a negative number that would index the lookup table with a negative index - Oops!)
Other than this, this approach seems ok to me.
A third, maybe better option:
Since you mentioned fgets(), I think you could do this easily by combining fgets() with sscanf(). First, you read a line with fgets(). Then, you use sscanf() to match a string consisting of only characters in the range [a-zA-Z]. This can be done with the format specifier %[a-zA-Z]s. Then, you just have to check if this matched the entire line. Here's a working program:
#include <stdio.h>
#include <string.h>
int main(void) {
static char buf[512];
static char name[512];
int is_valid = 0;
while (!is_valid) {
fgets(buf, sizeof(buf), stdin);
size_t line_len = strlen(buf);
if (line_len > 0 && buf[line_len-1] == '\n') {
buf[line_len-1] = '\0';
line_len--;
}
int n = 0;
if (sscanf(buf, " %[a-zA-Z] %n", name, &n) == 1 && buf[n] == '\0') {
is_valid = 1;
} else {
printf("Please enter a valid name.\n");
}
}
printf("Name: %s\n", buf);
return 0;
}
Make sure your buffers are large enough; this code is vulnerable to buffer overflow for arbitrarily long names / lines.
Now let's see the code to make the first letter upper case:
//all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
Again, remove strlen() from the loop condition. toupper() and tolower() also expect an int as an argument representing either EOF or an unsigned char converted to int. You should cast it to unsigned char to avoid problems with possible sign extension, as I said earlier with the other example.
This is wrong:
//first letter to upper
name[0] = toupper(name[i]);
It should be:
//first letter to upper
name[0] = toupper(name[0]);
(The argument to toupper() is name[0], not name[i]).
Finally, this is useless:
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
%s will never give you a string with whitespaces (refer to the manpage quote I pasted above).
Assuming that you want your name to have only characters a through z or A through Z then you could use this function
//Returns 1 if non alphabetic character is found, 0 otherwise
int NonAlphaCharsFound(char *name)
{
int FoundNonChar = 0;
int i, nameLength;
nameLength = strlen(name);
for(i = 0; i < nameLength; i++)
{
if((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || name[i] == ' ')
{
//do nothing if it's an alphabect character
//name[i] == ' ' is to allow for spaces if you want spaces in the name
}
else
{
FoundNonChar = 1;
break;
}
}
return FoundNonChar;
}
Related
int main() {
char userInput[100]; //Store user input
//Take user input
//scanf(" %s",&userInput);
//scanf("%[^\n]s",&userInput);
//scanf("%[^\n]", &userInput);
//gets(userInput);
scanf("%[]s", &userInput); //This takes input but doesnt leave input loop
printf(" %s",userInput);
//i = index to start for looping through the string, starting at the beginning
//count = Stores occurrences of '$'
//inputLength = length of the input, used for limit of loop
int i =0,count =0;
int inputLength = strlen(userInput);
//Loop through the user input, if the character is '$', the integer count will be incremented
for (i; i < inputLength; i++){
if (userInput[i] == '$'){
count++;
}
}
printf("%d", count);
return 0;
}
Hi i'm having some issues with my code, i need to take an input of 3 lines and count the number of'$' in the input. The input method not commented "scanf("%[]s", &userInput);" is the one only i have discovered to take all 3 lines of input, BUT i can't break the input loop and continue with my program.
Any help would be greatly appreciateed
To read 3 lines with the cumbersome scanf(), code needs to look for '$', '\n', and EOF. The rest of input is discardable.
int count = 0;
int line = 0;
while (line < 3) {
scanf("%*[^$\n]"); // Scan for any amount of characters that are not $ nor \n,
// "*" implies - do not save.
char ch;
if (scanf("%c", &ch) != 1) { // Read next character.
break;
}
if (ch == '$') count++;
else line++;
}
printf("$ count %d\n", count);
As #chux suggested, reading with fgets provides a convenient way to protect from buffer overrun and without having to hard code field-width modifiers in scanf conversion specifiers.
Here, if all you need to do is count the number of '$' characters found in your input (regardless of how many lines), you can simply read ALL the input in fixed sized chunks of data. fgets does just that. It doesn't matter if you have one line, or one million lines of input. It also doesn't matter if your input lines are one-character or one million characters long. You can simply read each line and count the number of '$' found within each chunks of data read, keeping a count of the total found.
You can do this for any character. If you wanted to also count the number of line, you can simply check for '\n' characters and keep a total there as well. The only corner-case in counting lines with fgets is to insure you protect against a non-POSIX end-of-file (meaning a file with no '\n' as the final character). There are a couple of ways to handle this. Checking that the last character read was a '\n' is as good as any.
Putting the pieces together, and protecting against a non-POSIX eof, you could do something similar to the following, which simply reads all data available on stdin and outputs a final '$' and line count:
#include <stdio.h>
#define MAXC 100
int main (void) {
char buf[MAXC] = ""; /* buffer to hold input in up to MAXC size chunks */
size_t lines = 0, dollars = 0; /* counters for lines and dollar chars */
int i = 0;
while (fgets (buf, MAXC, stdin)) /* read all data */
for (i = 0; buf[i]; i++) /* check each char in buf */
if (buf[i] == '$') /* if '$' found */
dollars++; /* increment dollars count */
else if (buf[i] == '\n') /* if '\n' found */
lines++; /* increment line count */
if (i && buf[i-1] != '\n') /* protect against non-POSIX eof */
lines++;
/* output results */
printf ("input contained %zu lines and %zu '$' characters.\n",
lines, dollars);
return 0;
}
Look things over and let me know if you have further questions.
scanf("%[]s", &userInput);" is the one only i have discovered to take all 3 lines of input, BUT i can't break the input loop and continue with my program.
"%[]" is an invalid scanf() specifier. Anything may happen, it is undefined behavior, including taking all lines in and not returning.
The 's' in the format serves no purpose here - drop it.
Yes fgets() is best but let us abuse scanf() to read 3 lines and look for '$'.
char line[3][100] = {0};
// v--------- Consume all leading whitespace
// | vv ----- limit input to 99 characters as scan() appends a \0
// | || v-v-- Look for "not \n"
#define FMT_1LINE " %99[^\n]"
// Let the compiler concatenate the 3 formats into 1 string for scanf
int scan_count = scanf(FMT_1LINE FMT_1LINE FMT_1LINE, line[0], line[1], line[2]);
// Check return value
if (scan_count == 3) {
// Successfully read 3 lines
int count = 0;
for (int line_index = 0; line_index < 3; line_index++) {
char *s = line[line_index];
while (*s) { // no need for strlen(), just loop until the null character
count += *s == '$';
s++;
}
}
printf("$ count %d\n", count);
}
You write:
scanf("%[]s", &userInput); //This takes input but doesnt leave input loop
but the comment is at best misleading. Your format string is malformed, so the behavior of the scanf call is undefined. An empty scan set (between the [] in the format) does not make sense, because the resulting field could never match anything. Therefore, a ] appearing immediately after the opening ] of the scan set is interpreted as a literal character not the ending delimiter. Your scan set is therefore unterminated.
Note, too, that %[ is its own field type, separate from %s. An 's' following the closing ] of the scan set is not part of such a field descriptor, but rather an ordinary character to match.
A trivial way to do this with scanf would be to read characters one at a time in a loop via a %c field. This is probably not what the exercise is looking for, and it's a hack to use scanf() instead of getchar() for this purpose, but perhaps it would serve:
int nl_count = 0;
int dollar_count = 0;
do {
char c;
int result = scanf("%c", &c);
if (result != 1) {
break;
}
switch (c) {
case '\n':
nl_count++;
break;
case '$':
dollar_count++;
break;
}
} while (nl_count < 3);
I'm afraid it would be much more complicated to do it safely reading multiple characters at a time with a %[ field, and there is no safe way to read all three lines in one scanf call, unless you can rely on the input lines not to exceed a line length limit known to you.
int readMatrix() {
char userInput[100][3]; //Store user input
int j = 0, m = 0;
for(m = 0; m < 3; m++){
scanf("%s", &userInput[j][m]); //This takes input (Ex: 22 *(enter)* 33$ *(enter)* 66$ *(enter)*
j++; //increase the column
}
int i =0,count =0;
m = 0;
//Loop through the user input, if the character is '$', the integer count will be incremented
for (i = 0; i < 100; i++){
for(m = 0; m < 3; m++){
if (userInput[i][m] == '$'){
count++;
}
}
}
printf("%d", count);
return 0;
}
This is my code here, I'm trying to create a programme that counts characters using functions and then determine the average value of characters when an empty line is encountered. The programme is suppose to allow the user to enter multiple lines until an empty line is encountered but I can't seem to.
#include <stdio.h>
#include <Windows.h>
int main()
{
char str[1000];
int Digits, Char, SpecialChar, linecount = 0;
int counter;
int total;
int average;
Digits = Char = SpecialChar = 0;
printf("Please type in your words here: ");
gets(str);
for (counter = 0; str[counter] != NULL; counter++)
{
if (str[counter] >= '0' && str[counter] <= '9')
Digits++;
else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
Char++;
else
SpecialChar++;
}
while (str[counter] != '\n')
{
if (str[counter] = '\n')
{
linecount ++;
}
}
total = Digits + Char + SpecialChar;
average = total / linecount;
printf("\nDigits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d", Digits, Char, SpecialChar, linecount);
printf("\nTotal no. of characters = %d", total);
printf("\nAverage no. of characters = %d", average);
Sleep(5000000);
return 0;
}
As far as I know, the function gets is interrupted after "\n". Also, using fgets you will have to put an attention on the '\0' addition on the string. That means that
The programme is suppose to allow the user to enter multiple lines until an empty line is encountered but I can't seem to.
will never be able to be accomplished using gets this way. Because gets is not a recommended function, I edited you code a little bit in a way that you might be searching.
Something to metion, that I found out it might be a logic error, before you read this code
for (counter = 0; str[counter] != NULL; counter++)
This seems strange, because the fgets will always record the "\n" character. So, the next condition
if (str[counter] = '\n')
will never be true
I see some others errors on you code, but, not majors ones. So, I see the suggestion as a sufficient as appointing them
while (fgets(str, 1000, stdin) && str[0] != '\n'){ //I dont know if checking the first element of the string is redundancy,
//because, the I think the fgets function will return NULL if you just press enter, as the first character
for (counter = 0; str[counter] != '\n'; counter++{
if (str[counter] >= '0' && str[counter] <= '9')
Digits++;
else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
Char++;
else
SpecialChar++;
}
linecount ++; //new line is someting that you will always reach, so,
//there is no reason for any condition
}
Solution is below:
#include <stdio.h>
#include <Windows.h>
int main()
{
char str[1000];
int Digits, Char, SpecialChar, linecount = 0;
int counter;
int total;
int average;
int flag = 1;
Digits = Char = SpecialChar = 0;
printf("Please type in your words here: ");
while(flag == 1)
{
gets(str);
if (str[0] == NULL || str[0] == '\n')
{
flag = 0;
break;
}
for (counter = 0; str[counter] != NULL; counter++)
{
if (str[counter] >= '0' && str[counter] <= '9')
Digits++;
else if ((str[counter] >= 'A' && str[counter] <= 'Z') || (str[counter] >= 'a' && str[counter] <= 'z'))
Char++;
else
SpecialChar++;
}
linecount ++;
}
total = Digits + Char + SpecialChar;
average = total / linecount;
printf("\nDigits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d", Digits, Char, SpecialChar, linecount);
printf("\nTotal no. of characters = %d", total);
printf("\nAverage no. of characters = %d", average);
Sleep(5000000);
return 0;
}
The gets() function is unsafe and extremely susceptible to buffer overflows. Never use this function. A better alternative is fgets(), though the non-standard (but widely available) POSIX getline() function is also a good option.
Both fgets() and gets() fetch a line of input, but gets() discards the newline character, while fgets() keeps it and stores it in the input buffer (if there is room). This means that you may need to remove the newline character after fetching a line of input with fgets(). It also means that when the user simply presses ENTER, the input buffer contains only a \n character, followed by a \0 null terminator; fgets() always null-terminates the input buffer.
To read multiple lines of user input, stopping only when the user presses ENTER, fgets() should be called in a loop. One way of doing this is to use a for(;;) {} loop construction, which never terminates (equivalent to while(1) {}). If the first character in a line of input is a \n character, then a break; statement exits the loop.
Notes on the Posted Code
The posted code is comparing input characters with character constants to determine whether the input is numeric, alphabetic, or otherwise. This is better, and more portably, accomplished by using Standard Library functions from ctype.h. Using library functions here means that the code does not need to explicitly consider the current locale or character encoding (which may not be ASCII or UTF-8).
The posted code contains the line:
for (counter = 0; str[counter] != NULL; counter++) {}
Note that NULL is the null pointer constant, equivalent to (void *) 0, but not equivalent to 0. The goal of this loop appears to be to iterate over a string, terminating when the null terminator (\0) is reached. So, the controlling expression should be changed:
for (counter = 0; str[counter] != '\0'; counter++) {}
Also, the purpose of this loop in the posted code is unclear:
while (str[counter] != '\n')
{
if (str[counter] = '\n')
{
linecount ++;
}
}
If str[counter] is a newline character, then the loop is not entered; otherwise the if statement in the loop body assigns '\n' to str[counter], evaluating to true and incrementing linecount. On the next iteration str[counter] == '\n', so the loop is terminated. After the previous loop (with NULL changed to '\0' in the controlling expression), counter is the index of the \0 character in str, so this loop replaces the null terminator with a newline character, making str a string no longer. This will lead to undefined behavior if the code later attempts to treat str as a string.
If the line if (str[counter] = '\n') is a typo, meant to be if (str[counter] == '\n'), then this is an infinite loop once entered.
An Example Program
Here is a heavily modified of the posted code that uses fgets() to get user input, and Standard Library functions to classify input characters.
The fgets() function returns a null pointer in the event of an error, so this is checked for and handled in about the simplest way possible. After input has been stored in the str[] buffer array, the first character is checked; if it is \n, then the user entered an empty line (probably: see the next paragraph), and the loop is terminated. Otherwise, the next step is to see if the input line contains a newline character at all. The strchr() (from string.h) function is used here for this. If the \n is not found, then a null pointer is returned, otherwise a pointer to the \n character is returned. This is used to write over the \n with \0, effectively removing the newline character. Then linecount is incremented. Thus, the line counter is incremented only when a newline character is encountered in the input.
Note that when input is too large for the input buffer, at least the newline character will remain in the input stream waiting for the next I/O function call. It is possible that only the newline character remains in the input stream, so on the next loop iteration the first character in the buffer is \n, interpreted by this program as an empty line. If there is a possibility that input will be larger than the buffer allocation, more subtlety will be required to handle this situation. One solution is to use a flag to indicate whether the start of a line is being read. Here, line_start is initialized to 1, set to 1 whenever linecount is incremented, and set to 0 whenever a newline character is not found in the input buffer. In order for a newline to indicate an empty line of input, line_start must be set to 1, and the first character in the input buffer must be a \n character. With this modification, the program will reliably read lines of input even longer than the allocated 1000 characters. You can test this out by making the allocation for str[] smaller; try char str[2];.
Here is the complete program:
#include <stdio.h>
#include <ctype.h> // for isdigit(), isalpha()
#include <string.h> // for strchr()
int main(void)
{
char str[1000];
int Digits = 0;
int Char = 0;
int SpecialChar = 0;
int linecount = 0;
int counter;
int total;
int average;
puts("Please type in your words here:");
int line_start = 1;
for (;;) {
if (fgets(str, sizeof str, stdin) == NULL) {
/* Handle error */
fprintf(stdin, "I/O error\n");
return 1;
}
/* Terminate loop on empty line */
if (line_start && str[0] == '\n') {
break;
}
/* If newline present, remove and increment linecount */
char *cptr = strchr(str, '\n');
if (cptr != NULL) {
*cptr = '\0';
++linecount;
line_start = 1;
} else {
line_start = 0; // complete line not read
}
/* update character counters */
for (counter = 0; str[counter] != '\0'; counter++) {
unsigned char uc = str[counter];
if (isdigit(uc)) {
Digits++;
} else if (isalpha(uc)) {
Char++;
} else {
SpecialChar++;
}
}
}
total = Digits + Char + SpecialChar;
average = total / linecount; // integer division
printf("Digits: %d \nCharacters: %d \nSpecial Characters: %d \nLine: %d\n",
Digits, Char, SpecialChar, linecount);
printf("Total no. of characters = %d\n", total);
printf("Average no. of characters = %d\n", average);
return 0;
}
I was working on a program in C to count the number of spaces in a sentence. But I haven't managed to get it to work properly. If I enter something like Hello world 1234 how are you the output I'm getting is 3 when the output expected is 5.
My code is :
//Program to count number of words in a given Sentence
#include <stdio.h>
#include <string.h>
int main()
{
char sent[100];
char sentence[] = {' ', '\0'};
printf("\nEnter a sentence :\n");
gets(sent);
strcat(sentence, sent);
int l = strlen(sentence), i = 0, count = 0, countCh = 0;
printf("%d", l);
char ch, ch1;
for (i = 0; i < (l-1); i++)
{
ch = sentence[i];
if (ch == ' ')
{
ch1 = sentence[i+1];
if (((ch1 >= 'A') && (ch1 <= 'Z'))||((ch1 >= 'a') && (ch1 <= 'z')))
count++;
}
}
printf("\nNo of words is : %d", count);
return 0;
}
I used the same logic in Java and it worked fine. Could someone explain whats going wrong?
The problem in your code is with the definition of sentence. When you leave out the array dimension and initialize it, the size of the array will be determined by the length of the initializer.
Quoting the man page of strcat()
The strcat() function appends the src string to the dest string, overwriting the terminating null byte ('\0') at the end of dest, and then adds a terminating null byte. The strings may not overlap, and the dest string must have enough space for the result. If dest is not large enough, program behavior is unpredictable;
That is, the program will invoke undefined behavior.
This way, sentence has certainly way less memory than it it supposed to hold. Moreover, strcat() is not at all required there.
The correct way to do it will be
Define sentence with a proper dimention, like char sentence[MAXSIZE] = {0};, where MAXSIZE will be a MACRO having the size of your choice.
use fgets() to read the user input.
use isspace() (from ctype.h) in a loop to check for presence of space in the input string.
The following
if (((ch1 >= 'A') && (ch1 <= 'Z'))||((ch1 >= 'a') && (ch1 <= 'z')))
count++;
probably should be
if (ch1 != ' ')
count++;
As now " 12345" would not be counted as word.
Also count counts the spaces, so the word count is one more: hence 3 instead of 5.
Your sentence seems to have had the intention of counting the terminatin NUL.
If you want to count real words containing letters, use a bool state whether current and prior state of being in a letter differ.
As mentioned overflow is possible with your code.
How can I scanf() the integer values I enter into an array until I hit enter.
I believe I can use getchar() != '\n'.
but how do I loop through the line ?
Suppose my input is 20 21 2 12 2. I want an array that has all those inputs.
What given functions could I use in order to scan them all in.
You are trying to read integers as characters so once read you need to convert it to integers.
Read the line to a buffer using fgets() then parse the input buffer to get integers.
Store the integers to the array.
The code looks like
char buf[300];
int a[5],i=0;
fgets(buf,sizeof(buf),stdin);
char *p = strtok(buf," ");
while(p != NULL)
{
char *endptr;
a[i] = strtol(p,&endptr,10);
if ((*endptr != '\0') && (isspace(*endptr) == 0))
printf("warning: invalid value detected\n");
else
i++;
p = strtok(NULL," ");
}
You can use the alternative strtol() instead of atoi() to convert string to integer.
PS: Your buf should be large enough to hold the whole line. fgets() read till newline character.
If you use getchar() you obtain digits one by one, so you need
to store them first in the buffer, and when white space comes,
you convert those digits into a number, and store it into array.
here is the explanation of the code I made for you.
1st if statement : if obtained character is a digit, store it in buf
2nd if statement : if obtained character is a white space or EOL and at least 1 digit is stored in buf, convert digits into number and store it in array a.
3rd if statement : if obtained character is not a digit or a white space or a EOL, warns users.
4th if statement : if obtained character is a EOL, end loop.
The code below works fine.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(){
#define BUFSIZE 50
#define ARRAYSIZE 5
int i,k,a[ARRAYSIZE];
char c,buf[BUFSIZE];
for(i=0,k=0;(i<BUFSIZE)&&(k<ARRAYSIZE);){
c=getchar();
if(isdigit(c)){
buf[i++] = c;
}else if((i>0) && (c==' ' || c=='\n')){
buf[i] = '\0';
a[k++] = atoi(buf);
i=0;
}else if(!(c==' ' || c=='\n')){
printf("warning : invalid value %c is detected\n",c);
i=0;
}
if(c=='\n'){
break;
}
}
printf("input :");
for(i=0;i<ARRAYSIZE;i++){
printf("%d, ",a[i]);
}
printf("\n");
}
I want to have a user enter numbers separated by a space and then store each value as an element of an array. Currently I have:
while ((c = getchar()) != '\n')
{
if (c != ' ')
arr[i++] = c - '0';
}
but, of course, this stores one digit per element.
If the user was to type:
10 567 92 3
I was wanting the value 10 to be stored in arr[0], and then 567 in arr[1] etc.
Should I be using scanf instead somehow?
There are several approaches, depending on how robust you want the code to be.
The most straightforward is to use scanf with the %d conversion specifier:
while (scanf("%d", &a[i++]) == 1)
/* empty loop */ ;
The %d conversion specifier tells scanf to skip over any leading whitespace and read up to the next non-digit character. The return value is the number of successful conversions and assignments. Since we're reading a single integer value, the return value should be 1 on success.
As written, this has a number of pitfalls. First, suppose your user enters more numbers than your array is sized to hold; if you're lucky you'll get an access violation immediately. If you're not, you'll wind up clobbering something important that will cause problems later (buffer overflows are a common malware exploit).
So you at least want to add code to make sure you don't go past the end of your array:
while (i < ARRAY_SIZE && scanf("%d", &a[i++]) == 1)
/* empty loop */;
Good so far. But now suppose your user fatfingers a non-numeric character in their input, like 12 3r5 67. As written, the loop will assign 12 to a[0], 3 to a[1], then it will see the r in the input stream, return 0 and exit without saving anything to a[2]. Here's where a subtle bug creeps in -- even though nothing gets assigned to a[2], the expression i++ still gets evaluated, so you'll think you assigned something to a[2] even though it contains a garbage value. So you might want to hold off on incrementing i until you know you had a successful read:
while (i < ARRAY_SIZE && scanf("%d", &a[i]) == 1)
i++;
Ideally, you'd like to reject 3r5 altogether. We can read the character immediately following the number and make sure it's whitespace; if it's not, we reject the input:
#include <ctype.h>
...
int tmp;
char follow;
int count;
...
while (i < ARRAY_SIZE && (count = scanf("%d%c", &tmp, &follow)) > 0)
{
if (count == 2 && isspace(follow) || count == 1)
{
a[i++] = tmp;
}
else
{
printf ("Bad character detected: %c\n", follow);
break;
}
}
If we get two successful conversions, we make sure follow is a whitespace character - if it isn't, we print an error and exit the loop. If we get 1 successful conversion, that means there were no characters following the input number (meaning we hit EOF after the numeric input).
Alternately, we can read each input value as text and use strtol to do the conversion, which also allows you to catch the same kind of problem (my preferred method):
#include <ctype.h>
#include <stdlib.h>
...
char buf[INT_DIGITS + 3]; // account for sign character, newline, and 0 terminator
...
while(i < ARRAY_SIZE && fgets(buf, sizeof buf, stdin) != NULL)
{
char *follow; // note that follow is a pointer to char in this case
int val = (int) strtol(buf, &follow, 10);
if (isspace(*follow) || *follow == 0)
{
a[i++] = val;
}
else
{
printf("%s is not a valid integer string; exiting...\n", buf);
break;
}
}
BUT WAIT THERE'S MORE!
Suppose your user is one of those twisted QA types who likes to throw obnoxious input at your code "just to see what happens" and enters a number like 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 which is obviously too large to fit into any of the standard integer types. Believe it or not, scanf("%d", &val) will not yak on this, and will wind up storing something to val, but again it's an input you'd probably like to reject outright.
If you only allow one value per line, this becomes relatively easy to guard against; fgets will store a newline character in the target buffer if there's room, so if we don't see a newline character in the input buffer then the user typed something that's longer than we're prepared to handle:
#include <string.h>
...
while (i < ARRAY_SIZE && fgets(buf, sizeof buf, stdin) != NULL)
{
char *newline = strchr(buf, '\n');
if (!newline)
{
printf("Input value too long\n");
/**
* Read until we see a newline or EOF to clear out the input stream
*/
while (!newline && fgets(buf, sizeof buf, stdin) != NULL)
newline = strchr(buf, '\n');
break;
}
...
}
If you want to allow multiple values per line such as '10 20 30', then this gets a bit harder. We could go back to reading individual characters from the input, and doing a sanity check on each (warning, untested):
...
while (i < ARRAY_SIZE)
{
size_t j = 0;
int c;
while (j < sizeof buf - 1 && (c = getchar()) != EOF) && isdigit(c))
buf[j++] = c;
buf[j] = 0;
if (isdigit(c))
{
printf("Input too long to handle\n");
while ((c = getchar()) != EOF && c != '\n') // clear out input stream
/* empty loop */ ;
break;
}
else if (!isspace(c))
{
if (isgraph(c)
printf("Non-digit character %c seen in numeric input\n", c);
else
printf("Non-digit character %o seen in numeric input\n", c);
while ((c = getchar()) != EOF && c != '\n') // clear out input stream
/* empty loop */ ;
break;
}
else
a[i++] = (int) strtol(buffer, NULL, 10); // no need for follow pointer,
// since we've already checked
// for non-digit characters.
}
Welcome to the wonderfully whacked-up world of interactive input in C.
Small change to your code: only increment i when you read the space:
while ((c = getchar()) != '\n')
{
if (c != ' ')
arr[i] = arr[i] * 10 + c - '0';
else
i++;
}
Of course, it's better to use scanf:
while (scanf("%d", &a[i++]) == 1);
providing that you have enough space in the array. Also, be careful that the while above ends with ;, everything is done inside the loop condition.
As a matter of fact, every return value should be checked.
scanf returns the number of items successfully scanned.
Give this code a try:
#include <stdio.h>
int main()
{
int arr[500];
int i = 0;
int sc = 0; //scanned items
int n = 3; // no of integers to be scanned from the single line in stdin
while( sc<n )
{
sc += scanf("%d",&arr[i++]);
}
}