What's wrong with this string checking program? - c

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.

Related

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

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 :)

fuggy concept on how function and character array work in function parameter

** I understood how the function getline is working, it simply assigning value in each s[] array address which gets stored into the char line[] array because function argument has local scope there is no conflict due to the use of different array names unless it shares the same data type, But my concern is that why checker function has no effect on an actual string. correct me if the above understanding is wrong and why the checker function not working.**
the expected result was, getting string without trailing blanks and tabs like (hello World) but instead of that actual input that I typed in is printed which is ('\s'hello World'\t').
#define MAXLINE 50
int getline(char line[], int max);
int checker(char line[]);
int main(){
char line[MAXLINE];
while( getline(line, MAXLINE) > 0 )
if( checker(line) > 0 )
printf("%s",line);
return 0;
}
int getline(char s[],int lim){
int c,i,j;
j=0;
for(i=0; (c=getchar()) != EOF && c != '\n';i++){
if(i < lim-1){
s[j]=c;
++j;
}
}
if(c == '\n'){
s[j] = c;
++j;
++i;
}
s[j] = '\0';
return i;
}
int checker(char s[]){
int i;
i=0;
while(s[i] != '\n' )
++i;
--i;
while(i >= 0 && (s[i] == ' ' || s[i] == '\t') )
i++;
if( i >= 0){
++i;
s[i] = '\n';
++i;
s[i] = '\0';
}
return i;
}
If you are trying to trim trailing blanks and tabs from your string, try changing the contents of the second while loop in checker() to contain i-- rather than i++.
Since checker() is intended to change the string, perhaps a different name would be better. The word check does not usually imply modification. A well chosen name is a great help to the next person who encounters your code.
The bug seems to be here:
while(i >= 0 && (s[i] == ' ' || s[i] == '\t') )
i++;
^^^^
This shall probably be i--;
That said... Your function isn't secure. It lacks some checks to prevent access outside the char array.
For instance:
What happens if the input string has no '\n' ?
What happens if the input string is a space followed by '\n' ?
Also the getline function has a problem. If the input is longer than lim, the code will do s[lim] = '\0'; which is out of bounds.

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.

Program to find vowels, If statements not assigning correct values [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
I have a simple program to find the vowels in a string. The for loop is supposed to iterate through the string and see if the char matches any of the vowels using and if else block but the output is just 100 As.
I tried making them all just ifs but that gave all Us.
#include <stdio.h>
int main()
{
const int SIZE = 100;
char str[SIZE] = {"the brown fox\0"};
char vowels[SIZE];
for (int i = 0; i <= SIZE; i++) {
if (str[i] == '97' || '65') {
vowels[i] = 'a';
}
else if (str[i] == '101' || '69' ) {
vowels[i] = 'e';
}
else if (str[i] == '105' || '73') {
vowels[i] = 'i';
}
else if (str[i] == '111' || '81') {
vowels[i] = 'o';
}
else if (str[i] == '117' || '85') {
vowels[i] = 'u';
}
printf("%c", vowels[i]);
}
return 0;
}
EDIT: Fixed the assignment if e.g. (str[i] == '97' || str[i] == '65') now it's printing strange symbols
EDIT 2: New code
#include <stdio.h>
int main()
{
const int SIZE = 100;
char str[SIZE] = {"the brown fox\0"};
char vowels[SIZE];
for (int i = 0; i <= SIZE; i++) {
if (str[i] == 'a' || str[i] == 'A') {
vowels[i] = 'a';
}
else if (str[i] == 'e' || str[i] =='E' ) {
vowels[i] = 'e';
}
else if (str[i] == 'i' || str[i] == 'I') {
vowels[i] = 'i';
}
else if (str[i] == 'O' || str[i] == 'o') {
vowels[i] = 'o';
}
else if (str[i] == 'u' || str[i] == 'U') {
vowels[i] = 'u';
}
printf("%c", vowels[i]);
}
return 0;
}
EDIT 3: Even after initialing vowels to '' at the start of the loop as suggested the strange symbols are gone but it's still not functioning properly.
You are comparing your char str[i] with '97'
6.4.4.4
An integer character constant has type int. The value of an integer character constant containing a single character that maps to a single-byte execution character is the numerical value of the representation of the mapped character interpreted as an integer. The value of an integer character constant containing more than one character (e.g., 'ab'), or containing a character or escape sequence that does not map to a single-byte execution character, is implementation-defined.
If you want to compare a char you can use the ascii value for example 97 or directly the char with 'c'.
For more maintenability and readability I prefer using the char directly.
There is other problems in your code:
First, in your for loop: for (int i = 0; i <= SIZE; i++) {
You are going too far in your array because of your <= as arrays id starts with 0, if you type str[100], in reality you are using the 101st char.
Another problem is your if statements: if (str[i] == '97' || '65') {
Here your if statement is equivalent to if (str[i] == '97' || '65' != 0) {
Consider retyping str[i] == : if (str[i] == '97' || str[i] == '65') {
Plus don't forget the first problem I mentionned about your '97'
You have a very large number of small problems summarized below:
#define SIZE 100 /* if you need a constant, #define one (or more) */
...
char vowels[SIZE] = ""; /* initialize all zero, {0) is valid also */
An integer constant is created by #define or by use of an enum. A const qualified int is not a constant integer. (that said VLAs are legal in C99, but optional in C11)
int idx = 0; /* separate index for filling vowels array */
Keep a separate index for filling the vowels array.
/* don't use magic-numbers in your code */
if (str[i] == 'a' || str[i] == 'A') {
Don't use magic-numbers, instead, use literal character constants were needed in your code to produce much more readable code.
Your program takes arguments, use them to pass the string to parse (or read from stdin), e.g.
int main (int argc, char **argv) {
const char *str = (argc > 1) ? argv[1] : "the brown fox";
...
The test ? if_true : if_false operator is called the ternary operator. It allows a simple in-line conditional to select one of two values based on the test condition (e.g. (argc > 1))
If you plan on using vowels as a string, don't forget to nul-terminate vowels after the loop, e.g.
vowels[idx] = 0; /* nul-terminate vowels */
Correcting all the errors and adding the arguments to main() you could do something similar to:
#include <stdio.h>
#define SIZE 100 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
const char *str = (argc > 1) ? argv[1] : "the brown fox";
char vowels[SIZE] = ""; /* initialize all zero, {0) is valid also */
size_t idx = 0; /* separate index for filling vowels array */
for (int i = 0; idx < SIZE - 1 && str[i]; i++) {
/* don't use magic-numbers in your code */
if (str[i] == 'a' || str[i] == 'A') {
vowels[idx++] = 'a'; /* assign 'a', increment index */
}
else if (str[i] == 'e' || str[i] == 'E' ) {
vowels[idx++] = 'e';
}
else if (str[i] == 'i' || str[i] == 'I') {
vowels[idx++] = 'i';
}
else if (str[i] == 'o' || str[i] == 'O') {
vowels[idx++] = 'o';
}
else if (str[i] == 'u' || str[i] == 'U') {
vowels[idx++] = 'u';
}
}
vowels[idx] = 0; /* nul-terminate vowels */
printf (" %zu vowels: ", idx); /* print number of vowels */
for (int i = 0; vowels[i]; i++) /* output each vowel, comma-separated */
printf (i > 0 ? ", %c" : "%c", vowels[i]);
putchar ('\n'); /* tidy up with newline */
return 0;
}
Example Use/Output
bin\vowels.exe "a quick brown fox jumps over the lazy dog"
11 vowels: a, u, i, o, o, u, o, e, e, a, o
Depending on your compiler str[i] == '117' (and the rest) may give you an error as signle quotes are only to be used when you want to implement the ascii equivalent of a single character like 'a' or so. Therefore str[i] == '117' is checking if str[i] is equal to the ascii equivalent of "117".
Other than that " || " is a logical "or" operator. When you write down str[i] == '111' || '81' you simply mean "find ascii codes of 111 and 81(which dont exist) , use them in "or" operation, check if the result equals str[i]".
last but not least i found a nice function online which might help making your code more compact
int isvowel(int ch)
{
int c = toupper(ch);
return (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U');
}
to explain it shortly, if the char equivalent of given int ch is lowercase, the function changes it to uppercase and checks if it is an uppercase vowel, if the given integer already equals an uppercase vowel int c = toupper(ch); doesnt change anything.
Implementation can be done as:
for(int i=0; i<SIZE; i++) //scan all emelents of str[SIZE]
{
if(isvowel(str[i])) //print if vowel
printf ("%c", str[i]);
}

Resources