自写的C函数拆分字符串时出错

时间:2019-07-17 01:44:52

标签: c string loops

几周前我刚刚了解了字符串,并且对自己编写函数很感兴趣。起初我尝试了一个简短的句子,但它确实有效,但是当我尝试一个较长的句子时,我遇到了问题...

问题可能出在“ getSplitText”函数中,如何解决此错误?

在这种情况下使用for循环不好吗?

之前

image 1

之后

image 2

这是我的代码

#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;
}

1 个答案:

答案 0 :(得分:2)

OP代码中出现了许多malloc()

关于已经给出的评论:

  
      
  1. 将任何东西乘以1都没有效果,只会使代码混乱。建议删除该表达式。
  2.   

恕我直言,这是风格问题。如果它改善或削弱了代码的可读性,则是个人喜好问题。我认为编译器足够聪明,可以忽略乘以1的情况,我也会忽略。

  
      
  1. 函数malloc()期望使用类型为size_t的参数,但是textlen被声明为int
  2.   

尽管我同意,只要提供的int值为正,我就不会将其视为关键点。

  
      
  1. 始终检查(!= NULL)返回值以确保操作成功。
  2.   

这也是我的建议。

我个人讨厌的是,代码中有一些malloc(),但没有一个free()。用malloc()分配内存意味着内部堆内存管理器中将某些堆内存标记为“使用中”。如果未free() d,它将被标记为“已使用”,直到过程寿命结束。丢失(例如重写)指向分配的存储器的指针使其丢失。 (无法再次解决。)这称为memory leak

当然,这在公开的示例代码中不是关键问题,但是在具有更多堆内存消耗的大型应用程序中可能会成为一个问题。因此,我觉得值得一提。

整个示例似乎类似于使用标准库函数可以完成的操作

(并不是您误会我。我认为没有什么类似标准功能的学习目的。)

getLengthString()实际上没有任何问题。但是,它管理两个计数变量ilen。 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的回答。

要注意两个事实:

  1. strtok()通过用\0字节替换分隔符的出现来修改输入字符串(以分隔找到的标记)。

  2. 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.

Live Demo on coliru