Перевести строку в число
- #include <ctype.h>
- #include <errno.h>
- #include <stdbool.h>
- #include <stdlib.h>
- /*
- Use these functions instead of strto{l,ll,ul,ull}.
- The strto* are too flexible. They allow developers to write code with no validation of input data,
- making their code unreliable (e.g. you get 0 for an empty string) and less maintainable.
- OTOH, safe_ato* have no flexibility: they work only with strings consisting entirely of numeric input.
- It allows developers to write reliable, simple, and identical code.
- */
- /*
- Example:
- char *v = "10";
- bool ok;
- long s = safe_atol(&ok, v);
- if (ok) {
- printf("%ld\n", s);
- }
- */
- long
- safe_atol
- (bool *ok, char *s)
- {
- char *end;
- errno = 0;
- long output = strtol(s, &end, 10);
- // a string must not be empty (1) and begin with spaces (2)
- // there must be a number and no remaining characters (3)
- // there must be no out of ranger error and other errors (4)
- *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
- return output;
- }
- long long
- safe_atoll
- (bool *ok, char *s)
- {
- char *end;
- errno = 0;
- long long output = strtoll(s, &end, 10);
- // a string must not be empty (1) and begin with spaces (2)
- // there must be a number and no remaining characters (3)
- // there must be no out of ranger error and other errors (4)
- *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
- return output;
- }
- unsigned long
- safe_atoul
- (bool *ok, char *s)
- {
- char *end;
- errno = 0;
- unsigned long output = strtoul(s, &end, 10);
- // a string must not be empty (1) and begin with spaces (2)
- // there must be a number and no remaining characters (3)
- // there must be no out of ranger error and other errors (4)
- *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
- return output;
- }
- unsigned long long
- safe_atoull
- (bool *ok, char *s)
- {
- char *end;
- errno = 0;
- unsigned long long output = strtoull(s, &end, 10);
- // a string must not be empty (1) and begin with spaces (2)
- // there must be a number and no remaining characters (3)
- // there must be no out of ranger error and other errors (4)
- *ok = s[0] != '\0' && !isspace(s[0]) && end[0] == '\0' && !errno;
- return output;
- }
- void
- test_safe_atol
- (void)
- {
- char *v = "1s0";
- long s;
- bool ok1;
- if ((s = safe_atol(&ok1, v), ok1)) {
- printf("HI\n");
- }
- bool ok;
- assert_true_msg((safe_atol(&ok, ""), !ok), "a string must not be empty");
- assert_true_msg((safe_atol(&ok, " 42"), !ok), "a string must not begin with spaces");
- assert_true_msg((safe_atol(&ok, "abc"), !ok), "there must be a number");
- assert_true_msg((safe_atol(&ok, "42 "), !ok), "there must be no remaining characters");
- assert_true_msg((safe_atol(&ok, "10000000000000000000"), !ok), "there must be no out of range error");
- assert_true_msg((safe_atol(&ok, "0x7"), !ok), "it does not work with non-decimal numbers");
- assert_true((safe_atol(&ok, "--42"), !ok));
- long num = safe_atol(&ok, "-42");
- assert_true_msg(ok, "it should be ok to negative numbers");
- assert_true_msg(num == -42, "it should be ok to negative numbers");
- num = safe_atol(&ok, "0");
- assert_true_msg(ok, "it should be ok to 0");
- assert_true_msg(num == 0, "it should be ok to 0");
- num = safe_atol(&ok, "42");
- assert_true_msg(ok, "it should be ok to positive numbers");
- assert_true_msg(num == 42, "it should be ok to positive numbers");
- num = safe_atol(&ok, "1000000000000000000");
- assert_true(ok);
- assert_true(num == 1000000000000000000);
- errno = 1;
- assert_true_msg((safe_atol(&ok, "10"), ok), "it ignores old errno");
- // convert LONG_MIN to string
- int len = snprintf(NULL, 0, "%ld", LONG_MIN) + 1;
- char long_min_str[len];
- sprintf(long_min_str, "%ld", LONG_MIN);
- // convert LONG_MAX to string
- len = snprintf(NULL, 0, "%ld", LONG_MAX) + 1;
- char long_max_str[len];
- sprintf(long_max_str, "%ld", LONG_MAX);
- num = safe_atol(&ok, long_min_str);
- assert_true_msg(ok, "it should be ok to LONG_MIN");
- assert_true_msg(num == LONG_MIN, "it should be ok to LONG_MIN");
- num = safe_atol(&ok, long_max_str);
- assert_true_msg(ok, "it should be ok to LONG_MAX");
- assert_true_msg(num == LONG_MAX, "it should be ok to LONG_MAX");
- }
Метод отличается от стандартного тем, что входные значения проверяются. Функции возвращают false, если строка содержит что-нибудь отличное от цифрового ввода.
В дополнение к основному коду добавил тесты. По ним можно посмотреть как работает функция.
По аналогии можно сделать обёртки над strtod()/strtof()/strtold()
В дополнение к основному коду добавил тесты. По ним можно посмотреть как работает функция.
По аналогии можно сделать обёртки над strtod()/strtof()/strtold()