使用日志函数(没有 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);
}
我没有测试代码,但应该很接近。
一些伪代码
- 查找最大计数
- 打印时查找该计数的宽度
w=sprintf(buf, "%d", mxcnt)
- 循环
w
次(wi = 0 到 w - 1)
- 对于每个非零计数
- 表单字符串
sprintf(buf, "%*d", w, count[i])
- 打印
buf[wi]
字符
- 打印space
- 打印\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
我的一个项目遇到了一个小问题。我应该编写一个 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);
}
我没有测试代码,但应该很接近。
一些伪代码
- 查找最大计数
- 打印时查找该计数的宽度
w=sprintf(buf, "%d", mxcnt)
- 循环
w
次(wi = 0 到 w - 1)- 对于每个非零计数
- 表单字符串
sprintf(buf, "%*d", w, count[i])
- 打印
buf[wi]
字符 - 打印space
- 表单字符串
- 打印\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