Check whether the input is digit or not in C programming - c

I am currently reading this book: The C Programming Language - By Kernighan and Ritchie (second Edition) and one of the examples I am having trouble understanding how to check whether the input is digit or not. The example is on Page 22, explaining under the array chapter.
Below is the example.
#include <stdio.h>
/* count digits, white space, others */
main()
{
int c, i, nwhite, nother;
int ndigit[10];
nwhite = nother = 0;
for (i = 0; i < 10; ++i)
{
ndigit[i] = 0;
}
while ((c = getchar()) != EOF)
{
if (c >= '0' && c <= '9')
{
++ndigit[c-'0'];
}
else if (c == ' ' || c == '\n' || c == '\t')
{
++nwhite;
}
else
{
++nother;
}
printf("digits =");
for (i = 0; i < 10; ++i)
{
printf(" %d", ndigit[i]);
}
printf(", white space = %d, other = %d\n",nwhite, nother);
}
For this example, what confused me is that the author mentioned that the line ++ndigit[c-'0'] checks whether the input character in c is a digit or not. However, I believe that only the if statement ( if (c>= '0' && c<= '9') ) is necessary, and it will check if c is digit or not. Plus, I do not understand why [c-'0'] will check the input(c) is digit or not while the input variable (c) is subtracted from the string-casting ('0').
Any suggestions/explanations would be really appreciated.
Thanks in advance :)

The if statement checks whether the character is a digit, and the ++ndigit[c-'0'] statement updates the count for that digit. When c is a character between '0' and '9', then c-'0' is a number between 0 and 9. To put it another way, the ASCII value for '0' is 48 decimal, '1' is 49, '2' is 50, etc. So c-'0' is the same as c-48, and converts 48,49,50,... to 0,1,2...
One way to improve your understanding is to add a printf to the code, e.g. replace
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
with
if (c >= '0' && c <= '9')
{
++ndigit[c-'0'];
printf( "is digit '%c' ASCII=%d array_index=%d\n", c, c, c-'0' );
}

I would try to explain with an example
suppose the input is abc12323
So the frequency of 1=1
frequency of 2=2
frequency of 3=2
if (c >= '0' && c <= '9') //checks whether c is a digit
++ndigit[c-'0'];
Now if you do printf("%d",c) then you will get the ascii value of the
character
for c='0' the ascii value will be 48,c='1' ascii value will be 49 and it goes
till 57 for c='9'.
In your program you are keeping a frequency of the digits in the input so you need to update the index of the digit in the array every time you get it
if you do ndigit[c]++ then it will update ndigit[48] for c='0',ndigit[49] for c='1'
So either you can do ndigit[c-'0']++ as ascii value of '0'=48 in decimal
or you can simply do ndigit[c-48]++ so for c='0' ndigit[0] is updated,c=1'
ndigit[1] is updated
you can check the re factored code here http://ideone.com/nWZxL1
Hope it helps you,Happy Coding

Related

Checking if characters in a char array are integers | c script

Hi I have the following code. It is suppose to loop through each character in the string and break out of the loop if one of the characters in the string is not a digit (0-9) (does not have ascii value 0-9).
//check if opperands are positive ints not zero
size_t i = 0;
//iterate through characters in string until null
while (argv[1][i] != '\0') {
int c = argv[1][i];
if(c >= 0 && c <= 9){
printf("True\n");
i++;
}
else {
printf("false\n");
return 1;
}
}
however, say the loop is iterating through the string 1234 it will return false, even though all of the digits ascii values are between 1 and 9. Anyone have any ideas on why it is doing this, I think it might be something with my if/else statement. Thanks!
The number literal 0 and 9 are not the same as the character literal '0' and '9'. You should replace if (c >= 0 && c <= 9) with if (c >= '0' && c <= '9'). Note the single quotes around the digits.

How to count vowels, consonants, digits in a txt file using redirection in C

Brand new C coder here. In my first C course in school. I have experience in java but this course is all in C. I have homework to create a program that reads the contents of a file and counts the number of upper and lower case letters, vowels, consonants and digits. The program is not supposed to have any arguments, but will take a .txt file from the command line via redirection. My question is, how do I correct my current code to read from stdin each character of the file, whether it be a letter or a number? I'm really struggling with how read the contents of the file from stdin, read each character and then decide which category it belongs in. Any help would be appreciated. Thanks.
I'll be running the program like this...
$ program < testFile.txt
Where testFile.txt will contain the following text:
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
int upper = 0; // Number of upper case letters
int lower = 0; // Number of lower case letters
int vowel = 0; // Number of vowels
int consonant = 0; // Number of constants
int digits = 0; // Number of digits
int total = 0; // Total number of characters in file
int i =0;
char value[100];
fgets(value, 100, stdin);
while(value[i] != '\0');
{
if (value[i] >= 'A' && value[i] <= 'Z')
{
upper++;
if (value[i] == 'A' || value[i] == 'E' || value[i] == 'I' || value[i] == 'O' || value[i] == 'U' || value[i] == 'Y')
{
vowel++;
}
else {
consonant++;
}
}
else if (value[i] >= 'a' && value[i] <= 'z')
{
lower++;
if (value[i] == 'a' || value[i] == 'e' || value[i] == 'i' || value[i] == 'o' || value[i] == 'u' || value[i] == 'y')
{
vowel++;
}
else {
consonant++;
}
}
else if (value[i] >= '0' && value[i] <= '9')
{
digits++;
}
total++;
i++;
}
printf("upper-case: %d", upper);
printf("\nlower-case: %d", lower);
printf("\nvowels: %d", vowel);
printf("\nconsonants: %d", consonant);
printf("\ndigits: %d", digits);
printf("\ntotal: %d", total);
printf("\n");
return 0;
I expect output to show how many upper case letters, lower case letters etc.
But once I run $ program < testFile.txt, it just sits there, no output to command line or anything.
Remove the semicolon after the while statement. :-)
while(value[i] != '\0');
This is your most obvious problem, it basically means:
while value[i] != '\0':
do nothing
end while
In other words, if it enters the loop, it will never exit it, because nothing changes that would affect the condition under which the loop continues.
There are other problems as well such as the fact that you will only process the first line rather than the whole file. The whole idea of using fgets and processing a line is unnecessary when you can just start with the following filter skeleton:
int ch;
while ((ch = getchar()) != EOF) {
/* process ch */
}
This will process an entire file character by character until all characters are done (or until an error occurs) so you can just tailor the body loop to do what you need - you've basically done that bit in your code with the loop over the line characters.
I would suggest not using the following code (since this is classwork) but you can also make better use of flow control constructs and library functions (from ctype.h and string.h), something like:
while ((ch = getchar()) != EOF) {
// Lib functions to detect upper/lower-case letters.
if (isupper(ch)) {
++upper;
} else if (islower(ch))
++lower;
}
// And to detect letter/digit type.
if (strchr("aeiouAEIOU", ch) != NULL) {
++vowel;
} else if (isalpha(ch)) {
++consonant;
} else if (isdigit(ch)) {
++digits;
}
++total;
}
This is particularly important since there's no actual guarantee that non-digit characters will be consecutive.

How does getchar_unlocked() work?

My question is based on a CodeChef problem called Lucky Four.
This is my code:
int count_four() {
int count = 0;
char c = getchar_unlocked();
while (c < '0' || c > '9')
c = getchar_unlocked();
while (c >= '0' && c <= '9') {
if (c == '4')
++count;
c = getchar_unlocked();
}
return count;
}
int main() {
int i, tc;
scanf("%d", &tc);
for (i = 0; i < tc; ++i) {
printf("%d\n", count_four());
}
return 0;
}
Let's say I make a slight change to count_four():
int count_four() {
int count = 0;
char c = getchar_unlocked();
while (c >= '0' && c <= '9') {
if (c == '4')
++count;
c = getchar_unlocked();
}
while (c < '0' || c > '9') // I moved this `while` loop
c = getchar_unlocked();
return count;
}
This is my output after moving the while loop below the other one:
0
3
0
1
0
instead of:
4
0
1
1
0
The input used to test the program:
5
447474
228
6664
40
81
Why is this happening? How do getchar() and getchar_unlocked() work?
getchar_unlocked is just a lower level function to read a byte from the stream without locking it. In a single thread program, it behaves exactly like getchar().
Your change in the count_four function changes its behavior completely.
The original function reads the standard input. It skips non digits, causing an infinite loop at end of file. It then counts digits until it gets a '4'. The count is returned.
Your version reads the input, it skips digits, counting occurrences of '4', it then skips non digits, with the same bug on EOF, and finally returns the count.

Exceptions in c programming language

I'm learning c language and I hit a wall, if you would like to help me I appreciate (here is the ex: "Write a program that reads characters from the standard input to end-of-file. For each character, have the program report whether it is a letter. If it is a letter, also report its numerical location in the alphabet and -1 otherwise." btw is not homework).The problem is with the \n i don't know how to make it an exception. I'm new around here please let me know if I omitted something. Thank you for your help.
int main(void)
{
char ch;
int order;
printf("Enter letters and it will tell you the location in the alphabet.\n");
while ((ch = getchar()) != EOF)
{
printf("%c", ch);
if (ch >= 'A' && ch <= 'Z')
{
order = ch - 'A' + 1;
printf(" %d \n", order);
}
if (ch >= 'a' && ch <= 'z')
{
order = ch - 'a' + 1;
printf(" %d \n", order);
}
if (order != (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
{
if (ch == '\n');
else if (order != (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
printf(" -1 \n");
}
}
system("pause");
}
You are talking about an "exception" which can be interpreted in other ways in programming.
I understand that you want that '\n' be "excepted" in the set of nonalphabetical characters, that is, that it doesn't generate the error value -1.
Since you are using console to run the program, a sequence of character is going to be read till ENTER key is pressed, which generates the character \n. So, I'm not pretty sure that the while() condition you used, that compares against EOF, it's a good decision of yours.
I would put there directly the comparisson against '\n'.
while ((ch = getchar()) != '\n')
To inform if ch is a letter or not, we could use string literals. The following use of string assignment would deserve more explanation, but I will omit it. It's valid with string literals:
char *info;
if (order != -1)
info = "is a letter";
else
info = "is not a letter";
You are assuming an encoding where the letters are in contiguous increasing order (as in ASCII).
By assuming that, it's enough to work with uppercase or lowercase letters, since you are only interested in the position that the letter occupy in the alphabet. So, you can choose to work with uppercase, for example, in this way:
if (ch >= 'a' && ch <= 'z')
ch = (ch - 'a' + 'A');
The effect of that line of code is that ch is converted to uppercase, only if ch is a lowercase letter. Another kind of character is not affected.
As a consequence, from now on, you only have uppercase letters, or nonalphabetical characters.
Then it's easy to code the remaining part:
if (ch >= 'A' && ch <= 'Z')
order = ch - 'A' + 1; // It brings no. position of letter in alphabet
else
order = -1; // This is the erroneous case
A printf() at the end of the loop could bring all the information about the character:
printf(" %16s: %4d \n", info, order);
The resulting code is shorter in more clear:
#include <stdio.h>
int main(void) {
char ch;
int order;
char *info;
while ((ch = getchar()) != '\n') {
printf("%c",ch);
if (ch >= 'a' && ch <= 'z') /* Converting all to uppercase */
ch = (ch - 'a' + 'A');
if (ch >= 'A' && ch <= 'Z')
order = ch - 'A' + 1; /* Position of letter in alphabet */
else
order = -1; /* Not in alphabet */
if (order != -1)
info = "is a letter";
else
info = "is not a letter";
printf(" %16s: %4d \n", info, order);
}
}
If you need to end the input by comparing against EOF, then the type of ch has to be changed to int instead of char, so you can be sure that the EOF value (that is an int) is properly held in ch.
Finally, this means that ch needs initialization now, for example to a neutral value in the program, as '\n'.
Finally, just for fun, I add my super-short version:
#include <stdio.h>
int main(void) {
int ch, order;
while ((ch = getchar()) != '\n') {
order = (ch>='a' && ch<='z')? ch-'a'+1:((ch>='A' && ch<='Z')? ch-'A'+1: -1);
printf("%c %8s a letter: %4d \n", ch, (order != -1)? "is":"is not", order);
}
}
The C language does not have exceptions. Exceptions were first introduced into programming in C++. You can do it manually in C using setjmp() and longjmp(), but it really isn't worth it.
The two most popular of doing error handling in C are:
Invalid return value. If you can return -1 or some other invalid value from a function to indicate 'there was an error', do it. This of course doesn't work for all situations. If all possible return values are valid, such as in a function which multiplies two numbers, you cannot use this method. This is what you want to do here - simply return -1.
Set some global error flag, and remember to check it later. Avoid this when possible. This method ends up resulting in code that looks similar to exception code, but has some serious problems. With exceptions, you must explicitly ignore them if you don't want to handle the error (by swallowing the exception). Otherwise, your program will crash and you can figure out what is wrong. With a global error flag, however, you must remember to check for them; and if you don't, your program will do the wrong thing and you will have no idea why.
First of all, you need to define what you mean by "exception"; do you want your program to actually throw an exception when it sees a newline, or do you simply want to handle a newline as a special case? C does not provide structured exception handling (you can kind-of sort-of fake it with with setjmp/longjmp and signal/raise, but it's messy and a pain in the ass).
Secondly, you will want to read up on the following library functions:
isalpha
tolower
as they will make this a lot simpler; your code basically becomes:
if ( isalpha( ch ) )
{
// this is an alphabetic character
int lc = tolower( ch ); // convert to lower case (no-op if ch is already lower case)
order = lc - 'a' + 1;
}
else
{
// this is a non-alphabetic character
order = -1;
}
As for handling the newline, do you want to just not count it at all, or treat it like any other non-alphabetic character? If the former, just skip past it:
// non-alphabetic character
if ( ch == '\n' )
continue; // immediately goes back to beginning of loop
order = -1;
If the latter, then you don't really have to do anything special.
If you really want to raise an honest-to-God exception when you see a newline, you can do something like the following (I honestly do not recommend it, though):
#include <setjmp.h>
...
jmp_buf try;
if ( setjmp( try ) == 0 ) // "try" block
{
while ( (ch = getchar() ) != EOF )
{
...
if ( ch == '\n' )
longjmp( try, 1 ); // "throw"
}
}
else
{
// "catch" block
}
I'm having hard time trying to understand why you even try to handle '\n' specifically.
You might be trying to implement something like this:
int main(void)
{
char ch;
int order;
printf("Enter letters and it will tell you the location in the alphabet.\n");
while ((ch = getchar()) != EOF)
{
printf("%c", ch);
if (ch >= 'A' && ch <= 'Z') {
order = ch - 'A' + 1;
printf(" %d \n", order);
} else if (ch >= 'a' && ch <= 'z') {
order = ch - 'a' + 1;
printf(" %d \n", order);
} else if (ch == '\n') { } else {
printf(" -1 \n");
}
}
system("pause");
}
While this is a good solution, I would recommend rewriting it in a more optimal way:
int main(void)
{
char ch;
printf("Enter letters and it will tell you the location in the alphabet.\n");
while ((ch = getchar()) != EOF)
{
int order;
if (ch != '\n'){
if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') {
order = ch & 11111B;
printf("Letter %d\n", order);
} else {
order = -1;
printf("Not letter: %d\n", order);
}
}
}
system("pause");
}
This way the program relies on specific way letters coded in ASCII

C array push, why subtract '0'?

I'm learning C from The C Programming Language, Second Edition. In it, there is the following code:
#include <stdio.h>
/* count digits, white space, others */
main() {
int c, i, nwhite, nother;
int ndigit[10];
nwhite = nother = 0;
for (i=0; i<10; ++i) {
ndigit[i] = 0;
}
while ((c = getchar()) != EOF) {
if (c >= '0' && c <= '9') {
++ndigit[c-'0'];
}
else if (c == ' ' || c == '\n' || c == '\t') {
++nwhite;
}
else {
++nother;
}
}
printf("digits =");
for (i=0; i<10; ++i) {
printf(" %d", ndigit[i]);
}
printf(", white space = %d, other = %d\n", nwhite, nother);
}
Now, I can understand what this code is doing. It is counting how many times each digit appears in the input, and then putting that count into the index of the digit, ie 11123 = 0 3 1 1 0 0 0 0. I'm just curious about 1 line of it:
++ndigit[c-'0'];
This adds 1 to the index c of the array, but why does it subtract 0 from c? Surely that's pointless, right?
The expression c - '0' is converting from the character representation of a number to the actual integer value of the same digit. For example it converts the char '1' to the int 1
I think it makes a bit more sense to look at complete examples here
int charToInt(char c) {
return c - '0';
}
charToInt('4') // returns 4
charToInt('9') // returns 9
It's not subtracting zero... It's subtracting the ASCII value of the character '0'.
Doing so gives you an ordinal value for the digit, rather than its ASCII representation. In other words, it converts the characters '0' through '9' to the numbers 0 through 9, respectively.

Resources