【问题标题】:Checking the validity of a string representing a number检查表示数字的字符串的有效性
【发布时间】:2018-10-19 13:56:46
【问题描述】:

我需要编写一个函数来检查字符串的一些属性:

  1. 字符串必须表示一个正整数 (> 0)
  2. 整数不得超过 32 位内存
  3. 字符串中没有字母

如果满足这些条件,它应该将字符串作为 int 返回,如果其中任何一个条件不满足,它应该返回 -1。

目前该函数无法处理以下 2 个输入:

  • 4y
  • 13.4

如果我的isDigit() 循环按预期工作,它将能够检查它们。为什么循环不起作用?

int convert(const char length[]) {
  long input = atol(length);
  if (input >= 2147483648 || input <= 0) {
    return -1;
  }
  int chkr = 0;
  while (chkr < strlen(length)) {
    if (isdigit(length[chkr++]) == 0) {
      return -1;
   }
    else {
      return atoi(length);
    }
  }
  input = atol(length);
  if (length[0] == '0') {
    return -1;
  }
  if (strlen(length) < 3) {
    return -1;
  }
 else {
    return atoi(len gth);
  }
}

【问题讨论】:

  • 我宁愿使用strtol。另外请展示一些有效和无效字符串的例子
  • 请告诉我们什么是“3 整数字符串”。
  • 我建议 length 通常不是一个好的字符串名称 - 当然在提供的代码的上下文中。
  • 学习使用调试器。
  • 从不使用atol()(或atoi()atoll())。来自the man page:“...atoi() 未检测到错误。”

标签: c


【解决方案1】:

你的函数非常复杂和错误。

改用这个,让 C 库做这些脏活:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>

// The function you're interested in

int convert(const char string[]) {
  char *endptr;
  if (!isdigit((unsigned char)string[0]))
    return -1;

  errno = 0;    // need to set errno to 0 (see errno documentation)
  long value = strtol(string, &endptr, 10);
  if (errno != 0 || value <= 0 || value > 2147483647 || *endptr != 0)
  {
    return -1;
  }
  return value;
}

int main() {
  // Test different cases:

  struct {
    const char *input;
    int expected;
  } testcases[] =
  {
    // OK cases
    "123", 123,
    "1234", 1234,
    "2147483647", 2147483647,

    // fail cases
    "-1234", -1,      // number is negatif
    "12.3", -1,       // contains non digit '.'
    "123y", -1,       // contains non digit 'y'
    "2147483648", -1, // out of range
    " 123", -1,      // starts with a space

    // wrong test case on purpose
    "1234", 1245,
  };

  // Test all test cases

  for (int i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++)
  {
    int value = convert(testcases[i].input);
    if (value != testcases[i].expected)
    {
      printf("convert(\"%s\") failed, returned value = %d, expected value = %d\n", testcases[i].input, value, testcases[i].expected);
    }
    else
    {
      printf("convert(\"%s\") passed\n", testcases[i].input);
    }
  }
  return 0;
}

程序打印每个测试用例。最后一个测试用例是故意错误的。

for 循环遍历多个测试用例,并为每个失败的测试用例打印相关值。

输出:

convert("123") passed
convert("1234") passed
convert("2147483647") passed
convert("-1234") passed
convert("12.3") passed
convert("123y") passed
convert("2147483648") passed
convert("1234") failed, returned value = 1234, expected value = 1245

【讨论】:

    【解决方案2】:

    您只检查第一个字符,然后立即返回。

    input = atol(length);
    

    无法访问。

    【讨论】:

      【解决方案3】:

      看看这个循环:

      while (chkr < strlen(length)) {
          if (isdigit(length[chkr++]) == 0) {
              return -1;
          }
          else {
              return atoi(length);
          }
      }
      

      您不会遍历整个字符串,无论如何您都会在第一个字符之后返回。您不想在这里返回else 的情况,否则会跳过程序的其余部分。只要有这个:

      while (chkr < strlen(length)) {
          if (isdigit(length[chkr++]) == 0) {
              return -1;
          }
      }
      

      之后,这一行在这里

      input = atol(length);
      

      不需要。无论如何,输入都有那个值。不过,它不会造成任何伤害。

      【讨论】:

      • 感谢您对整理代码的回复。尽管该函数现在更简单了,但它现在无法将任何输入字符串转换为输出数字...
      • 我只想对大家的讨论表示感谢。感觉就像我已经学会了一个绝对的卸载,我的问题已经解决了!
      • isdigit 需要一个无符号字符范围或 EOF 中的值。使用 char,代码有​​ UB 风险。 'isdigit((unsigned char) length[...])' 是谨慎的。
      • 这将在“4000000000”上失败。
      【解决方案4】:
      int convert(const char length[]) {
          if (atol(length) >= 2147483648 || atol(length) <= 0)
              return -1;
      
          int chkr = 0;
          while (chkr < strlen (length)) {
              if (isdigit(length[chkr++]) == 0)
                  return -1;
          }
      
          return atoi(length);
      }
      

      编辑:固定答案。不知道我为什么不使用 isdigit()。
      我想太努力了,结果适得其反。

      【讨论】:

      • 那么为什么不让编译器为您完成工作并编写'0' 而不是48'9' 而不是57
      • 彻底改造 standard C isdigit() function 是一个非常错误的想法 - 充其量是 - 想法。
      • 我只想对大家的讨论表示感谢。感觉就像我已经学会了一个绝对的卸载,我的问题已经解决了!
      • 1) isdigit 没有为 char
      • 问题要求正整数。我也不会接受 +123。
      【解决方案5】:

      如前所述,您的 while 循环在第一次迭代后返回。

      使用sscanf 代替atolatoi。这更可取,因为您可以检测错误:

      int convert(const char *length){
          int err, sz;
          unsigned rtn;
          /*%u reads an unsigned integer (mostly 32 bit) >= 0*/
          err = sscanf(length, "%u%n", &rtn, &sz);
          /*check reading error occured*/
          if(err == 0){
              return -1;
          }
          /*check if there is no whitespace/sign*/
          if(!isdigit(length[0])){
              return -1;
          }
          /*check if 0 < rtn <= INT_MAX*/
          if(rtn <= 0 || rtn > INT_MAX){
              return -1;
          }
          /*check everything got read*/
          /*=> no letters*/
          if(sz != strlen(length)){
              return -1;
          }
          return rtn;
      }
      

      让我们测试一下:

      /*these fail*/
      const char zero[] = "0";
      const char spaceStart[] = " 84654";
      const char spaceEnd[] = "84654 ";
      const char negative[] = "-7869";
      const char tooBig[] = "2147483648";
      const char fitsInto32BitInt[] = "2147483647";
      const char positive[] = "+7526";
      const char withLetter[] = "4y";
      const char withPoint[] = "13.4";
      /*these work*/
      const char one[] = "1";
      const char fine[] = "746838";
      const char fitsInto32BitInt[] = "2147483647";
      

      【讨论】:

      • printf("%d\n", convert("5000000000")); 返回705032704,我希望OP 会想要-1。重点是,在扫描文本以使用"%u" 保存在unsigned 中时,溢出是未定义的行为"%u" 合理但不稳健。
      猜你喜欢
      • 1970-01-01
      • 2012-10-28
      • 1970-01-01
      • 1970-01-01
      • 2014-11-14
      • 1970-01-01
      • 2013-02-13
      • 1970-01-01
      相关资源
      最近更新 更多