I am currently on a beginner course in C and was given an exercise requiring my program to check if the user input contains non-alphabets. I've figured to use the function isalpha()
to check the user input and if it contains non-alphabets, the program should ask the user to enter another input.
Below is my current code:
#include <stdio.h>
#include <ctype.h>
#define MAX 13
int main() {
char player1[MAX];
int k = 0;
// Ask player 1 to type a word.
printf("Player 1, enter a word of no more than 12 letters: \n");
fgets(player1, MAX, stdin);
// // Loop over the word entered by player1
for (int i = 0; i < player1[i]; i ) {
// if any chars looped through is not an alphabet, print message.
if (isalpha((unsigned char)player1[i]) == 0) {
printf("Sorry, the word must contain only English letters.");
}
}
However, after testing it, I've derived a few cases from its results.
Case 1:
Entering without any input prints ("Sorry, the word must contain only English letters. ")
Case 2: An input with 1 non-alphabetic character prints the 'sorry' message twice. Additionally, an input with 2 non-alphabetic characters print the 'sorry' message thrice. This implies that case 1 is true, since no input prints the message once, then adding a non-alphabetic prints the message twice.
Case 3: An input of less than 10 characters(all alphabetic) prints out the sorry message also.
Case 4: An input of more than 9 characters(all alphabetic) does not print out the sorry message, which satisfies my requirements.
Why are these the cases? I only require the message to print once if after looping through the user input, there's found to be a non-alphabetic character!
CodePudding user response:
Strings in C are null-terminated, which means they contains an extra byte '\0' to mark the end of the string (character 0 in the ascii table), so you can only store 12 characters in a char array of size 13.
If you array contains a string smaller than 12 characters, since you loop over the whole array, you'll meet that null-terminating-byte, which fails isalpha(): it checks if character is in range ['A', 'Z'] or ['a', 'z']. Characters are just integers for your computers, so isalpha() checks if received value is is range [65, 90] or [97, 122], and 0 is not. To be more precise, the notion of integer makes no sense for your computer, that's just how we interpret information, it's just a bunch of bits for your computer.
See ascii table: https://www.rapidtables.com/code/text/ascii-table.html
By having a fixed size buffer, you'll have garbage after the contained string if the string doesn't take all the space.
You have 2 conditions to stop iterating:
- end of array, to prevent overflowing the array
- end of string, to prevent mis-interpreting bytes in array which are further than string end
Error message might be printed several times, since you keep checking even after an error occured, you have to break the loop.
Below code doesn't meet mentioned problems
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define BUFFER_SIZE 13
#define MIN(a, b) (a < b ? a : b)
int main(void)
{
char player1[BUFFER_SIZE];
int maxIndex;
int i;
/* Ask player 1 to type a word */
printf("Player 1, enter a word of no more than 12 letters: \n");
fgets(player1, BUFFER_SIZE, stdin);
/*
* Max index for iteration, if string is lesser than 12 characters
* (excluding null-terminating byte '\0') stop on string end, otherwise
* loop over whole array
*/
maxIndex = MIN(strlen(player1) - 1, BUFFER_SIZE);
for (i = 0; i < maxIndex; i ) {
/* Print error if non-letters were entered */
if (isalpha(player1[i]) == 0) {
printf("Sorry, the word must contain only English letters.");
/* Error occured, no need to check further */
break;
}
}
/*
for (i = 0; i < maxIndex; i )
printf("%d ", (int) player1[i]);
printf("\n%s\n", player1);*/
return 0;
}
The MIN() is a macro, a ternary expression which returns the smallest argument, nothing really complicated here.
But note that, when you enter the word, you press <Enter>, so your string contains a "go to next line" character (character '\n', n°10 in ascii table, as @Shawn mentioned in comments), so you have to stop before it: that's why I use strlen(player) - 1
, string ends with "\n\0", and strlen() returns the number of bytes before '\0' (including '\n').
I've let a dump of the string at the end, you can modify the end-index there to see what's sent to isalpha(), replace maxIndex with BUFFER_SIZE.
CodePudding user response:
This:
for (int i = 0; i < player1[i]; i ) {
loops from 0 up until (but not including) the code point value of the i:th character, updating i
every time it loops. It will very likely access outside the array bounds, which is undefined behavior.
It should look for the terminator (or linefeed but let's keep it simple):
for (size_t i = 0; player1[i] != '\0'; i) {
CodePudding user response:
As @unwind has noted, the conditional of the OP for()
loop is incorrect.
Good to trust to isalpha()
but your code doesn't have to fondle each and every character. Another standard library function, strspn()
, when supplied with your needs, can perform the looping work for you.
#include <stdio.h>
#include <string.h>
#define MAX 13
int main() {
char player1[MAX];
char *permissible =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Ask player 1 to type a word.
printf("Player 1, enter a word of no more than 12 letters: \n");
fgets(player1, MAX, stdin);
if( player1[ strspn( player1, permissible ) ] != '\n' )
printf("Sorry, the word must contain only English letters.");
return 0;
}
CodePudding user response:
to use the function isalpha() to check the user input and if it contains non-alphabets
Simply read one character at a time. No maximum needed.
#include <ctype.h>
#include <stdio.h>
int main(void) {
int ch;
int all_alpha = 1;
printf("Player 1, enter a line\n");
while ((ch = getchar()) != '\n' && ch != EOF) {
if (!isalpha(ch) {
all_alpha = 0;
}
}
if (!all_alpha) {
printf("Sorry, the line must contain only letters.");
}
}