Перевести строку в число

  1. #include <ctype.h>
  2. #include <errno.h>
  3. #include <stdbool.h>
  4. #include <stdlib.h>
  5.  
  6. /*
  7.     Use these functions instead of strto{l,ll,ul,ull}.
  8.  
  9.     The strto* are too flexible. They allow developers to write code with no validation of input data,
  10.     making their code unreliable (e.g. you get 0 for an empty string) and less maintainable.
  11.  
  12.     OTOH, safe_ato* have no flexibility: they work only with strings consisting entirely of numeric input.
  13.     It allows developers to write reliable, simple, and identical code.
  14. */
  15.  
  16. /*
  17.     Example:
  18.  
  19.         char *v = "10";
  20.         bool ok;
  21.         long s = safe_atol(&ok, v);
  22.         if (ok) {
  23.             printf("%ld\n", s);
  24.         }
  25. */
  26. long
  27. safe_atol
  28.     (bool *ok, char *s)
  29. {
  30.     char *end;
  31.     errno = 0;
  32.     long output = strtol(s, &end, 10);
  33.  
  34.     // a string must not be empty (1) and begin with spaces (2)
  35.     // there must be a number and no remaining characters (3)
  36.     // there must be no out of ranger error and other errors (4)
  37.     *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
  38.  
  39.     return output;
  40. }
  41.  
  42. long long
  43. safe_atoll
  44.     (bool *ok, char *s)
  45. {
  46.     char *end;
  47.     errno = 0;
  48.     long long output = strtoll(s, &end, 10);
  49.  
  50.     // a string must not be empty (1) and begin with spaces (2)
  51.     // there must be a number and no remaining characters (3)
  52.     // there must be no out of ranger error and other errors (4)
  53.     *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
  54.  
  55.     return output;
  56. }
  57.  
  58. unsigned long
  59. safe_atoul
  60.     (bool *ok, char *s)
  61. {
  62.     char *end;
  63.     errno = 0;
  64.     unsigned long output = strtoul(s, &end, 10);
  65.  
  66.     // a string must not be empty (1) and begin with spaces (2)
  67.     // there must be a number and no remaining characters (3)
  68.     // there must be no out of ranger error and other errors (4)
  69.     *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
  70.  
  71.     return output;
  72. }
  73.  
  74. unsigned long long
  75. safe_atoull
  76.     (bool *ok, char *s)
  77. {
  78.     char *end;
  79.     errno = 0;
  80.     unsigned long long output = strtoull(s, &end, 10);
  81.  
  82.     // a string must not be empty (1) and begin with spaces (2)
  83.     // there must be a number and no remaining characters (3)
  84.     // there must be no out of ranger error and other errors (4)
  85.     *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
  86.  
  87.     return output;
  88. }
  1. void
  2. test_safe_atol
  3.     (void)
  4. {
  5.     char *v = "1s0";
  6.     long s;
  7.     bool ok1;
  8.     if ((s = safe_atol(&ok1, v), ok1)) {
  9.         printf("HI\n");
  10.     }
  11.  
  12.     bool ok;
  13.     assert_true_msg((safe_atol(&ok, ""), !ok), "a string must not be empty");
  14.     assert_true_msg((safe_atol(&ok, " 42"), !ok), "a string must not begin with spaces");
  15.     assert_true_msg((safe_atol(&ok, "abc"), !ok), "there must be a number");
  16.     assert_true_msg((safe_atol(&ok, "42 "), !ok), "there must be no remaining characters");
  17.     assert_true_msg((safe_atol(&ok, "10000000000000000000"), !ok), "there must be no out of range error");
  18.     assert_true_msg((safe_atol(&ok, "0x7"), !ok), "it does not work with non-decimal numbers");
  19.     assert_true((safe_atol(&ok, "--42"), !ok));
  20.     long num = safe_atol(&ok, "-42");
  21.     assert_true_msg(ok, "it should be ok to negative numbers");
  22.     assert_true_msg(num == -42, "it should be ok to negative numbers");
  23.     num = safe_atol(&ok, "0");
  24.     assert_true_msg(ok, "it should be ok to 0");
  25.     assert_true_msg(num == 0, "it should be ok to 0");
  26.     num = safe_atol(&ok, "42");
  27.     assert_true_msg(ok, "it should be ok to positive numbers");
  28.     assert_true_msg(num == 42, "it should be ok to positive numbers");
  29.     num = safe_atol(&ok, "1000000000000000000");
  30.     assert_true(ok);
  31.     assert_true(num == 1000000000000000000);
  32.     errno = 1;
  33.     assert_true_msg((safe_atol(&ok, "10"), ok), "it ignores old errno");
  34.  
  35.     // convert LONG_MIN to string
  36.     int len = snprintf(NULL, 0, "%ld", LONG_MIN) + 1;
  37.     char long_min_str[len];
  38.     sprintf(long_min_str, "%ld", LONG_MIN);
  39.  
  40.     // convert LONG_MAX to string
  41.     len = snprintf(NULL, 0, "%ld", LONG_MAX) + 1;
  42.     char long_max_str[len];
  43.     sprintf(long_max_str, "%ld", LONG_MAX);
  44.  
  45.     num = safe_atol(&ok, long_min_str);
  46.     assert_true_msg(ok, "it should be ok to LONG_MIN");
  47.     assert_true_msg(num == LONG_MIN, "it should be ok to LONG_MIN");
  48.     num = safe_atol(&ok, long_max_str);
  49.     assert_true_msg(ok, "it should be ok to LONG_MAX");
  50.     assert_true_msg(num == LONG_MAX, "it should be ok to LONG_MAX");
  51. }
Метод отличается от стандартного тем, что входные значения проверяются. Функции возвращают false, если строка содержит что-нибудь отличное от цифрового ввода.

В дополнение к основному коду добавил тесты. По ним можно посмотреть как работает функция.

По аналогии можно сделать обёртки над strtod()/strtof()/strtold()

Реклама

Мы в соцсетях

tw tg yt gt