我编写了一个代码,用于列出字符串中字符的出现次数。它有效,但我想知道,是否有可能把它整理好?例如下降。听起来很简单,但我在这里使用了两个数组,我不知道怎么可能将它们链接起来,这样它们就不会在排序后搞砸了。我读到在C ++中我可以使用std :: pair但是从我发现的内容来看,在C中没有替代它。
有没有一种简单的方法可以对它进行排序,以便我可以列出大多数/最不受欢迎的字符?我开始进入冒泡排序,但无论我选择什么,两个阵列之间仍然没有链接。
以下是代码:
#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;
}
也许在我的方法中有一个简单的错误,如果是的话,那么我很乐意指出它。
答案 0 :(得分:3)
你使用指针line
而没有初始化它 - UB。
你应该为它动态分配,或者只是使用带有适当配置的静态数组 - 例如,1000个字符(包括'\ 0')就足够了。
char line[1000] = "";
你真的不需要将输入行限制为25个字符,如下所示:
scanf("%25[^\n]", line);
相反,使用fgets
会更好:
fgets(line, sizeof(line), stdin);
顺便说一句,你真的不需要演员:
hits[tolower(line[i]) - 'a')]++;
然后你的解决方案应该有效。
答案 1 :(得分:3)
首先,在@ artm的回答中提出修正建议。
但是关于你的排序问题。 C标准库具有一个(或多个,取决于版本)例程,用于对任何类型的数组进行排序。最常见的是qsort(3)
。问题是,您必须为例程提供一个可用于比较数组元素的函数。因此,您需要一种将字符及其出现一起存储的方法,并且您需要编写该比较函数。
我建议接受@ jack的评论并使用一系列结构。每个结构都类似于:
struct letter {
char character;
int count;
};
将字符及其出现次数分组。然后分配一个这样的数组,每个字母一个(而不是现在只有int[]
)。
struct letter letters[26];
for (int i = 0; i < 26; i++) {
letters[i] = (struct letter){ (char)(i + 'a'), 0 };
}
在搜索从用户检索到的行的循环中,每次看到相应字母时都会更新letter.count
字段。
然后,您可以使用标准库qsort(3)
函数根据其中的计数对字母数组进行排序。要使用该函数,您需要一个比较函数,它告诉库两个元素的排序(更大或更小)。
比较函数必须具有签名:int (*compar)(const void *, const void *)
。 void*
指向letters
数组中的各个元素,因此您需要将它们转换为struct letter
并比较其中的计数。它可能看起来像这样(未经测试):
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;
}
然后按照以下方式调用qort(3)
函数。
qsort(&letters[0], sizeof(letters), sizeof(struct letter), &letter_cmp);
第一个参数是数组的开头。第二个是元素中数组的大小。第三个是每个元素的大小,第四个是你的比较函数。
此调用返回后,letters
数组已按增加count
的顺序排序。因此,letters[0].character
会为您提供最低count
的字符,而letters[25].character
会为您提供最高(最多次出现)的字符。
答案 2 :(得分:1)
可以根据需要使用和分配指向结构的指针。指针可以类似于数组使用,但可以使用realloc
进行扩展。这将允许包含大写和小写字母或任何char
所需的范围
这会从stdin中获取字符,直到EOF或换行符,因此对输入的字符数没有太大的限制。
#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;
}
答案 3 :(得分:1)
您的程序崩溃是因为您使用line
来读取scanf()
的字节,但它是一个未初始化的指针,因此将字节存储到它会调用未定义的行为。
line
定义为数组来解决此问题:char line[80];
getchar()
。要按递减顺序打印统计数据,您可以使用结构数组并对其进行排序,或者只是迭代地打印最大数字并重置它:
#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;
}