Sort characters by popularity in C

I wrote code to list the occurrences of characters in a string. It works, but I was wondering if it can be put in order? For example, descent. It sounds simple, but I use two arrays here, and I don’t know how I could connect them so that after sorting they would not get mixed up. I read that in C ++ I could use std :: pair, but from what I found there is no replacement for C.

Is there a simple way to just sort it so that I have a list of the most / least popular characters? I started to enter the bubble, but whatever I choose, there will still be no connection between the two arrays.

Here is the code:

#include <stdio.h> #include <string.h> #include <stdlib.h> main(int argc, char **argv) { int hits[26] = { 0 }; char letters[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; char *line; int i; printf("Write the line:\n"); scanf("%25[^\n]", line); for (i = 0; i < strlen(line); i++) { if (!isalpha(line[i])) continue; hits[(int)(tolower(line[i]) - 'a')]++; } for (i = 0; i < 26; i++) { printf("%c is showing up %d times.\n", letters[i], hits[i]); } return 0; } 

Perhaps there is a simple mistake that I make in my approach, if so, then I would be glad if that were indicated.

+5
source share
4 answers

First make the corrections suggested in @artm's answer.

But regarding your sorting question. The C standard library has one (or more, depending on the version) routines for sorting an array of any type. The most common is qsort(3) . The rubbing is that you have to give the subroutine a function that it can use to compare the elements of the array. Thus, you need a way to store both the character and its appearance together, and you need to write this comparison function.

I would suggest taking the @jack comment and using an array of structures. Each structure will look something like this:

 struct letter { char character; int count; }; 

to group both the character and its number of occurrences. Then select an array of them, one for each letter (and not just int[] , as you have now).

 struct letter letters[26]; for (int i = 0; i < 26; i++) { letters[i] = (struct letter){ (char)(i + 'a'), 0 }; } 

In your loop looking at the line received from the user, update the letter.count field for the corresponding letter each time it is visible.

Then you can use the standard qsort(3) library to sort the array of letters according to the counts inside them. To use this function, you need a comparison function that tells the library the ordering of two elements (more or less).

The comparison function must have a signature: int (*compar)(const void *, const void *) . The void* element points to the individual elements in your letters array, so you need to assign them to a struct letter and compare them in them. It might look something like this (untested):

 int letter_cmp(const void* first, const void* second) { struct letter* first_letter = (struct letter *) first; struct letter* second_letter = (struct letter*) second; if (first_letter->count == second_letter->count) { return 0; } else if (first_letter->count < second_letter->count) { return -1; } return 1; } 

Then you call the qort(3) function as follows.

 qsort(&letters[0], sizeof(letters), sizeof(struct letter), &letter_cmp); 

The first argument is the beginning of the array. The second is the size of the array in elements. The third size of each element, and the fourth is a comparison function.

After this call, the letters array was sorted in ascending order of count . So letters[0].character gives you the character with the smallest count , and letters[25].character gives you the one with the highest (most cases).

+3
source

You used the line pointer without initializing it - UB.

You should dynamically allocate for it or just use a static array with some appropriate support - for example, 1000 characters (including "\ 0") would be enough.

 char line[1000] = ""; 

You do not need to limit the input string to 25 characters as follows:

 scanf("%25[^\n]", line); 

Instead, using fgets will be much better:

 fgets(line, sizeof(line), stdin); 

Btw, you really don't need to apply here:

 hits[tolower(line[i]) - 'a')]++; 

Then your solution should work.

+3
source

A pointer to a structure can be used and highlighted as needed. A pointer can be used similarly to an array, but can be expanded with realloc . This would allow uppercase and lowercase letters or any char range. This gets characters from stdin to EOF or newlines, so the number of characters you enter is limited.

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> struct charcount { char ch; int count; }; void showeach ( struct charcount *ary, int size) { int index = 0; int ch = 0; for ( index = 0; index < size; index++) { if ( index % 3 == 0) { printf ( "\n"); } if ( index && index % 48 == 0) { printf ( "\npress enter\n"); while ( ( ch = fgetc ( stdin)) != '\n') { if ( ch == EOF) { fprintf ( stderr, "problem getting input. EOF\n"); } } } //show the characters and the count printf ( " %4c", ary[index].ch); printf ( "%6d " , ary[index].count); } printf ( "\n"); } int main(void) { size_t index = 0; size_t elements = 0; size_t loop = 0; int ch = 0; int found = 0; struct charcount *chno = NULL;//so realloc will work on first call struct charcount *temp = NULL; struct charcount swap; //get each character until newline or EOF while ( ( ( ch = fgetc ( stdin)) != EOF) && ch != '\n') { if ( ch && isalpha ( ch)) {//restict to letters ch = tolower ( ch);//restrict to lower case found = 0; for ( loop = 0; loop < elements; loop++) { if ( ch == chno[loop].ch) { chno[loop].count++; found = 1; break; } } if ( !found) {//new element. reallocate if ( ( temp = realloc ( chno, sizeof ( *chno) * (elements + 1))) == NULL) { fprintf ( stderr, "problem allocating\n"); free ( chno); return 1; } chno = temp; chno[elements].ch = ch; chno[elements].count = 1; //sort by letter for ( loop = elements; loop > 0; loop--) { if ( chno[loop].ch < chno[loop - 1].ch) { swap = chno[loop]; chno[loop] = chno[loop - 1]; chno[loop - 1] = swap; continue; } break; } elements++; } } } showeach ( chno, elements); //sort by count for ( index = 1; index < elements; index++) { for ( loop = index; loop > 0; loop--) { if ( chno[loop].count > chno[loop - 1].count) { swap = chno[loop]; chno[loop] = chno[loop - 1]; chno[loop - 1] = swap; } else { break; } } } showeach ( chno, elements); if ( chno) { free ( chno);//free allocated memory } return 0; } 
+1
source

Your program crashes because you use line to read bytes with scanf() , but this is an uninitialized pointer, so storing bytes in it causes undefined behavior.

  • You can fix this problem by specifying line as an array: char line[80];
  • Alternatively, you can change the code to read an arbitrarily long line, one byte at a time with getchar() .

To print statistics in descending order, you can use an array of structures and sort it, or just iteratively print the largest number and reset it:

 #include <stdio.h> #include <stdlib.h> main(int argc, char **argv) { int hits['z' - 'a' + 1] = { 0 }; int c, i, max; printf("Write the line:\n"); while ((c = getchar()) != EOF && c != '\n') { if (isalpha(c)) { hits[tolower(c) - 'a'] += 1; } } for (;;) { for (i = max = 0; i < 'z' - 'a' + 1; i++) { if (hits[i] > hits[max]) max = i; } if (hits[max] == 0) break; printf("%c is showing up %d times.\n", 'a' + max, hits[max]); hits[max] = 0; } return 0; } 
+1
source

Source: https://habr.com/ru/post/1263126/


All Articles