Counting the number of words, numbers, uppercase and lowercase characters - c

I am a beginner programmer and there was this exercise I found that said:
Write a string of characters and determine the number of words, numbers, uppercase and lowercase characters and spaces.
I thought I built a decent enough program and it works, kind of!
The problem is that when I try to run it the result is not entirely correct.
For example; When I write: HI MY name is Ani 1 1 2 a
it says that
Spaces = 8. Correct here
Numbers = 3. Correct here as well
Upper Case characters = 4. It should be 5
Lower Case characters = 7. It should be 9
Words = 26. Which is completely wrong
As for the words, I found a new way to count them. By counting spaces+1, but I want to count them correctly.
Is it possible to point out the mistakes?
This is what I have done so far
int main() {
char str[1000+1];
int words = 0;
int numbers = 0;
int uppercharacters = 0;
int lowercharacters = 0;
int spaces = 0;
int i;
printf("Please enter the string of characters: ");
gets(str);
for (i = 0; str[i] != '\0'; i++) {
if (str[i] > 'a' && str[i] < 'z')
lowercharacters++;
else if (str[i] > 'A' && str[i] < 'Z')
uppercharacters++;
else if (str[i] == ' ')
spaces++;
else if (str[i] > '0' && str[i] < '9')
numbers++;
else if (str[i] == ' ' && str[i + 1] != ' ');
words++;
}
printf("Spaces = %d\n", spaces);
printf("numbers = %d\n", numbers);
printf("Upper Case characters = %d\n", uppercharacters);
printf("Lower Case characters = %d\n", lowercharacters);
printf("Words = %d\n", words + 1);
return 0;
}

As for the words, I found a new way to count them. By counting spaces+1, but I want to count them correctly.
The code fails due to ; at the end of the else if().
Tip: Good compilers with all warnings enabled will warn about that.
Save time, and enable all warnings.
// v !!!
else if(str[i]==' ' && str[i+1]!=' ');
words++;
Even if corrected to
else if(str[i]==' ' && str[i+1]!=' ')
words++;
it still fails with input like " abc" (lead space) reports as two words.
Instead, count the occurrences of a letter following a non-letter.
char previous = '\n';
for(i=0; str[i] != '\0'; i++) {
if (isalpha(str[i]) && !isalpha(previous)) {
words++;
}
previous = str[i];
}
Make your own helper functions if the standard ones, like is...(), are not allowed.

You should use if(str[i]>='a' && str[i]<='z') instead of if(str[i]>'a' && str[i]<'z'). You don't want to exclude the characters z and a from being tested.

For the counting words part, notice that there is one misplaced semicolon after your last else if statement. The number of words won't be 100% correct if you fix that typo, but you might be able to work from there :)

Related

What's wrong with this string checking program?

I want to make this program only English in the string, but when the end of the string ends in English, another strange word is added. Why is that?
int main(void)
{
char line[100];
char line2[100];
printf("Please enter a string: ");
gets_s(line, sizeof(line));
int k = 0;
for(int i = 0; line[i] != '\0'; ++i) {
while(line[i] >= 'A'&& line[i] <= 'z') {
line2 [k++] = line[i++];
}
}
line2[k] = '\0';
printf("string: %s\n", line2);
return 0;
}
for(int i = 0; line[i] != '\0'; ++i) {
while(line[i] >= 'A'&& line[i] <= 'z') {
line2 [k++] = line[i++];
}
}
replacing the for loop with a while loop...
int i = 0;
while (line[i] != '\0') {
while (line[i] >= 'A' && line[i] <= 'z') {
line2 [k++] = line[i++];
}
i++;
}
So, here you can see if the inner while goes to the '\0' i gets incremented past the terminating zero byte.
The basic problem is that you put responsibility for incrementing i in two different places. As a result, it can get incremented more than you want -- for example, so as to skip over the string terminator.
It appears to me that you have caused this problem for yourself by making the code more complicated than it needs to be. Why use a two-loop nest to iterate over a single string? In fairness, there are indeed potential reasons to do that, but none of them appear to be in evidence here. I suggest changing the inner while loop to an if statement, and not incrementing i within:
for (int i = 0; line[i] != '\0'; ++i) {
if (line[i] >= 'A' && line[i] <= 'z') {
line2[k++] = line[i];
}
}
Note how there is only one place where i is incremented in that variation.
Note also that there may be characters that satisfy c >= 'A' && c <= 'z' but are not letters. I say "may" because C is not specific to a particular character set or encoding, but in practice, it is virtually certain that there are non-letter characters in that range on your implementation. Perhaps isalpha((unsigned char) line[i]) would be a better fit, though it is not, by itself, without character set related issues.

C program to capitalize a word inside quotation marks

I need to build a function that gets an input and capitalizes only the first letter, doesn't print numbers, capitalizes after a . for a new sentence, and capitalizes all words between a double quotation marks ".
This is what I got until now:
#include <stdio.h>
#define MAX 100
int main()
{
char str[MAX] = { 0 };
int i;
//input string
printf("Enter a string: ");
scanf("%[^\n]s", str); //read string with spaces
//capitalize first character of words
for (i = 0; str[i] != '\0'; i++)
{
//check first character is lowercase alphabet
if (i == 0)
{
if ((str[i] >= 'a' && str[i] <= 'z'))
str[i] = str[i] - 32; //subtract 32 to make it capital
continue; //continue to the loop
}
if (str[i] == '.')//check dot
{
//if dot is found, check next character
++i;
//check next character is lowercase alphabet
if (str[i] >= 'a' && str[i] <= 'z')
{
str[i] = str[i] - 32; //subtract 32 to make it capital
continue; //continue to the loop
}
}
else
{
//all other uppercase characters should be in lowercase
if (str[i] >= 'A' && str[i] <= 'Z')
str[i] = str[i] + 32; //subtract 32 to make it small/lowercase
}
}
printf("Capitalize string is: %s\n", str);
return 0;
}
I cant find a way to remove all numbers from input and convert all lowercase to uppercase inside a " plus code for not printing numbers if user input them.
if I input
I am young. You are young. All of us are young.
"I think we need some help. Please" HELP. NO, NO NO,
I DO NOT
NEED HELP
WHATSOEVER.
"Today’s date is
15/2/2021"...
I am 18 years old, are you 20 years old? Maybe 30 years?
output:
I am young. You are young. All of us are young.
"I THINK WE NEED SOME HELP. PLEASE" help. No, no no,
i do not
need help
whatsoever.
"TODAY’S DATE IS
//"...
I am years old, are you years old? maybe years?
The C standard library provides a set of functions, in ctype.h, that will help you
Of particular interest, would be:
isdigit() - returns true if digit
isalpha() - returns true if alphabet character
isalnum() - returns true if alpha/numeric character
islower() - returns true if lower case character
isupper() - returns true if upper case character
tolower() - converts character to lower case
toupper() - converts character to upper case
So, for example, you could replace the test/modify with:
if ( islower( str[i] ) )
{
str[i] = toupper( str[i] );
}
Pedantically, islower() and toupper() return an unsigned int but that's a separate matter...
You can remove letters from a string if you keep two indices, one for reading and one for writing. The following loop will remove all digits from a string:
int j = 0; // writing index, j <= i
int i; // reading index
for (i = 0; str[i]; i++) {
int c = (unsigned char) str[i];
if (!isdigit(c)) str[j++] = c;
}
str[j] = '\0';
(I've used to character classification functions from <ctype.h> mentioned in Andrew' answer.)
This is safe, because j will always be smaller or equal to i. Don't forget to mark the end of the filtered string with the nullterminator, '\0'. You can combine this filtering with your already existing code for replacing characters.
In your code, you capitalize letters only if they are directly behind a full stop. That's usually not the case, there's a space between full stop and the next word. It's better to establish a context:
shift: capitalize the next letter (beginning or after full stop.)
lock: capitalize all letters (inside quotation marks.)
When you read a letter, decide whether to capitalize it or not depending of these two states.
Putting the filtering and the "shift context§ together:
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char str[] = "one. two. THREE. 4, 5, 6. \"seven\", eight!";
int shift = 1; // Capitalize next letter
int lock = 0; // Capitalize all letters
int j = 0; // writing index, j <= i
int i; // reading index
for (i = 0; str[i]; i++) {
int c = (unsigned char) str[i];
if (isdigit(c)) continue;
if (isalpha(c)) {
if (shift || lock) {
str[j++] = toupper(c);
shift = 0;
} else {
str[j++] = tolower(c);
}
} else {
if (c == '"') lock = !lock;
if (c == '.') shift = 1;
str[j++] = c;
}
}
str[j] = '\0';
puts(str);
printf("(length: %d)\n", j);
return 0;
}
In order to remove some characters, you should use 2 index variables: one for reading and one for writing back to the same array.
If you are allowed to use <ctype.h>, it is a much more portable and efficient way to test character types.
Also do not use scanf() with protection against buffer overflow. It is as bad as using gets(). Given the difficulty in specifying the maximum number of bytes to store into str, you should use fgets() instead of scanf().
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#define MAX 100
int main() {
char str[MAX];
int i, j;
unsigned char last, inquote;
//input string
printf("Enter a string: ");
if (!fgets(str, sizeof str, stdin)) { //read string with spaces
// empty file
return 1;
}
last = '.'; // force conversion of first character
inquote = 0;
//capitalize first character of words
for (i = j = 0; str[i] != '\0'; i++) {
unsigned char c = str[i];
//discard digits
if (isdigit(c)) {
continue;
}
//handle double quotes:
if (c == '"') {
inquote ^= 1;
}
//upper case letters after . and inside double quotes
if (last == '.' || inquote) {
str[j++] = toupper(c);
} else {
str[j++] = tolower(c);
}
if (!isspace(c) && c != '"') {
// ignore spaces and quotes for the dot rule
last = c;
}
}
str[j] = '\0'; // set the null terminator in case characters were removed
printf("Capitalized string is: %s", str);
return 0;
}

reading space character into string with size determined by str_size

I'm trying to make this program such that the user could type any given string of characters, and the program would separate alphanumerical characters from the rest, print them into a second string, and finally print the final result into the screen.
I've already tried using scanf ("%[^\n]%*c", string);, but it doesn't seem to work since the size of the string is not specified beforehand, and is rather defined by STR_SIZE.
char string[STR_SIZE];
printf("please type in a string \n");
scanf("%s", string);
printf("string: \n %s \n", string);
int size = (strlen(string));
char alfanumerico[STR_SIZE];
int count = 0;
int count2 = 0;
while(count <= size)
{
if(string[count] >= '0' && string[count] <= '9')
{
alfanumerico[count2] = string[count];
count2++;
}
if(string[count] >= 'a' && string[count] <= 'z')
{
alfanumerico[count2] = string[count];
count2++;
}
if(string[count] >= 'A' && string[count] <= 'Z')
{
alfanumerico[count2] = string[count];
count2++;
}
if(string[count] ==' ')
{
alfanumerico[count2] = string[count];
count2++;
}
count++;
}
printf("alphanumerical characters typed: \n %s \n", alfanumerico);
Given the user typed a string such as: -=-=[[][][]}}Hello 123 ```//././.
I expect the output to be: Hello 123
scanf is not the way to go, especially if your input might contain white-spaces on which scanf would stop reading more inputs and wouldn't store spaces for instance.
You should use fgets which lets you limit the input data according to the buffer this data is stored in. So something like:
fgets(string, STR_SIZE, stdin)
should work.
About the size - you should have some limitation about the maximum size of the string and then STR_SIZE should be set to this number. It should be part of your program requirements or just a size that makes sense if you're making the requirements. It must be defined before you're reading input from the user because the buffer memory is allocated before reading to it.
A comment about style, unrelated to your question - always try to decrease code duplication to 0. The line alfanumerico[count2] = string[count]; count2++; appears 4 times in your code. A more elegant minimal if statement with exactly the same functionality would be:
if ((string[count] >= '0' && string[count] <= '9') ||
(string[count] >= 'a' && string[count] <= 'z') ||
(string[count] >= 'A' && string[count] <= 'Z') ||
(string[count] == ' '))
{
alfanumerico[count2] = string[count];
count2++;
}
and to be even more minimal:
char c = string[count];
if ((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c == ' '))
{
alfanumerico[count2] = c;
count2++;
}
It's also more readable and more maintainable - if you want to change the variable count to i you do it in one place instead of 8.
Also, always close a scope in a new line.

Issue with String position appending C program

I am trying to write program for piglatin. I was not getting the output what I am expecting.
take the first letter of a “word” and appending that letter to the end of the word with “ay” added to the end as well.
Input : Darrin, what are you doing with 500 and 100?
Output: arrin, hatway reaay ouyay oingday ithway 500 ndaay 100?
Expected Output: arrinday,hatway reay ouyay oingday ithway 500 nday 100?
What's wrong with output : First word not appended with ay
Since I am appending 'ay', I need eliminate the extra 'a' if the word starts with a or end's with 'a'. I just need add ay at the end instead of first letter + ay. For example: Input is Alex and allen are 500 Output should be lexay nday llenay
Also if the starting letter is not alphabet then we should return the same word.
Please help me to solve this
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
static char inputBuffer[100];
static char outputBuffer[100];
void translate (void)
{
char bufferValue;
char firstLetter;
int j = 0, k = 0, m = 0;
printf("\n");
while (j < (sizeof(inputBuffer) - 1))
{
bufferValue = inputBuffer[j];
if (((bufferValue >= 'A') && (bufferValue <= 'Z')) || ((bufferValue >= 'a') && (bufferValue <= 'z')))
{
if (j == 0)
{
firstLetter = bufferValue;
}
else if (inputBuffer[j-1] == ' ')
{
firstLetter = bufferValue;
}
else
{
printf("%c", bufferValue);
outputBuffer[m] = bufferValue; m++;
}
}
else if ((bufferValue == ' ') && !(
((inputBuffer[j-1] < 'A') ||
((inputBuffer[j-1] > 'Z') && (inputBuffer[j-1] < 'a')) ||
(inputBuffer[j-1] > 'z'))))
{
printf("%cay%c", firstLetter, bufferValue);
outputBuffer[m] = firstLetter; m++;
outputBuffer[m] = 'a'; m++;
outputBuffer[m] = 'y'; m++;
outputBuffer[m] = bufferValue; m++;
firstLetter = ' ';
}
else
{
printf("%c", bufferValue);
outputBuffer[m] = bufferValue; m++;
}
j++;
}
printf("\n final output: %s",outputBuffer);
return;
}
int main(void)
{
printf("enter the string\t");
fflush(stdin);
gets(inputBuffer);
printf ("\nInput buffer contents: %s", inputBuffer);
translate();
return 0;
}
First word not appended with ay
The problem is just not that only the first word is not being appended by first letter and ay, but whenever you have some non alphabet character at the end of a word (digits/special characters, except space), ay will not be appended to that word.
For example, try this input:
Darrin, what, are you doing with 500 and 100?
You'll get the output:
arrin, hat, reaay ouyay oingday ithway 500 ndaay 100?
So mainly, the problem is in the last else you have:
else
{
printf("%c", bufferValue);
outputBuffer[m] = bufferValue; m++;
}
See, when , comes immediately after a word, the control comes to this else and it just adds the , as it is, it does not append the firstLetter and ay.
But you can't always append firstLetter and ay in this else, you'll have to come up with some kind of condition, so you could separate the 500 and Darrin,, cause 500 will also go through this else statement.
Maybe, you could try checking if firstLetter is an alphabet or not, if it is, then append the firstLetter and ay, otherwise not.
else
{
if ((firstLetter >= 'a' && firstLetter <= 'z') || (firstLetter >= 'A' && firstLetter <= 'Z'))
printf("%cay", firstLetter);
outputBuffer[m] = firstLetter; m++;
outputBuffer[m] = 'a'; m++;
outputBuffer[m] = 'y'; m++;
firstLetter = ' ';
}
printf("%c", bufferValue);
outputBuffer[m] = bufferValue; m++;
}
But this will still not process the words like 0abcdef,, which do have alphabets in it, but start with some non-alphabet character, so that's your call, if you want to put them to the numbers category (like 500), to leave them as they are, or to process them.
Here is the working example.
P.S. I've made some other changes too (which don't affect your output), but the major change was what I explained (which does).
EDIT:
From the comments below:
If the word starts with Vowel(a,e,i,o,u) then just add y else first letter + ay
You can write a function in your program called isVowel to check if some character is vowel or not:
int isVowel(char c)
{
c = tolower(c);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
return 1;
return 0;
}
Now, you're adding ay at two places in your program:
In the else if and the last else:
outputBuffer[m] = firstLetter; m++;
outputBuffer[m] = 'a'; m++;
outputBuffer[m] = 'y'; m++;
firstLetter = ' ';
So, you can add an if at the statements outputBuffer[m] = 'a'; m++; to only add this a if the firstLetter is not a vowel:
outputBuffer[m] = firstLetter; m++;
if (!isVowel(firstLetter))
{
outputBuffer[m] = 'a';
m++;
}
outputBuffer[m] = 'y'; m++;
firstLetter = ' ';
change this at both places i.e. in the else if and else, and you'll be done.
I've updated the code on ideone
The real problem is that you didn't see the forest through the trees which made the implementation awful to read. To add insult to injury, you decided to break the basic rules of code locality (not using globals unless necessary) and DRY (functions to tell if a charater is a letter exist in the standard library of any language I can think of, don't reimplement it), which made it pretty much irrecoverable as far as maintenance is concerned.
Now, let's read the task description again:
take the first letter of a “word” and appending that letter to the end of the word with “ay” added to the end as well.
Notice what already stands out because of quoting: word.
So, I'd divide the implementation into two distinct tasks:
Iterate through a sentence word by word.
Once you can reliably identify words, do the piglatin thing.
The end result of might look like this:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
void piglatinize(const char* in)
{
static const char* SEP = " .,?"; // word separators
// Iterate input by words
const char *sep = NULL, *word = NULL, *end = in;
while (sep = end, // separators from previous word end
word = &end[strspn(end, SEP)], // start of word
end = &word[strcspn(word, SEP)], // end of word
*sep) // iterate until we hit terminating zero character
{
int wordlen = (int)(end - word);
int seplen = (int)(word - sep);
if (wordlen > 0 && isalpha(word[0])) // word starts with a letter, pig it!
{
char firstletter = tolower(word[0]);
const char* suffix = (firstletter == 'a') ? "y" : "ay";
printf("%.*s%.*s%c%s",
seplen, sep, // separators from previous word
wordlen - 1, &word[1], // word without first letter
firstletter, suffix);
}
else // not a real word, just print unchanged
{
printf("%.*s%.*s", seplen, sep, wordlen, word);
}
}
}
int main()
{
piglatinize("Darrin, what are you doing with 500 and 100?");
}
I admit the while loop continuation condition is a handful. If you have trouble understanding this example you might want to read on strspn (and its opposite strcspn) and the comma operator.

Function to check for alphabetic characters

I created a Function to check if user typed a Real Name excluding all other non alphabetic characters.
Well, from my side, as a beginer in C language its works fine.
Anyway i have just a small problem, with the string name, if there is space inside that string i get wrong Name, but if there is only one name (michi) everything is ok.
#include <stdio.h>
#include<string.h>
/* Here is the Function which check if the string contains only: */
/* abcdefghijklmnopqrstuvwxyz and ABCDEFGHIJKLMNOPQRSTUVWXYZ */
int checkName(char *s){
int i,length;
length = (strlen(s));
for (i=0;i<length;i++){
if(s[i] == '0' || s[i] <= '9'){
return 1;
}
}
return 0;
}
int main(){
char name[]= "Michi";
int check;
if((check = checkName(name)) == 0){
printf("\n\n\t\t\tYour name is:\t%s\n\n",name);
}else{
printf("\n\n\t\t\tWrong name:\t%s\n\n",name);
}
return 0;
}
My questions are:
1)
Did i found a right way of checking if string contains only non alphabetic characters.
2)
How can i extend my Function to skip spaces
Take a look at isalpha in ctype.h. This returns true if a char is a letter, just like what you want.
http://www.cplusplus.com/reference/cctype/isalpha/
By the way, if you're checking ASCII encodings, your function fails for characters such as '(' or '~'.
Here is the Function which check if the string contains only:
abcdefghijklmnopqrstuvwxyz and ABCDEFGHIJKLMNOPQRSTUVWXYZ
Looking at the code below that statement, you're lying. What your code does is checking whether there is a character 0 or any character below 9 in the string. Better do what you're saying:
if((str[i] >= 'a' && str[i] <= 'z') ||
(str[i] >= 'A' && str[i] <= 'Z') ||
(str[i] == ' ')) {
// fine ..
}
else {
// not fine!
}
As you see I added the space to the set of allowed characters. To get rid of the if branch just negate the whole test expression (either by hand or using the not operator !).
The comparisons are working this way because of the layout of the ASCII table.
Note that there's a library function for this: isalpha
If you have a valid set, test against this set, not other sets that might or might not be the complement set (So many sets in one sentence :-):
for (i=0; i<length; i++) {
int valid = 1;
valid &= s[i] >= 'a' && s[i] <= 'z';
valid &= s[i] >= 'A' && s[i] <= 'Z';
valid &= s[i] == ' ';
if (!valid) {
return 0; // or any value you prefer to indicate "not valid"
}
}
If you want to check only alphabetic chars and space, you can use isapha and isspace from ctype.h. These functions return non-zero for ture and zero for false.
You can just continue the loop if the character is a space:
for (i=0;i<length;i++){
if(s[i] == ' '){
continue;
}
else if(s[i] == '0' || s[i] <= '9'){
return 1;
}
}
Furthermore, you could also make sure it does not contain any other character than just alphabetic, by checking if all character are not outside the range of accepted characters:
for (i=0;i<length;i++){
if((s[i] < 'A') || (s[i] > 'Z' && s[i] < 'a') || (s[i] > 'z')){
return 1;
}
}
Note: the ASCII table is a nice "tool" to confirm the range you have to check.

Resources