Sagi Binder
Sagi Binder

Reputation: 69

Function that counts chars in a string in C

I am new a C. I would like to get help to finish my function.

The mission is:

Write a function that accepts a string maximum length of 256 characters containing characters from 'a' to 'z'.

The function to print the number of occurrences of each character.

For example: input abba output will be:

a = 2 b = 2 c = 0 d = 0 .... z = 0

Do not use if during any function.

I would like to get your help to finish this program please.

This is my code

#include "stdlib.h"
#include "conio.h"
#include "stdio.h"
#include "string.h"
#define size 256



void repeat(char *str);
void main()
{
    char str[size];
    printf("Please enter a string:\n");
    flushall;
    gets(str);
    repeat(str);
    system("pause");
    return ;
}
void repeat(char *str)
{

    char temp=strlen(str);
    int i, count=0;
    do
    {
    for (i=0; i<temp ; i++)
        {
            count += (*str == str[temp-i]);
        }
    printf("Char %c appears %d times\n ",*str,count);
    count=0;
    }
    while(*(str++));
}



    Please enter a string:
abbba
Char a appears 1 times
 Char b appears 2 times
 Char b appears 1 times
 Char b appears 0 times
 Char a appears 0 times
 Char   appears 0 times
 Press any key to continue . . .

this is the output! I would like to do it in the same building i did. and should be like Char a appears 2 times Chars b appears 3 times

Upvotes: 0

Views: 13546

Answers (5)

pablo1977
pablo1977

Reputation: 4433

EDIT I have edited the program to follow the requirements of @SagiBinder.

(In my old version, I used an if sentence that checked if the character is in the set 'a'...'z').

The type of temp must be "bigger", that is, something different to char.
Try int, instead.

The algorithm would be this (some details of your program are not repeated here):

int temp = strlen(str);

int i, j;

unsigned char c; 

int ch[UCHAR_MAX]; // The macro CHAR_MAX needs the header <limits.h>
for (i = 1; i <= UCHAR_MAX; i++)
    ch[i] = 0;

for (j=0; j<temp ; j++) {
      c = (unsigned char)(str[j]);
      ch[c]++;
}

for (c = 'a'; c <= 'z'; c++)
    printf("%c == %d\n", c, ch[c]);

The variable temp holds the length of the string str.
The macro UCHAR_MAX (existing in the header <limits.h>, that you have to #include at the beginning of the program). It is the max. value that holds in a unsigned char.
The array ch[] contains a component for each possible value in the range of the type unsigned char. The intent is that, for some character c, the element ch[c] is the amount of times that c is in str.
I have used unsigned char in order to ensures that the index c of the array ch[] when writting ch[c] is a non-negative integer value, because an array cannot have negative indexes.
The 2nd for goes through the string str. In the step number j, the j-th character of the string str is taken.
This character is a value of type char.
Since one cannot be sure that char have not negative values, I have converted it to (unsigned char) with an explicit cast.
This value is held in the variable c.

The value of c has the (unsigned char version of the) j-th character in str, so we are going to count it.
How?
Well, we access the array of counters: ch[] with index c, and increment its value in 1:

      ch[c]++;

After the for is finished, we have in the array ch[] the information we want.

Finally, we check for the characters from 'a' to 'z'.
(For this, we have supposed that the character encodings in our system follow the convention that the letters have contiguous values).

The 3rd for goes from 'a' to 'z', and the values of the letter (the variable c that controls the for) and the counting of this letter, that is, ch[c].

Moreover: to show the count of any character, you need a re-cast to char, in this way:

    printf("%c: %d\n", (char)c, ch[c]);

But this is not necessary with the letters 'a' to 'z', because they belong to the basic execution character set which means that their values are non-negative and equal to their unsigned char counterparts. So, in this case, it is enough to write:

    printf("%c: %d\n", c, ch[c]);

EDIT 2: I will use the idea in the answer of @jxh to improve my code.

Since it cannot be guaranted that the encodings of letters 'a' to 'z' are in contiguous order, we can use a string holding the letters:

   char letters[] = "abcdefghijklmnopqrstuvwxyz";

The "last" element is, by C convention, a \0 character held after the element 'z'.

   Now, we can show the letter counting by changing the 3rd `for` in this way:  

 for (i = 0; letter[i] != '\0'; i++)
     printf("%c == %d\n", letter[i], ch[letter[i]]);

This is equivalent to write:

 for (i = 0; letter[i] != '\0'; i++) {
     c = letter[i];
     printf("%c == %d\n", c, ch[c]);
 }

Upvotes: 0

surender8388
surender8388

Reputation: 474

Optimized solution. complexity O(N), N - Input String length.

your void repeat function will be like this,

void repeat(char *str)
{

    int temp=strlen(str);// use int here
    int i, count=0;
int charCount[26] = {0};


#if 0
//your logic, traverses the string (n*n) time, n - input string length.
    do
    {
        for (i=0; i<temp ; i++)
        {
            count += (*str == str[temp-i]);
        }
        printf("Char %c appears %d times\n ",*str,count);
        count=0;
    }
    while(*(str++));
#endif

#if 1
// This logic traverses string once only. n time, n - input string length.
for (i=0; i<temp ; i++)
    {
        charCount[str[i]%'a']++;
    }

for (i=0; i<26 ; i++)
    {
        printf("%c appears :  %d times \n", 'a'+i, charCount[i]);
    }
#endif
}

[EDIT] Here

charCount[str[i]%'a']++; // 'a' is used a its ASCII Value.

You can use it as

charCount[str[i]%97]++;
  1. If you wan to count lower case letter and upper case letter both.

use it like this

if(str[i] >= 'a' && str[i] <= 'z'){
        iMap = str[i]%97; // 97 is ASCII Value of 'a'
        charCount[iMap]++;
    }else if(str[i] >= 'A' && str[i] <= 'Z'){
        iMap = str[i]%65; // 65 is ASCII Value of 'A'
        charCount[iMap]++;
    }
//iMpa is a integer (int iMap;), used for better undersanding.

Upvotes: 0

jxh
jxh

Reputation: 70402

You make a stipulation about not using if. This satisfies that restriction.

#include <stdio.h>

int main(void) {
    int i, c;
    int counts[256] = { 0 };
    const char lower[] = "abcdefghijklmnopqrstuvwxyz";
    while ((c = getchar()) != EOF) {
        counts[c] += 1;
    }
    for (i = 0; lower[i]; ++i) {
        c = lower[i];
        printf("Char %c appears %d times.\n", c, counts[c]);
    }
    return 0;
}

The problem with your attempt is that you do not track any state to remember which characters you have already printed information about. It also fails to include the character under consideration as part of the count. It also makes multiple passes over the string to collect count information about each character, but that doesn't affect correctness, just performance. If you can somehow remember which character you have already printed out information for, so that you don't do it again when the same character appears later in the string, your method should print out the counts for the characters that appear. Afterwards, you would need to print out zero counts for the characters that did not appear at all. If the outputs need to be in alphabetical order, then you need to make sure you take care of that as well.

One way to track the information properly and to allow your output to be printed in alphabetical order is to maintain counts for each character in an array. After making a pass over the string and incrementing the count associated with each found character, you can iterate over the count array, and print out the counts.


The following program is for zubergu:

#include <stdio.h>
#include <string.h>

int main (void) {
    int i, c;
    int counts[26] = { 0 };
    const char lower[] = "abcdefghijklmnopqrstuvwxyz";
    while ((c = getchar()) != EOF) {
        switch (c) {
        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
        case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
        case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
        case 'v': case 'w': case 'x': case 'y': case 'z':
            counts[strchr(lower, c) - lower] += 1;
            break;
        default:
            break;
        }
    }
    for (i = 0; lower[i]; ++i) {
        printf("Char %c appears %d times.\n", lower[i], counts[i]);
    }
    return 0;
}

Upvotes: 3

zubergu
zubergu

Reputation: 3706

It might be one of the ugliest solutions, but also the simplest:

while(*str!='\0')
{
  switch(tolower(*str))
  {
    case 'a': a_count++;break;
    case 'b': b_count++;break;
    .
    .
    .
  }
  str++;
}

It checks if str points to valid letter, then turns it to lower, so it's not case sensitive('A' will be same as 'a' character). No 'if' used and will work with every length char array terminated with '\0' char.

Upvotes: 1

Dheeraj Sharma
Dheeraj Sharma

Reputation: 45

 i = 0;
  while (s[i] !=0)
   if (( s[i] >= 'a' && s[i] <= 'z') || (s[i] <= 'A' && s[i] >= 'Z'))
   {
     letters++;
     i++;
   }
   else
     if (( s[i] >= '!' && s[i] <= ')'))
     {
       other++;
     }
   else
     if (( s[i] >= '0' && s[i] <= '9'))
     {
       numbers++;
     }
      total = letters + numbers + other;

Upvotes: -1

Related Questions