【问题标题】:Count alphabets in C using log functions(without math.h) and arrays使用对数函数(没有 math.h)和数组计算 C 中的字母
【发布时间】:2025-12-04 23:00:01
【问题描述】:

我的一个项目遇到了一点小问题。我应该编写一个 c 程序来计算输入/文件中存在的每个字符。 (它应该是一个基本程序。)约束 - 我不能使用 math.h 库来生成日志函数并获得格式的输出:

    1                                           
    5   1 2 0 2 2 5 8     4 3 6 6 2   5 5 7 2 1 1   2   
    7 9 8 1 7 2 4 1 0 0 4 5 0 2 2 5 2 6 3 6 6 3 7 0 2 2 
    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

该程序应该计算标准输入流中每个字母(不区分大小写)的出现次数并显示直方图。

如您所见,输出是垂直格式化的,每行打印字符位置的以 10 为底的数字。 现在,这可能看起来很愚蠢,但到目前为止我所拥有的是:

    #include <stdio.h>
    #include <ctype.h>

    /*
    int logBase10 (int num) {
       method to calculate the log base 10 of num
    }
    */

    int main (int argc, char * argv[]) {
        char alpha;
        int count = 0;
        int ascii[128] = {0};

        while ( (alpha = getchar()) != EOF) {
            count++;
            ascii[(int)alpha]++;
            alpha = getchar();
        }

        printf("Char \t Count \n");
        printf("------------------------\n");

        for (int i = 0; i < 127; i++) {
            if(ascii[i] > 0) {
                printf("%c \t %d \n", i, ascii[i]);
            }
        }
    }

产生这样的输出:

    Char     Count 
    ------------------------

         5 
         93 
    ,    6 
    -    2 
    .    3 
    ;    2 
    C    2 
    I    6 
    N    1 
    T    1 
    W    2 
    a    26 
    b    5 
    c    8 
    d    13 
    e    55 
    f    11 
    g    7 
    h    28 
    i    32 
    k    3 
    l    26 
    m    17 
    n    31 
    o    27 
    p    12 
    q    1 
    r    26 
    s    22 
    t    42 
    u    11 
    v    8 
    w    8 
    y    13 
    z    1 

首先,我的程序正在打印不需要的 ascii 字符(, ; - 等),我正在努力将打印功能更改为更垂直,但我根本无法弄清楚日志方法。我知道 log(10) 是 1,因为 10^1 是 1,但我无法弄清楚如何使用它来创建方法本身。另外,对于额外的字符,我尝试使用:

    if(ascii[i] > 65 || ascii[i] < 90 || ascii[i] >= 97 || ascii[i] <= 122 ) {
        printf("%c \t %d \n", i, ascii[i]);
    }

无济于事。尝试这样做会产生更多乱码。

感谢任何帮助/反馈。

  • 灵魂

【问题讨论】:

  • 注意:不要使用char alpha;,而是使用int alpha;来区分fgetc()的返回值:典型的256unsigned charEOF
  • 至于您对信件的检查:a &gt; 65 || a &lt; 90 将适用于任何 a。建议使用字符文字 'A' 而不是硬编码的 ASCII 值 65。您可能还想看看来自&lt;ctype.h&gt;isalpha
  • int ascii[128]; ... ascii[(int)alpha]alpha &lt; 0 || alpha &gt; 127 时很危险。 int alpha; int ascii[UCHAR_MAX+1]; ... ascii[alpha] 更好。
  • 我认为您需要 log 函数来确定显示的高度,即最大高度。位数。您实际上并不需要为此记录:将最高计数反复除以 10,并跟踪在达到零之前您可以除以的频率。
  • 恕我直言,这个问题不像在自动取款机上问的那样好。这是太多的上下文。尝试更具体地说明您确切需要什么,并添加有关您尝试过的内容以及为什么您认为它不起作用的信息。跨度>

标签: c arrays count character logarithm


【解决方案1】:

评论者已经指出了您的代码存在的问题。这是一个只计算字母并打印垂直标签的版本。它不需要&lt;ctype.h&gt;&lt;math.h&gt;

  • 每个字符都有一个字母索引,该索引是从 0 到 25 的大小写字母的数字,如果字符不是字母,则为 -1。这会将数组大小减少到 26。

  • 您可以通过精心计算找出每个数字,但最简单的方法是将数字打印到字符串中。 snprintf 为你做这件事。您可以将数字与字段宽度右对齐。典型的int 的最大值约为 20 亿,有 10 位。你应该考虑到这一点,即使你必须通过整个 Moby-Dick 和圣经才能得到这么多的计数。

  • 您可以通过先假设宽度为十位并检查最大计数是否为十位(即是否为 1,000,000,000 或更高)来测试是否应该开始打印。然后在每次迭代中将该限制除以 10。

代码如下:

#include <stdio.h>

// return letter index or -1 for non-letter
int letter(int c)
{
    if ('a' <= c && c <= 'z') return c - 'a';
    if ('A' <= c && c <= 'Z') return c - 'A';
    return -1;
}

int main(int argc, char * argv[])
{
    int count[26] = {0};        // letter counts
    char label[26][12];         // buffer for printing numbers
    int limit = 1000000000;     // smallest 10-digit number
    int max = 0;
    int i, j;

    // read and count letters
    while (1) {
        int c = getchar();

        if (c == EOF) break;

        c = letter(c);
        if (c >= 0) count[c]++;
    }

    // write auxiliary labels
    for (i = 0; i < 26; i++) {
        snprintf(label[i], sizeof(label[i]), "%10d", count[i]);
        if (count[i] > max) max = count[i];
    }

    // print vertical labels
    for (j = 0; j < 10; j++) {
        if (max >= limit) {        
            for (i = 0; i < 26; i++) {
                putchar(' ');
                putchar(label[i][j]);
            }
            putchar('\n');
        }
        limit /= 10;
    }

    // print horizontal rule
    for (i = 0; i < 26; i++) {
        putchar('-');
        putchar('-');
    }
    putchar('-');
    putchar('\n');

    // print letters
    for (i = 0; i < 26; i++) {
        putchar(' ');
        putchar('A' + i);
    }
    putchar('\n');

    return 0;
}

在你的例子中,它产生:

         1                                          
 5   1 2 0 2 2 5 8     4 3 6 6 2   5 5 7 2 1 1   2  
 7 9 8 1 7 2 4 1 0 0 4 5 0 2 2 5 2 6 3 6 6 3 7 0 2 2
-----------------------------------------------------
 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

【讨论】:

  • 非常感谢您,先生,您拯救了这一天。我正在尝试理解您的(辅助标签)代码部分,并希望在随意复制之前理解。 (另外,喜欢《白鲸记》或《圣经》的小(讽刺?))。赞一个。
  • 不涉及讽刺。您的示例文本是 Moby-Dick 的开头段落,Moby-Dick 和 Bilble 都是长书的示例。标签只是打印数字的字符串,左边用空格填充。然后将它们按列打印,而不是通常的按行表示。祝你好运。
【解决方案2】:

计算需要多少位数的一种简单方法是使用 sprintf 将整数计数转换为字符串,然后使用 strlen 找出您有多少位数。例如:

char str[20] = {0}; // 20 digits should be enough for your case

for (i = 0; i < 128; i++) {
    sprintf(str, "%d", ascii[i]);
    num_digits = strlen(str);
    printf("%d has %d digits\n", ascii[i], num_digits);
}

我没有测试代码,但它应该很接近。

【讨论】:

    【解决方案3】:

    一些伪代码

    1. 查找最大计数
    2. 打印时查找该计数的宽度w=sprintf(buf, "%d", mxcnt)
    3. 循环w 次(wi = 0 到 w - 1)
      1. 对于每个非零计数
        1. 表单字符串sprintf(buf, "%*d", w, count[i])
        2. 打印buf[wi]字符
        3. 打印空间
      2. 打印\n

    【讨论】: