【问题标题】:How do I properly use stringsplitting without strtok如何在没有 strtok 的情况下正确使用字符串拆分
【发布时间】:2021-08-12 06:11:26
【问题描述】:

我正在尝试制作一个 C 字符串计算器。这意味着我有一个带有数字和中间分隔符的字符串。分隔符可以是任意大小,只要它不是数字即可。此外,如果特定数字的值介于 1001 和 1111 之间,则不能使用它并将被忽略(设置为零)。这个赋值的难点在于使分隔/分割字符串部分没有strtok。我得到了各种错误的输出,我不知道我做错了什么,但我觉得我在疏忽某些事情或做一些非常愚蠢的事情。该代码还冻结了我的单元测试,所以我什至无法测试出了什么问题。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>

void ValidateInput(int *x) {
    if (*x >= 1001 && *x <= 1111) { 
        x = 0;
    }
}

int test(char *numbers, int numbers_length, int *result) {
    char cnum1[numbers_length];
    char cnum2[numbers_length];

    strcpy(cnum1, "");
    strcpy(cnum2, "");

    bool x = false;
    size_t i = 0;

    while (numbers[i] != '\0') {
        char c = numbers[i];
        if (isdigit(c)) {
            if (x) {
                strcat(cnum2, &c);
            }
            if (!x) {
                strcat(cnum1, &c);
            }
        }
        if (!isdigit(c)) {
            x = true;
        }
        i++;
    }

    int num1;
    num1 = atoi(cnum1);
    int num2 = atoi(cnum2);

    ValidateInput(&num1);
    ValidateInput(&num2);

    printf("%d\n", num1);
    printf("%d\n", num2);
    
    return num1 + num2;
}

int main() {
    int sum;
    char numbers[] = "100,10";
    int length = strlen(numbers);
    test(numbers, length, &sum);
    return -1;
}

【问题讨论】:

  • 您是否尝试过在调试器中逐行运行代码,同时监控所有变量的值,以确定您的程序在哪个点停止按预期运行?如果您没有尝试过,那么您可能想阅读以下内容:What is a debugger and how can it help me diagnose problems? 您可能还想阅读以下内容:How to debug small programs?
  • 'strcat(cnum2, &c);' strcat() 需要 NUL 终止的 char 数组作为两个参数 - 传递一个 char 的地址是 UB,(除非 char 是 NUL)。
  • 检查所有代码并检查所有 str* 库调用是否提供了有效参数,以及所有数组是否足够长以容纳复制的任何“字符串”,包括 NUL 终止符。
  • 马上我觉得有必要指出在ValidateInput 中,您正在检查指针的值(这很好),然后如果它无效,则设置 指向 0 的指针的地址(这不好)。如果您在那之后尝试访问x,这将导致段错误。
  • 源字符串中有多少个分隔符?他们应该如何跨字符串拆分?如果只有 1 个分隔符,我假设之后的所有内容都会进入第二个字符串?在您的问题中发布一两个应该如何修改数据的示例可能会有所帮助。

标签: arrays c string unit-testing pointers


【解决方案1】:

代码中存在一些问题:

  • ValidateInput()中的x = 0在函数外没有任何作用。你应该写*x = 0;

  • strcat(cnum2, &amp;c); 不正确:&amp;c 不是正确的 C 字符串。您应该即时转换数字或使用索引附加字符。

  • test() 不需要字符串的长度,它可以测试空终止符。

  • if (!x)if (!isdigit(c)) 是多余的,只需使用 else 子句即可。

  • test()返回sum,它不应该把sum的地址作为参数,它的返回值应该存储在main()函数中的sum中。

这是修改后的版本:

#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>

void ValidateInput(int *x) {
    if (*x >= 1001 && *x <= 1111) { 
        *x = 0;
    }
}

int test(char *numbers) {
    int num1 = 0;
    int num2 = 0;
    bool x = false;

    for (size_t i = 0; numbers[i] != '\0'; i++) {
        unsigned char c = numbers[i];
        if (isdigit(c)) {
            if (x) {
                num2 = num2 * 10 + c - '0';
            } else {
                num1 = num1 * 10 + c - '0';
            }
        } else {
            x = true;
        }
    }

    ValidateInput(&num1);
    ValidateInput(&num2);

    printf("%d\n", num1);
    printf("%d\n", num2);
    
    return num1 + num2;
}

int main() {
    char numbers[] = "100,10";
    int sum = test(numbers);
    printf("sum=%d\n", sum);
    return 0;
}

【讨论】:

  • 应该添加他的!isdigit() 不需要并且应该只是一个else。
  • 喜欢您的 atoi 替代品。应该让它成为将来某人的面试问题:) - 感觉像 num1/num2 的数组可能也会稍微简化 if 逻辑。 (x 成为索引。)
  • @chqrlie ,你是个救命稻草。我不知道该怎么感谢你才足够!你的代码创造了奇迹。不过,我确实有一个问题。这行代码做了什么:num2 = num2 * 10 + c - '0'。你为什么要倍增?您是否有消息来源,以便我可以阅读此内容,或者您​​介意向我解释一下吗?
  • 简称为atoi。在纸上浏览它,看看它是如何工作的:)
  • @MichaelDorgan 谢谢你的帮助 :)
【解决方案2】:

你忘记了 Nullbytes \0

char numbers[] = "100,10\0";
char cnum1[numbers_length]; //cnum1[3] = '\0'; for value 100 before  atoi(cnum1);
char cnum2[numbers_length]; //cnum2[2] = '\0'; for value 10  before  atoi(cnum2);

还有一个printf由自己的函数调用

printf("%d\n",test(numbers, length, &sum));

查看添加的结果。 ^^

您还可以将结果存储在您的result 变量中,即ref

//Hint: int test(..., int* result)
result = num1 + num2;
//and in the main
printf("%d\n",sum);

如果你想要返回使用,否则。

return num1 + num2;

提示:

int test(char* numbers, int numbers_length, int* result)
{
   char cnum1[numbers_length];
   char cnum2[numbers_length];
   char* ptr1 = &cnum1[0];
   char* ptr2 = &cnum2[0];
...
   if(x)
   {
     *ptr2 = c;
     ptr2++;
   }

   if(!x)
   {
     *ptr1 = c;
     ptr1++;
   }
   ....
   *ptr1 = '\0';
   int num1 = atoi(cnum1);
   *ptr2 = '\0';
   int num2 = atoi(cnum2);

【讨论】:

  • 如果您将它放在这样的引号中,可以肯定编译器会为您添加 \0。请参阅 godbolt.org/z/EGzh8occv 并注意 strlen 在汇编器中是正确的。
  • 也就是说,当他从 cnum1 切换到 cnum2 以及 cnum2 上的最终 NUL 时,他确实忘记了 NUL 字节......
  • 除了您还忘记在缓冲区切换和最终写入时 NUL 终止您的字符串。您可能会在调试版本中侥幸成功,其中 cnum1cnum2 为您设置为 0,但在发布时,这将失败。考虑为您的if(!x) 也使用else - 更简单的逻辑。最后,如果您要发布答案作为解决方案,请确保完全编译。我个人发现它有助于链接一个 Godbolt 或其他在线编译器结果来验证我的代码。
  • 现在可以正常工作了。放置空字节。对不起,我自己忘记了。 ;P
  • 当然 - 我对演示更改的代码片段没有任何问题,但如果它不是解决方案,请不要将其称为“解决方案”。看看这个例子的upvoted answer。并感谢您更新您的答案并接受一些建议。 :) 我实际上正在考虑删除我的答案,因为最佳答案是我的建议的两倍......
猜你喜欢
  • 1970-01-01
  • 2012-09-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多