【问题标题】:How to properly tokenize strings in C without random symbols?如何在没有随机符号的情况下正确标记 C 中的字符串?
【发布时间】:2022-01-22 03:18:15
【问题描述】:

我目前正在学习 C 并尝试编写一个函数来标记由空格分隔的段落/字符串并返回一个包含所有标记的数组。我被卡住了,因为我无法弄清楚为什么某些令牌会携带不在原始字符串中的符号。有人可以帮我弄清楚我的代码有什么问题吗?另外,我不想在代码中添加额外的库或使用 strtok() 等函数。

char **tokenizeParagraph(char *paragraph) {
    char *ptr = paragraph;
    char words[MAX_WORDS][MAX_WORDLENGTH];
    int wordIndex = 0;
    int wordLen = 0;

    while (*ptr) {
        wordLen = 0;

        while (*ptr && *ptr != ' ') {
            wordLen++;
            ptr++;
        }

        if (wordLen > 0) {
            strncpy(words[wordIndex], paragraph, wordLen);
            printf("%s\n", words[wordIndex]);
            wordIndex++;
        }

        ptr++;
        paragraph = ptr;
    }
    return words;
}

这是一个演示结果:

tokenizeParagraph("I'm currently learning C and trying to write a function to tokenize a paragraph/string delimited by spaces and return an array with all the tokens.");

Error Demo

非常感谢!

已编辑:

@Sourav Kannantha B 和@Finxx 建议的动态内存方法非常有用。但是由于我不想添加 库,所以我将数组声明移出函数并将其作为参数传入,因此函数返回后数组不会被堆栈内存擦除。

char words[MAX_WORDS][MAX_CHARS];
void tokenizeParagraph(char words[MAX_WORDS][MAX_CHARS], char *paragraph)

【问题讨论】:

  • 一方面,数组,单词,是在堆栈上分配的,返回它是未定义的行为。阅读有关堆与堆栈的信息
  • 除了已诊断的其他问题之外,我看不到您在哪里终止字符串。
  • strncpy 的使用可能是您的错误的来源。永远不要使用该功能。详情见Is strcpy dangerous and what should be used instead?

标签: arrays c string computer-science tokenize


【解决方案1】:

@Finxx 已经提出的建议已经足够好了。但是如果 wordLen 变化很大,你仍然可以改进它。

char **tokenizeParagraph(char *paragraph) {
    char *ptr = paragraph;
    char** words = malloc(sizeof(char*) * MAX_WORDS);
    int wordIndex = 0;
    int wordLen;

    while (*ptr) {
        wordLen = 0;

        while (*ptr && *ptr == ' ') {
            ptr++;
        }

        paragraph = ptr;

        while (*ptr && *ptr != ' ') {
            wordLen++;
            ptr++;
        }

        if (wordLen > 0) {
            words[wordIndex] = malloc(sizeof(char) * wordLen+1);
            strncpy(words[wordIndex], paragraph, wordLen);
            words[wordIndex][wordLen] = '\0';
            printf("%s\n", words[wordIndex]);
            wordIndex++;
        }
    }

    for(;wordIndex < MAX_WORDS; wordIndex++) {
        words[wordIndex] = NULL;
    }
    return words;
}

另外,请注意strncpy 不添加终止 NUL 字符。这可能是输出中出现随机字符的原因。

另外,不要忘记free调用者函数分配的内存。:

int main() {
    ...
    char** words = tokenizeParagraph(para);
    ...
    for(int i = 0; i < MAX_WORDS; i++) {
        free(words[i]);
    }
    free(words);
    ...
    return 0;
}

【讨论】:

  • 它现在对我有用,谢谢!我错误地认为 strncpy() 会返回一个封闭的字符串。另外我会记住内存分配部分。
  • @LinguaFranca 我在return words; 上方编辑了我的代码。这部分是避免悬空指针所必需的,因为您实际上并没有返回 words 数组中有“多少”字。
【解决方案2】:

您正在堆栈上创建变量words 并在堆栈上返回指向此变量的指针。但是,当您从函数返回时,堆栈不再局限于您的程序,这意味着您的指针指向的某些内容可能会发生变化,从而导致未定义的行为。为了防止这种情况,请更改此代码:

char words[MAX_WORDS][MAX_WORDLENGTH];

用这个:

char** words = calloc(MAX_WORDS * MAX_WORDLENGTH, sizeof(char));

这将在堆而不是堆栈上分配内存,尽管您需要包含stdlib.h

【讨论】:

  • 我建议不要建议calloc。 1. 它不必要地将数据初始化为零。 2. 在事先知道大小的情况下使用堆分配不仅仅是为了将数据保存在内存中。 3. 您还应该提及free。 4. 有一种从调用上下文传递数组的简单方法。
  • 谢谢!这些信息很有帮助。
  • @IharobAlAsimi 1. 它处理指定字符串结尾所需的空值。 2. 当你离开函数作用域时,指向栈内存的指针将被取消引用。 3. 当程序退出时,堆内存被取消引用,我怀疑在内存中保持 1KB 是否真的会伤害任何东西。 4. 如果真的可以的话,我会很感兴趣的。
  • 你没有提到他们需要免费通话。而且您的数字 1 确实是在 C 中使用字符串的一种不好的方法。我建议您先看看我的 SO 配置文件,然后再指出以下内容:堆内存延迟 ...
猜你喜欢
  • 1970-01-01
  • 2023-03-06
  • 2010-11-11
  • 2021-08-12
  • 2011-03-20
  • 1970-01-01
  • 2017-09-13
  • 2015-06-20
  • 1970-01-01
相关资源
最近更新 更多