使用日志函数(没有 math.h)和数组计算 C 中的字母表

Count alphabets in C using log functions(without math.h) and arrays

我的一个项目遇到了一个小问题。我应该编写一个 c 程序来计算 input/file 中存在的每个字符。 (它应该是一个基本程序。)约束 - 我不能使用 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]);
    }

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

任何 help/feedback 不胜感激。

计算您需要多少位数字的一种简单方法是使用 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);
}

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

一些伪代码

  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. 打印space
    2. 打印\n

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

  • 每个字符都有一个字母索引,大小写字母是 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