counting words of a string - c

As the title states, few things I must add to explain: " "/tab/","/"." are things that divide words in my situation, another note that there can be more the one space or dot coming one after the other
this is what I have
int countWords(char * str, int length){
int counter = 0;
for( int i = 0; i < length; i++){
if(( str[i] == " ") || ( str[i] == ".") || ( str[i] == ",")){
if(( str[i+1] != " ") || ( str[i+1] != ".") || (str[i+1] != ",")){
if(( str[i-1] != " ") || ( str[i-1] != ".") || (str[i-1] != ","))
counter++;
}
}
}
return counter;
}
I get an error saying that I can not compare int and a pointer, I do understand where that is coming from, but how can I get it to actually work?
Note* I can't use string.h

In expressions like this
str[i] == " "
^^^
you are trying to compare an object of type char with a string literal that is implicitly converted to the type char *.
You have to use a character constant instead of the string literal
str[i] == ' '
^^^
The enclosing if statements do not make sense. For example for i equal to 0 the expression str[i-1] tries to access memory beyond the string.
The first parameter of the function should be declared with qualifier const.
The function can look the following way as it is shown in the demonstrative program.
#include <stdio.h>
size_t countWords( const char *s )
{
size_t n = 0;
for (size_t i = 0; s[i] != '\0'; )
{
while (s[i] == ' ' ||
s[i] == '\t' ||
s[i] == '.' ||
s[i] == ',') ++i;
if (s[i] != '\0')
{
++n;
while ( s[i] != '\0' &&
! ( s[i] == ' ' ||
s[i] == '\t' ||
s[i] == '.' ||
s[i] == ',')) ++i;
}
}
return n;
}
int main( void )
{
char *s = " Hello\t, World...";
printf("%zu\n", countWords(s));
}
The program output is
2
Or the function's implementation can look like
size_t countWords(const char *s)
{
size_t n = 0;
while ( *s )
{
while (*s == ' ' ||
*s == '\t' ||
*s == '.' ||
*s == ',') ++s;
if ( *s )
{
++n;
while ( *s &&
!( *s == ' ' ||
*s == '\t' ||
*s == '.' ||
*s == ',' ) ) ++s;
}
}
return n;
}
A more general approach of declaring the function is the following
size_t countWords( const char *s1, const char *s2 );
where the string s2 specifies a set of word separators.

If you want to count word there is one fine example in K&R ANSI C. Try to write program like this.
#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* count lines, words, and characters in input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF)
{
++nc;
if (c == '\n')
++nl;
if (c == ' ' || c == '\n' || c = '\t')
state = OUT;
else if (state == OUT)
{
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
Hope this helps. :) You can find the pdf of this book here.

Related

I get weird symbols in the output in the terminal

I am trying to make a program that would take a sentence from the user and reverse the order of the words while keeping the special characters ['.','?','!'] at the end.
the problem I'm getting is that I get weird symbols after the last word and the special character.
I am programming using C.
I think it's because the last element printed is not a '\0'.
but I don't know how to fix that.
My code:
#include <ctype.h>
#define N 200
int main()
{
char arr[N] = { 0 };
char* p, * q, mark = 0;
int c;
p = arr;
printf("Enter a sentence: ");
while ((c = getchar()) != '\n' && p < arr + N)
{
if (c == '?' || c == '.' || c == '!')
{
mark = c;
break;
}
else
*p++ = c;
}
*p = '\0';
printf("Reversal of sentence: ");
while (p >= arr)
{
while (*--p != ' ' && p != arr);
if (p == arr) {q = arr;}
else {q = p + 1;}
while (*q != '\0' && *q != ' ')
{
printf("%c", *q++);
}
if (p >= arr)
{
printf(" ");
}
}
printf("%c", mark);
printf("\n");
return 0;
}
I've fixed the overflow error when reading the sentence by removing the \0 termination. The input string is never used by anything that cares. I've also added a check for End-Of-File.
Next I changed the reversal loop to not underflow and fixed outputing an extra ' ' before the mark.
Last the printf calls can use putchar instead.
#include <ctype.h>
#include <stdio.h>
#define N 200
int main()
{
char arr[N] = { 0 }; // initialization optional, nothing cares
char* p, * q, mark = 0;
int c;
p = arr;
printf("Enter a sentence: ");
while ((c = getchar()) != '\n' && c != EOF && p < arr + N)
{
if (c == '?' || c == '.' || c == '!')
{
mark = c;
break;
}
else
*p++ = c;
}
printf("Reversal of sentence: ");
while (p > arr)
{
while (*--p != ' ' && p != arr);
if (p == arr) {q = arr;}
else {q = p + 1;}
while (*q != '\0' && *q != ' ')
{
putchar(*q++);
}
if (p > arr)
{
putchar(' ');
}
}
putchar(mark);
putchar('\n');
return 0;
}

Problem in building a function that counts the letters in a function - Cs50

I am getting an int of the entire string s for 'letter', the conditions in my 'if' statement seem to not be reading properly - is my syntax incorrect?
I get user input:
string s = get_string("Text here: ");
the function is as follows:
int letter_count(string s)
{
int i =0;
int len = strlen(s);
int letter = 0;
while(i < len)
{
if (s[i] != '\0' || s[i] != '.' || s[i] != ',' || s[i] != '!' || s[i] != '?')
{
letter++;
}
i++;
}
return letter;
}
then call the function:
int letter = letter_count(s);
printf("letter Count: %i\n", letter);
Try changing the OR operator with the AND
if (s[i] != '\0' || s[i] != '.' || s[i] != ',' || s[i] != '!' || s[i] != '?')
is ALWAYS true. Because any character is either not "." or not ",". Which letter would you expect to be both?
You want to check whether the current letter is "not ." AND "not ," AND "not !".
I.e.
if (s[i] != '\0' && s[i] != '.' && s[i] != ',' && s[i] != '!' && s[i] != '?')
Almost correct, you have to change the type of the argument to char*, there is no string type on the default libraries. (Documentation of string library).
Working example with the modifications:
#include <stdio.h>
#include <string.h>
int letter_count (char* s)
{
int i = 0;
int len = strlen (s);
int letter = 0;
while (i < len)
{
if (s[i] != '\0' && s[i] != '.' && s[i] != ',' && s[i] != '!'
&& s[i] != '?')
{
letter++;
}
i++;
}
return letter;
}
int main ()
{
char my_word[] = "Sample word";
printf ("'%s' have %d letters",my_word, letter_count (my_word));
return 0;
}
Output:
'Sample word' have 11 letters

Is there a way to delete a repeated character in a string using pointers in C?

My teacher requested a C function with this characteristics:
int removeVowels(char *s)
{
...
}
Input (Example) : "Estaa e umaa string coom duuuplicadoos"
Output : "Esta e uma string com duplicados"
The program should also output the numbers of letters removed.
All help would be appreciated.
Attempt:
int aux[1000];
int i = 0;
int retiraVogaisRep (char *s){
if (*s == '\0'){
return 0;
}
else{
if ((*s == 'A' || *s == 'E' || *s == 'I' || *s == 'O' || *s == 'U' || *s == 'a' || *s == 'e' || *s == 'i' || *s == 'o' || *s == 'u') && aux[i] != *s){
i++;
aux[i] = *s;
return retiraVogaisRep(s + 1);
}
else if ((*s == 'A' || *s == 'E' || *s == 'I' || *s == 'O' || *s == 'U' || *s == 'a' || *s == 'e' || *s == 'i' || *s == 'o' || *s == 'u') && aux[i] == *s){
i++;
aux[i] = *s;
*s = '';
return 1 + retiraVogaisRep(s + 1);
}
else{
return retiraVogaisRep(s + 1);
}
}
}
Attemp 2 (works but is there a easier way?) :
char final[1000];
char aux[1000];
int k = 0;
int i = 0;
int y = 0;
int retiraVogaisRep (char *s){
if (*s == '\0'){
while (k < y){
*(s - i) = final[k];
i--;
k++;
}
while (i >= 0){
*(s - i) = final[1000];
i--;
}
return 0;
}
else{
if ((*s == 'A' || *s == 'E' || *s == 'I' || *s == 'O' || *s == 'U' || *s == 'a' || *s == 'e' || *s == 'i' || *s == 'o' || *s == 'u') && aux[i] != *s){
i++;
aux[i] = *s;
final[y] = *s;
y++;
return retiraVogaisRep(s + 1);
}
else if ((*s == 'A' || *s == 'E' || *s == 'I' || *s == 'O' || *s == 'U' || *s == 'a' || *s == 'e' || *s == 'i' || *s == 'o' || *s == 'u') && aux[i] == *s){
i++;
aux[i] = *s;
return 1 + retiraVogaisRep(s + 1);
}
else{
final[y] = *s;
y++;
i++;
return retiraVogaisRep(s + 1);
}
}
}
This is a possible solution:
int removeVowels(char *s)
{
char *src = s;
char *dst = s;
while (*dst != '\0')
{
switch (* ++ src)
{
case 'A': case 'a':
case 'E': case 'e':
case 'I': case 'i':
case 'O': case 'o':
case 'U': case 'u':
if (* src != * dst)
* ++ dst = * src;
break;
default:
* ++ dst = * src;
break;
}
}
return src - dst;
}
First, the first character of the input string is the first character of output -- even if it's a vowel, it certainly is not a duplicate yet!
Then we enter a loop. The loop will work until the resulting string is terminated -- so while the character pointed at by the dst pointer is not zero, do iterate.
In every iteration we advance the src pointer and fetch a character pointed at. If it is a vowel we test if it's same as the most recently accepted output character (pointed at by dst); if so, we skip it, otherwise we append it to the result (by advancing the dst pointer and copying the character.
OTOH if the character is not a vowel, we copy it with no more conditions.
Once src pointer reaches the end of the string, the zero byte (a string terminator) is copied, and the loop exits.
At this moment, src points at the end of the source string and dst points at the end of the resulting string (which starts at the same point where the source string was starting, but it can be shorter now than the source).
So the difference of pointers src - dst is exactly the number of characters (vowels) skipped.
Try it here: https://onlinegdb.com/ryNzvTUVu
int isVowel(char ch)
{
if (ch >= 'A' && ch <= 'Z')
ch = (ch - 'A') + 'a';
return (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u');
}
int removeVowels(char *s)
{
int removed = 0;
if (s)
{
while (*s != '\0')
{
char ch = *s++;
if (isVowel(ch) && (*s == ch))
{
char *src = s, *dst = s;
do {
++src;
++removed;
}
while (*src == ch);
while (*src != '\0') {
*dst++ = *src++;
}
*dst = '\0';
}
}
}
return removed;
}
Demo
The other answers (two at the time of posting this) both give excellent (and efficient) solutions. Here, I offer another – less efficient but, hopefully, simpler and clearer.
First, let's define a function to test if a character is a vowel. Whenever you end up doing something more than once in a program, or if (even though you only do it once) you get long, 'ugly' lines or blocks of code, then you should consider moving that code into a function. You never know when you might need it again and, if you do, then it's already a done deed.
int isVowel(char c)
{
static char* vowels = "AEIOUaeiou"; // Making this "static" cause initialization only once
// static char* vowels = "AEIOUWYaeiouwy"; // Alternative for Welsh people!
for (char* cp = vowels; *cp; ++cp) {
if (c == *cp) return 1; // Matched a vowel, we can return without further loops
}
return 0; // Loop finished without matching, so it's not a vowel
}
Next, the function to remove duplicate vowels. This version just runs a simple loop through the given string, copying the 'source' to the 'destination' if the removal condition isn't met. We can safely do this by overwriting the string itself, as we will never need to 'go back' and look at characters in that original string more than once.
Note, also, that we will be overwriting characters with themselves, until we have found at least one character to skip – but this is a case of sacrificing a modicum of efficiency to maintain code simplicity. There are other places where adding some complexity to the code would also make it more efficient … I'll leave these as the proverbial "exercise for the reader!"
int removeVowels(char* string)
{
int skipped = 0; // Keep a running tally of how many we removed
char *src = string, *dst = string; // Use 2 pointers (source and destination): both start at beginning of string
char previous = '?'; // Keep track of last letter - start with an 'impossible' value
while (*src) // Loop until we find the nul terminator
{
if (!isVowel(*src) || *src != previous) { // Not a vowel or not same as last ...
*dst++ = *src; // ... so copy it to 'new' string and increment destination pointer
}
else { // Otherwise, all we do is incrememnt our removals count.
++skipped;
}
previous = *src++; // Change our saved last letter, then increment the source pointer
}
*dst = '\0'; // Finally, add the nul-terminator to our modified string
return skipped;
}
Finally, a short main program to test the above code with the example input provided:
#include <stdio.h>
int main()
{
char test[] = "Estaa e umaa string coom duuuplicadoos";
int lost = removeVowels(test);
printf("Result: %s (removed %d vowels)\n", test, lost);
return 0;
}

Splitting an array without an inbuilt function

The code below is meant to take an input string like My name is Smith and outputs
My
name
is
Smith
and also has to exclude things like: , . and space, just these three, but instead it outputs, I'm not allowed to use any thing like strlen or strtok
My
y
name
ame
me
e
is
s
Smith
mith
ith
th
h
I've searched for the error everywhere in the code and I can't seem to figure it out
int main()
{
int wordSize = 0;
char str[81];
char* ptr_to_word[81];
gets_s(str);
for (char* res_p = &(str[0]); *res_p != '\0'; res_p++) {
if ((*res_p != '.') || (*res_p != ',') || (*res_p != ' '))
{
ptr_to_word[wordSize] = res_p;
wordSize++;
}
}
if (wordSize == 0)
{
printf("no solution");
}
else
{
for (int i = 0; i < wordSize; i)
{
char* a = ptr_to_word[i];
while ((*a != '.') && (*a != ',') && (*a != ' ') && (*a != '\0'))
{
printf("%c", *a);
a++;
}
printf("\n");
}
}
return 0;
}
In the condition of the if statement within this loop
for (char* res_p = &(str[0]); *res_p != '\0'; res_p++) {
if ((*res_p != '.') || (*res_p != ',') || (*res_p != ' '))
{
ptr_to_word[wordSize] = res_p;
wordSize++;
}
}
you have to use the logical AND operator instead of the logical OR operator like
if ((*res_p != '.') && (*res_p != ',') && (*res_p != ' '))
Nevertheless the loop in any case is wrong because the variable wordSize does not count words but counts each character that is not equal to the characters listed in the if statement.
To output separate words there is no need to declare an array of pointers to starts of words. You can output each word as soon as its start is found.
Here is a very simple demonstrative program.
#include <stdio.h>
int main(void)
{
enum { N = 81 };
char s[N];
fgets( s, N, stdin );
for ( const char *p = s; *p; )
{
int wordSize = 0;
while ( *p && *p != '.' && *p != ',' && *p != ' ' && *p != '\t' && *p != '\n' )
{
++wordSize;
++p;
}
if ( wordSize )
{
printf( "%.*s\n", wordSize, p - wordSize );
}
else
{
++p;
}
}
return 0;
}
If to input "My name is Smith" then the program output might look like
My
name
is
Smith
I appended the list of delimiters with the tab character '\t' and the new line character '\n' that can be inserted in the input string by the function fgets.
First of all, this condition
if ((*res_p != '.') || (*res_p != ',') || (*res_p != ' ')
will always be true, think about it, *res_p is always not '.' OR not ','. However, if you change the operator to && your code will still produce the same result.
The first loop with || operator writes a pointer to every character in your input string to the array ptr_to_word, and ' 's and '.'s are not printed only because of a condition in the second loop.
If you change the operator to && like so,
if ((*res_p != '.') && (*res_p != ',') && (*res_p != ' ')
you will write all the pointers, except for the '.', ',' and ' ', into the array, e.g. ptr_to_word[0] will point to 'My name is Smith', ptr_to_word[1] to 'y name is Smith', etc.
I have made a few changes to your code so that it would work as intended.
void split_str(char *str)
{
int wordSize = 0;
char *ptr_to_word[81];
char *res_p = &(str[0]);
int i = 0;
for (; *res_p != '\0'; res_p++) {
if ((*res_p != '.') && (*res_p != ',') && (*res_p != ' '))
{
wordSize++;
}
else
{
ptr_to_word[i] = res_p - wordSize;
wordSize = 0;
i++;
}
}
ptr_to_word[i] = res_p - wordSize;
ptr_to_word[i + 1] = NULL;
if (i == 0)
{
printf("no solution\n");
}
else
{
for (int i = 0; ptr_to_word[i]; i++)
{
char* a = ptr_to_word[i];
while ((*a != '.') && (*a != ',') && (*a != ' ') && (*a != '\0'))
{
printf("%c", *a);
a++;
}
printf("\n");
}
}
}
int main() {
split_str("My name is Smith\n");
}
This way you are writing a new value to the pointer array only when dot, comma or a space is encountered. For all other chars, you just do wordSize++ to keep track of the length of the word. I used pointer arithmetic to move back to the start of the word (ptr_to_word[i] = res_p - wordSize;).
I have tried to make only minimal changes to your code, just enough to make it work. There are still things I would change to make it better, e.g. the first loop finishes when the '\0' is encountered. Most likely, the last word finishes with a '\0', not a '.' or a ' '. Thus, it won't be written to the ptr_to_word, and I had to manually write it after the loop. Also, you don't even need two loops, you can use the same pointer. arithmetic to output the word as soon as it is found. So I'd advise you to do. a self-code-review, and optimize it further.
Good luck!

Test if the input is integer or symbol in fgets in C

I am constructing a program that takes string input from the keyboard then shows the number of consonants as an output. I have managed to do it in a ridiculous way in the function count_consonants. I tested using if statement whether each character in the input is a number or symbol to ignore them during calculations. I originally wanted to check if the string is not a string using fgets but I don't know how. That's not an effective way, so any ideas for this?
#include <stdio.h>
#include <string.h>
//function to calculate the consonants
int count_consonants(char str[]) {
int idx;
for (idx = 0; idx < 100; ++idx) {
if (str[idx] == '\0') {
break;
}
}
int vowl = 0;
for (int i = 0; i < idx; ++i) { //loop to check if the characters are vowels or not
if (str[i] == 'a' || str[i] == 'e' || str[i] == 'i' || str[i] == 'o'
|| str[i] == 'u' || str[i] == 'A' || str[i] == 'E' || str[i] == 'I'
|| str[i] == 'O' || str[i] == 'U' || str[i] == ' ') {
vowl += 1;
}
// numbers and symbols are counted here as vowels because if not,
// the compiler will count them the other way around
if (str[i] == '1' || str[i] == '2' || str[i] == '3' || str[i] == '4'
|| str[i] == '5' || str[i] == '6' || str[i] == '7' || str[i] == '8'
|| str[i] == '9') {
vowl += 1;
}
if (str[i] == ':' || str[i] == ',' || str[i] == '.' || str[i] == '$'
|| str[i] == '%' || str[i] == '^' || str[i] == '&' || str[i] == '*'
|| str[i] == '#' || str[i] == '_' || str[i] == '!') {
vowl += 1;
}
}
int cons = idx - vowl; // consonants = whole length of text - vowels
return cons - 1;
}
int main(int argc, char const *argv[]) {
char string[100];
char store[100][100];
int i = 0;
while (string[0] != '\n') {
fgets(string, 100, stdin);
strcpy(store[i], string);
i++;
}
for (int j = 0; j < i - 1; ++j) {
/* code */
printf("Number of consonants=%d\n", count_consonants(store[j]));
}
return 0;
}
shows the number of consonants
A simply way to count consonants, use isalpha(), strchr()
#include <string.h>
#include <ctype.h>
int my_isavowel(char ch) {
const char *p = strchr("aeiouAEIOU", ch); // search for a match
return p && *p; // If p is not NULL, and does not point to \0
}
int count_consonants(const char str[]) {
int count = 0;
while (*str != '\0') { // while not at end of string ...
char ch = *str++; // Get character and advance
count += isalpha((unsigned char) ch) && !my_isvowel(ch);
}
return count;
}
If you look for number of consonants, simply best count consonants instead of other things
#include <stdio.h>
#include <string.h>
int main (int narg,char*args[]){
char cons[ ] = "ZRTPQSDFGHJKLMWXCVBN";
char sentence[ ] = "This is my sentence!";
int i=0;
int sum_cons = 0;
for (i = 0; i < strlen(sentence); ++i)
if (strchr(cons,strupr(sentence)[i])) sum_cons++;
printf ("#CONS>%i\n",sum_cons);
return 0;
}

Resources