我想我检查了所有的边缘情况。如果有人想到我错过的边缘情况,请在 cmets 中告诉我,我会更新这篇文章。我试图使错误消息保持简单。如果您不同意此决定,请随时根据需要进行更改。
// 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() 也能返回数字开始的位置。您可以通过从结束指针迭代回来来解决这个问题,但它可能会很慢。