几周前我刚刚了解了字符串,并且对自己编写函数很感兴趣。起初我尝试了一个简短的句子,但它确实有效,但是当我尝试一个较长的句子时,我遇到了问题...
问题可能出在“ getSplitText”函数中,如何解决此错误?
在这种情况下使用for循环不好吗?
之前
之后
这是我的代码
#include <stdio.h>
#include <stdlib.h>
char *getSplitText(char *var_text, char split, int index);
int getLengthSplitText(char *var_text, char split);
int getLengthString(char *var_text);
int main(){
char var_text[100];
fgets(var_text,100,stdin);
int i;
printf("Total Characters: %d\n",getLengthString(var_text));
printf("Total Words: %d\n",getLengthSplitText(var_text,' '));
printf("Split Result:\n");
for(i=1;i<=getLengthSplitText(var_text,' ');i++){
printf("#%d-%s\n",i,getSplitText(var_text,' ',i));
}
return 0;
}
int getLengthString(char *var_text){
int len = 0, i = 0;
while (var_text[i] != '\0'){
len++;
i++;
}
return len;
}
int getLengthSplitText(char *var_text, char split){
int textlen = getLengthString(var_text);
int lenKata=0;
char *resultKata = malloc(sizeof(char) * textlen);
resultKata[0]='\0';
int i,j=0;
for(i=0;i<textlen;i++){
if(var_text[i]!=split){
if(textlen-1==i){
resultKata[j] = var_text[i];
resultKata[j + 1] = '\0';
lenKata ++;
}else {
resultKata[j] = var_text[i];
j++;
resultKata[j + 1] = '\0';
}
//printf("#1 %s\n",resultKata);
}else if(var_text[i]==split){
if(resultKata[0]!='\0'){
lenKata ++;
}else{
j = 0;
resultKata[0] = '\0';
}
}
//printf("#2 %s\n",resultKata);
}
return lenKata;
}
char *getSplitText(char *var_text,char split, int index){
int textlen = getLengthString(var_text);
char *resultKata = malloc(sizeof(char) * textlen);
resultKata[0]='\0';
if(index<=getLengthSplitText(var_text,split)) {
int i,j=0;
int lenKata=0;
for (i = 0; i < textlen; i++) {
if (var_text[i] != split) {
if (textlen - 1 == i) {
resultKata[j] = var_text[i];
resultKata[j + 1] = '\0';
lenKata++;
break;
} else {
resultKata[j] = var_text[i];
j++;
resultKata[j + 1] = '\0';
}
//printf("#1 %s\n",resultKata);
} else if (var_text[i] == split) {
if (resultKata[0] != '\0') {
lenKata++;
if (lenKata == index) {
resultKata[j] = '\0';
break;
} else {
j = 0;
resultKata[0] = '\0';
}
}
//printf("#2 %s\n",resultKata);
}
}
}
return resultKata;
}
答案 0 :(得分:2)
OP代码中出现了许多malloc()
。
关于已经给出的评论:
- 将任何东西乘以1都没有效果,只会使代码混乱。建议删除该表达式。
恕我直言,这是风格问题。如果它改善或削弱了代码的可读性,则是个人喜好问题。我认为编译器足够聪明,可以忽略乘以1的情况,我也会忽略。
- 函数
malloc()
期望使用类型为size_t
的参数,但是textlen
被声明为int
尽管我同意,只要提供的int
值为正,我就不会将其视为关键点。
- 始终检查(!= NULL)返回值以确保操作成功。
这也是我的建议。
我个人讨厌的是,代码中有一些malloc()
,但没有一个free()
。用malloc()
分配内存意味着内部堆内存管理器中将某些堆内存标记为“使用中”。如果未free()
d,它将被标记为“已使用”,直到过程寿命结束。丢失(例如重写)指向分配的存储器的指针使其丢失。 (无法再次解决。)这称为memory leak。
当然,这在公开的示例代码中不是关键问题,但是在具有更多堆内存消耗的大型应用程序中可能会成为一个问题。因此,我觉得值得一提。
整个示例似乎类似于使用标准库函数可以完成的操作
(并不是您误会我。我认为没有什么类似标准功能的学习目的。)
getLengthString()
实际上没有任何问题。但是,它管理两个计数变量i
和len
。 OP可能会忽略在每个迭代步骤之后它们始终具有相同的值。因此,其中之一是多余的,可以消除:
int getLengthString(char *var_text)
{
int len = 0;
while (var_text[len]) ++len;
return len;
}
关于getLengthSplitText()
(负责计算单词数),我根本不明白为什么需要任何malloc()
。因此,我编写了一个新函数getNumTokens()
,该函数计算由某个分隔字符分隔的非空单词(我称它们为“令牌”):
int getNumTokens(char *var_text, char split)
{
int n = 0;
for (int inToken = 0; *var_text; ++var_text) {
if (!inToken && *var_text != split) ++n; // count if new token starts
inToken = *var_text != split; // update flag
}
return n;
}
它有点短,不需要任何malloc()
。
请注意,变量inToken
实际上用作(布尔值)标志。记住在遍历文本时是否已经计算了令牌。通过分配当前字符(*var_text
)和分隔符(split
)的比较结果,在每个迭代步骤中对其进行更新。
提供的var_text
直接用于访问和进度-不使用额外的索引。由于只需要对文本进行一次迭代,因此更改指针不会造成任何伤害。这是一个本地副本(按值传递),功能内部的生命周期有限。
关于strtok()
,我曾经写过一个变体strtoke()
(只是为了好玩)作为对SO: Split string into Tokens in C, when there are 2 delimiters in a row的回答。
要注意两个事实:
strtok()
通过用\0
字节替换分隔符的出现来修改输入字符串(以分隔找到的标记)。
strtok()
管理内部全局状态,使其不可重入。
OP的解决方案似乎没有使用malloc()
(考虑到所有上述问题)购买这些限制。
因此,我稍微改变了假设(实际上在OP问题中没有提到任何限制)。我认为对输入(1.)的修改是可以接受的,并编写了一个新函数getNextToken()
:
char* getNextToken(char *var_text, char split)
{
// skip space
for (; *var_text && *var_text == split; ++var_text);
// remember start of token
char *token = var_text;
// skip token
for (; *var_text && *var_text != split; ++var_text);
// remark end of token
*var_text = '\0'; // doesn't hurt if there is already a '\0'
// done
return token;
}
该函数将返回var_text
中第一个标记的开始。如果它是一个空字符串(返回的指针的内容为\0
),则找不到标记。
因此,找到的令牌的末尾用\0
-byte(在给定的输入字符串中)标记,该字节可能是之前的拆分字符。
由于我不想使用全局内部状态,因此必须在getNextToken()
之外的令牌之间进行切换。我发现这是可以接受的,因为可以再次使用功能getLengthString()
完成此操作。这样,可以将指针从令牌的开头移动到结尾(已写入\0
-Byte)。加1,可能到达下一个标记的开始。当然,到达文本结尾时(终止符\0
之后的地址可能超出界限),这可能会中断。幸运的是,令牌的数量是已知的。
完整示例:
#include <stdio.h>
int getLengthString(char *var_text);
int getNumTokens(char *var_text, char split);
char* getNextToken(char *var_text, char split);
int main()
{
char var_text[100];
if (!fgets(var_text, 100, stdin)) {
fprintf(stderr, "Input failed!\n");
return -1;
}
printf("Total Characters: %d\n", (int)getLengthString(var_text));
const char split = ' ';
const int nTokens = getNumTokens(var_text, split);
printf("Total Words: %d\n", nTokens);
printf("Split Result:\n");
char *token = var_text;
for (int i = 1; i <= nTokens; ++i) {
token = getNextToken(token, split);
printf("#%2d-%s\n", i, token);
token += getLengthString(token) + 1;
}
return 0;
}
int getLengthString(char *var_text)
{
int len = 0;
while (var_text[len]) ++len;
return len;
}
int getNumTokens(char *var_text, char split)
{
int n = 0;
for (int inToken = 0; *var_text; ++var_text) {
if (!inToken && *var_text != split) ++n; // count if new token starts
inToken = *var_text != split; // update flag
}
return n;
}
char* getNextToken(char *var_text, char split)
{
// skip space
for (; *var_text && *var_text == split; ++var_text);
// remember start of token
char *token = var_text;
// skip token
for (; *var_text && *var_text != split; ++var_text);
// remark end of token
*var_text = '\0'; // doesn't hurt if there is already a '\0'
// done
return token;
}
I like C programming language but I have problem about this code.
的输出:
Total Characters: 66
Total Words: 12
Split Result:
# 1-I
# 2-like
# 3-C
# 4-programming
# 5-language
# 6-but
# 7-I
# 8-have
# 9-problem
#10-about
#11-this
#12-code.
Thank you for the help. Now, the code can work. I like programming.
的输出:
Total Characters: 68
Total Words: 13
Split Result:
# 1-Thank
# 2-you
# 3-for
# 4-the
# 5-help.
# 6-Now,
# 7-the
# 8-code
# 9-can
#10-work.
#11-I
#12-like
#13-programming.