我这里有这段代码,可以正确格式化硬编码的句子,并找到某个字母在该字符串中出现的频率:
#include <stdio.h>
#include <string.h>
int main() {
char words[1000][100];
int x = 0, y;
char myString[10000] = "The quick Brown ? Fox ? jumps over the Lazy Dog and the !##! LAZY DOG is still sleeping";
printf("Original Text:n");
printf("%sn", myString);
// Function for uppercase letters to become lowercase and to remove special characters
for (x = 0; x <= strlen(myString); ++x) {
if (myString[x] >= 65 && myString[x] <= 90)
myString[x] = myString[x] + 32;
}
for (x = 0; myString[x] != ' '; ++x) {
while (!(myString[x] >= 'a' && myString[x] <= 'z') &&
!(myString[x] >= 'A' && myString[x] <= 'Z') &&
!(myString[x] >= '0' && myString[x] <= '9') &&
!(myString[x] == ' ') && !(myString[x] == ' ')) {
for (y = x; myString[y] != ' '; ++y) {
myString[y] = myString[y + 1];
}
myString[y] = ' ';
}
}
printf("nModified Text: n%sn", myString);
// Part A
int counts[26] = { 0 };
int k;
size_t myString_length = strlen(myString);
for (k = 0; k < myString_length; k++) {
char c = myString[k];
if (!isalpha(c))
continue;
counts[(int)(c - 'a')]++;
}
printf("nLettertCountn------ -----n");
for (k = 0; k < 26; ++k) {
printf("%ct%dn", k + 'a', counts[k]);
}
// Part B
int i = 0, count = 0, occurrences[10000] = { 0 };
while (myString[i] != ' ') {
char wordArray[100];
int j = 0;
while (myString[i] != ' ' && myString[i] != ' ') {
wordArray[j++] = myString[i++];
}
if (wordArray[j - 1] == ',' || wordArray[j - 1] == '.') {
wordArray[j - 1] = ' ';
}
wordArray[j] = ' ';
int status = -1;
for (j = 0; j < count; ++j) {
if (strcmp(words[j], wordArray) == 0) {
status = j;
break;
}
}
if (status != -1) {
occurrences[status] += 1;
} else {
occurrences[count] += 1;
strcpy(words[count++], wordArray);
}
++i;
}
printf("nWord LengthtOccurrencesn----------- -----------n");
for (i = 0; i < count; ++i) {
// print each word and its occurrences
printf("%stt%dn", words[i], occurrences[i]);
}
}
B部分是我遇到问题的地方,我希望代码能够告诉我出现特定长度的单词,例如此实例:
Word length Occurrences
1 0
2 1
在这里,没有一个单词有一个字符的实例,但有一个单词有两个字符的实例。但是,我的代码输出的是给定特定单词的次数,而不是我上面想要的,如下所示:
Word Length Occurrences
----------- -----------
the 3
quick 1
brown 1
3
fox 1
jumps 1
over 1
lazy 2
dog 2
and 1
is 1
still 1
sleeping 1
我将如何更改它,以便它仅显示字长和频率的输出?
以下是有关代码的一些备注:
-
第一个循环重新计算每次迭代的字符串长度:
for (x = 0; x <= strlen(myString); ++x)
。由于您在循环中修改字符串,因此编译器很难确定字符串长度不会更改,因此经典优化可能不起作用。 使用与下一个循环相同的测试:for (x = 0; myString[x] != ' '; ++x)
-
大写的测试不是很好读,因为您对字母
A
和Z
的 ASCII 值进行硬编码,您应该编写:if (myString[x] >= 'A' && myString[x] <= 'Z') myString[x] += 'a' - 'A';
或使用
<ctype.h>
中的宏:unsigned char c = myString[x]; if (isupper(c)) myString[x] = tolower(c);
或同等且可能更有效:
myString[x] = tolower((unsigned char)myString[x]);
-
在第二个循环中,删除既不是字母、数字也不是空格的字符。 你有一个冗余的嵌套
while
循环和第三个嵌套循环,用于移动每个删除的字节的数组的其余部分:这种方法具有三次时间复杂度,O(N3),效率非常低。您应该改用线性时间操作的双指方法:for (x = y = 0; myString[x] != ' '; ++x) { unsigned char c = myString[x]; if (!isalnum(c) && c != ' ') { myString[y++] = c; } } myString[y] = ' ';
-
请注意,此循环会删除所有标点符号,而不是将其替换为空格:这可能会将单词粘合在一起,例如
"a fine,good man"
->"a finegood man"
-
在第三个循环中,您使用
char
值c
作为isalpha(c)
的参数。应包含使用此头文件中声明的任何函数的<ctype.h>
。<ctype.h>
中的函数和宏仅针对unsigned char
类型的所有值和特殊负值EOF
定义。如果类型char
在您的平台上签名,则如果字符串具有负字符,isalpha(c)
将具有未定义的行为。 在您的特定情况下,您过滤了不是 ASCII 字母、数字或空格的字符,因此这应该不是问题,但始终使用unsigned char
for character 参数来isalpha()
和等效函数是一个好习惯。 -
另请注意,此计数阶段可以合并到前面的循环中。
-
要计算单词的出现次数,数组
occurrences
应具有与words
数组相同的元素数 1000。您不检查边界,因此如果有超过 1000 个不同的单词和/或这些单词中的任何一个包含 100 个字符或更多,则具有未定义的行为。 -
在下一个循环中,从字符串中提取单词,在嵌套循环主体内递增
i
。您还会在外部循环的末尾递增i
,从而跳过最后一个 null 终止符。测试while (myString[i] != ' ')
将测试超出字符串末尾的字节,这是不正确的,并且可能是未定义的行为。 -
为避免在此循环中计算空单词,如果不在字符串末尾,则应在复制单词之前跳过空格序列。
-
根据问题,计算单个单词不是B部分应该做的事情,而是应该计算单词长度的频率。您可以在第一个循环中执行此操作,方法是跟踪当前单词的长度,并在找到分隔符时增加单词长度频率数组。
-
请注意,不需要修改字符串来计算字母频率或单词长度出现次数。
-
建议为每个任务编写一个单独的函数。
这是一个修改版本:
#include <ctype.h>
#include <stdio.h>
#define MAX_LENGTH 100
// Function to lowercase letters and remove special characters
void clean_string(char *str) {
int x, y;
printf("Original Text:n");
printf("%sn", str);
for (x = y = 0; str[x] != ' '; x++) {
unsigned char c = str[x];
c = tolower(c);
if (isalnum(c) || c == ' ') {
str[y++] = c;
}
}
str[y] = ' ';
printf("nModified Text:n%sn", str);
}
// Part A: count letter frequencies
void count_letters(const char *str) {
int letter_count['z' - 'a' + 1] = { 0 };
for (int i = 0; str[i] != ' '; i++) {
unsigned char c = str[i];
if (c >= 'a' && c <= 'z') {
letter_count[c - 'a'] += 1;
} else
if (c >= 'A' && c <= 'Z') {
letter_count[c - 'A'] += 1;
}
}
printf("nLettertCount"
"n------t-----n");
for (int c = 'a'; c <= 'z'; c++) {
printf("%ct%dn", c, letter_count[c - 'a']);
}
}
// Part B: count word lengths frequencies
void count_word_lengths(const char *str) {
int length_count[MAX_LENGTH + 1] = { 0 };
for (int i = 0, len = -1;; i++) {
unsigned char c = str[i];
// counting words as sequences of letters or digits
if (isalnum(c)) {
len++;
} else {
if (len >= 0 && len <= MAX_LENGTH) {
length_count[len] += 1;
len = -1;
}
}
if (c == ' ')
break;
}
printf("nWord LengthtOccurrences"
"n-----------t-----------n");
for (int len = 0; len <= MAX_LENGTH; len++) {
if (length_count[len]) {
printf("%-11dt%dn", len, length_count[len]);
}
}
}
int main() {
char myString[] = "The quick Brown ? Fox ? jumps over the Lazy Dog and the !##! LAZY DOG is still sleeping";
// Uncomment if modifying the string is required
//clean_string(myString);
count_letters(myString);
count_word_lengths(myString);
return 0;
}
输出:
Letter Count
------ -----
a 3
b 1
c 1
d 3
e 6
f 1
g 3
h 3
i 4
j 1
k 1
l 5
m 1
n 3
o 5
p 2
q 1
r 2
s 4
t 4
u 2
v 1
w 1
x 1
y 2
z 2
Word Length Occurrences
----------- -----------
1 1
2 7
3 3
4 4
7 1
使用strtok_r()
并简化计数.
它是同级strtok()
不是线程安全的。在为什么 strtok() 被认为是不安全的?
此外,strtok_r()
通过在字符串内插入