17

according documentation:

成功时,该函数将转换后的整数作为 long int 值返回。如果无法执行有效转换,则返回零值。如果正确值超出可表示值的范围,则返回 LONG_MAX 或 LONG_MIN,并将全局变量 errno 设置为 ERANGE。

考虑一下如何知道函数是否失败或仅将字符串转换为 strtol(str, (char**)NULL, 10);数字?str"0\0""0"

4

5 回答 5

19

如果要进行错误检查,则需要传递一个真实的指针地址,这样您就可以区分 0 产生的值"0"和类似的 0 产生的值"pqr"

char *endptr;
errno = 0;
long result = strtol(str, &endptr, 10);
if (endptr == str)
{
    // nothing parsed from the string, handle errors or exit
}
if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE)
{
    // out of range, handle or exit
}
// all went fine, go on
于 2012-07-01T05:25:37.463 回答
5

由于接受的答案实际上并不是检查失败的正确方法。

您不应通过检查 strtol 的返回值来检查错误,因为该字符串可能是 0l、LONG_MAX 或 LONG_MIN 的有效表示。相反,请检查 tailptr 是否指向您期望的数字之后的内容(例如,如果字符串应该在数字之后结束,则为 '\0')。您还需要在调用之前清除 errno 并在之后检查它,以防溢出。

于 2018-06-28T12:04:32.690 回答
4

恕我直言,我更sscanf()喜欢atoi()or strtol()。主要原因是您无法可靠地检查某些平台(即 Windows)上的错误状态,除非您使用sscanf()1如果成功则返回,0如果失败则返回)。

于 2012-07-01T05:24:52.470 回答
2

我想我检查了所有的边缘情况。如果有人想到我错过的边缘案例,请在评论中告诉我,我会更新这篇文章。我试图使错误消息保持简单。如果您不同意此决定,请随时按照您认为合适的方式进行更改。

// Copyright (C) 2021 by cmwt
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <errno.h>    // errno, ERANGE
#include <limits.h>   // LONG_MAX, LONG_MIN
#include <stdbool.h>  // true, false
#include <stddef.h>   // ptrdiff_t
#include <stdio.h>    // printf()
#include <stdlib.h>   // strtol()

struct ResultToLong {
  const char *err_msg;
  long        answer;
  ptrdiff_t   num_read;
  bool        is_func_success;
};

struct ResultToLong stringToLong(const char* start, int base) {
  struct ResultToLong result = {NULL, 0, 0, false};
  int save_errno = 0;
  char *end = NULL;

  if (base < 0 || base > 36) {
    result.err_msg = "Bad base: expect (0 <= base <= 36)";
    return result;
  }
  if (start == NULL) {
    result.err_msg = "Bad start: expect (start != NULL)";
    return result;
  }
  if (*start == '\0') {
    result.err_msg = "Bad start: start empty (const char* start == \"\";)";
    return result;
  }

  errno = 0;
  result.answer = strtol(start, &end, base);
  save_errno = errno;

  if (result.answer == 0 && *(start - 1) != '0') {
    result.err_msg = "Bad start: not a number";
    result.num_read = end - start;
    return result;
  }
  if (result.answer == LONG_MIN && save_errno == ERANGE) {
    result.err_msg = "Bad start: result < LONG_MIN";
    result.num_read = end - start;
    return result;
  }
  if (result.answer == LONG_MAX && save_errno == ERANGE) {
    result.err_msg = "Bad start: result > LONG_MAX";
    result.num_read = end - start;
    return result;
  }
  if (*end != '\0') {
  result.err_msg = "Warning: number in start is not '\\0' terminated";
  result.num_read = end - start;
  result.is_func_success = true;
  return result;
  }

  result.err_msg = "Success";
  result.num_read = end - start;
  result.is_func_success = true;
  return result;
}

int main() {
  struct ResultToLong result;
  const char* str;
  
  printf("Starting...\n\n");

  str= NULL;
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: %s\n", "<NULL>");
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);
  
  str= "";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "42";
  result = stringToLong(str, -1);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "42";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "42 ";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "                42";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "0x42";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "042";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "+9999999999999999999";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "-9999999999999999999";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  str= "?";
  result = stringToLong(str, 0);
  printf("Response message: %s\n", result.err_msg);
  printf("Input string: '%s'\n", str);
  printf("Number of chars processed: %ld\n", result.num_read);
  printf("Result: %ld\n\n", result.answer);

  printf("Done.\n");
}

如果您反对按值返回此大小的结构,则将指向该结构的指针作为附加参数传递,只是不要忘记处理结构指针为NULL. 我的目标是让这段代码易于理解。您可能想要组合检查并集中设置返回值。

其他想法

我有点希望strtol()也能从数字开始的地方返回。您可以通过从结束指针迭代回来来解决这个问题,但它可能会很慢。

于 2021-03-26T21:32:13.370 回答
1

您可以检查errno或传递第二个参数的非 NULL 值,并将其结果值与 进行比较str,例如:

char * endptr;
long result = strtol(str, &endptr, 10);
if (endptr > str)
{
    // Use result...
}
于 2012-07-01T05:24:28.440 回答