Tôi đang cố gắng tìm hiểu xem có cách nào khác để chuyển đổi chuỗi thành số nguyên trong C.
Tôi thường xuyên mô hình hóa sau đây trong mã của tôi.
char s[] = "45";
int num = atoi(s);
Vì vậy, có một cách tốt hơn hay cách khác?
Tôi đang cố gắng tìm hiểu xem có cách nào khác để chuyển đổi chuỗi thành số nguyên trong C.
Tôi thường xuyên mô hình hóa sau đây trong mã của tôi.
char s[] = "45";
int num = atoi(s);
Vì vậy, có một cách tốt hơn hay cách khác?
Câu trả lời:
Có strtol
IMO tốt hơn. Ngoài ra tôi cũng rất thích strtonum
, vì vậy hãy sử dụng nó nếu bạn có nó (nhưng hãy nhớ rằng nó không di động):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
Bạn cũng có thể quan tâm strtoumax
vàstrtoimax
đó là các chức năng tiêu chuẩn trong C99. Ví dụ bạn có thể nói:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
Dù sao, tránh xa atoi
:
Cuộc gọi atoi (str) sẽ tương đương với:
(int) strtol(str, (char **)NULL, 10)
ngoại trừ việc xử lý các lỗi có thể khác nhau. Nếu giá trị không thể được biểu diễn, hành vi không được xác định .
strtonum
gì? Tôi tiếp tục nhận được một cảnh báo tuyên bố ngầm
#<stdlib.h>
. Tuy nhiên, bạn có thể sử dụng strtoumax
thay thế tiêu chuẩn .
strtol
Giải pháp mạnh mẽ dựa trên C89
Với:
atoi
gia đình)strtol
(ví dụ: không có khoảng trắng hàng đầu cũng như các ký tự rác)#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {
STR2INT_SUCCESS,
STR2INT_OVERFLOW,
STR2INT_UNDERFLOW,
STR2INT_INCONVERTIBLE
} str2int_errno;
/* Convert string s to int out.
*
* @param[out] out The converted int. Cannot be NULL.
*
* @param[in] s Input string to be converted.
*
* The format is the same as strtol,
* except that the following are inconvertible:
*
* - empty string
* - leading whitespace
* - any trailing characters that are not part of the number
*
* Cannot be NULL.
*
* @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
*
* @return Indicates if the operation succeeded, or why it failed.
*/
str2int_errno str2int(int *out, char *s, int base) {
char *end;
if (s[0] == '\0' || isspace(s[0]))
return STR2INT_INCONVERTIBLE;
errno = 0;
long l = strtol(s, &end, base);
/* Both checks are needed because INT_MAX == LONG_MAX is possible. */
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
return STR2INT_OVERFLOW;
if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
return STR2INT_UNDERFLOW;
if (*end != '\0')
return STR2INT_INCONVERTIBLE;
*out = l;
return STR2INT_SUCCESS;
}
int main(void) {
int i;
/* Lazy to calculate this size properly. */
char s[256];
/* Simple case. */
assert(str2int(&i, "11", 10) == STR2INT_SUCCESS);
assert(i == 11);
/* Negative number . */
assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS);
assert(i == -11);
/* Different base. */
assert(str2int(&i, "11", 16) == STR2INT_SUCCESS);
assert(i == 17);
/* 0 */
assert(str2int(&i, "0", 10) == STR2INT_SUCCESS);
assert(i == 0);
/* INT_MAX. */
sprintf(s, "%d", INT_MAX);
assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
assert(i == INT_MAX);
/* INT_MIN. */
sprintf(s, "%d", INT_MIN);
assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
assert(i == INT_MIN);
/* Leading and trailing space. */
assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE);
assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE);
/* Trash characters. */
assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE);
assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE);
/* int overflow.
*
* `if` needed to avoid undefined behaviour
* on `INT_MAX + 1` if INT_MAX == LONG_MAX.
*/
if (INT_MAX < LONG_MAX) {
sprintf(s, "%ld", (long int)INT_MAX + 1L);
assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
}
/* int underflow */
if (LONG_MIN < INT_MIN) {
sprintf(s, "%ld", (long int)INT_MIN - 1L);
assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
}
/* long overflow */
sprintf(s, "%ld0", LONG_MAX);
assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
/* long underflow */
sprintf(s, "%ld0", LONG_MIN);
assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
return EXIT_SUCCESS;
}
str2int()
. Pedantic: sử dụng isspace((unsigned char) s[0])
.
(unsigned char)
dàn diễn viên có thể tạo ra sự khác biệt?
l > INT_MAX
và l < INT_MIN
là so sánh số nguyên vô nghĩa vì một trong hai kết quả luôn luôn là sai. Điều gì xảy ra nếu tôi thay đổi chúng l >= INT_MAX
và l <= INT_MIN
để xóa các cảnh báo? Trên ARM C, dài và int là các kiểu dữ liệu Cơ bản có
l >= INT_MAX
phát sinh chức năng không chính xác: Ví dụ trả về STR2INT_OVERFLOW
với đầu vào "32767"
và 16 bit int
. Sử dụng một biên dịch có điều kiện. Ví dụ .
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
Sẽ tốt hơn là if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
để cho phép gọi mã để sử dụng errno
trên int
out-of-range. Tương tự cho if (l < INT_MIN...
.
Đừng sử dụng các chức năng từ ato...
nhóm. Chúng bị hỏng và hầu như vô dụng. Một giải pháp tốt hơn vừa phải sẽ được sử dụng sscanf
, mặc dù nó cũng không hoàn hảo.
Để chuyển đổi chuỗi thành số nguyên, các hàm từ strto...
nhóm nên được sử dụng. Trong trường hợp cụ thể của bạn, nó sẽ là strtol
chức năng.
sscanf
thực sự có hành vi không xác định nếu nó cố gắng chuyển đổi một số bên ngoài phạm vi của loại của nó (ví dụ sscanf("999999999999999999999", "%d", &n)
:).
atoi
không cung cấp phản hồi thành công / thất bại có ý nghĩa và có hành vi không xác định khi tràn. sscanf
cung cấp phản hồi thành công / thất bại của các loại (giá trị trả về, đó là điều làm cho nó "tốt hơn vừa phải"), nhưng vẫn có hành vi không xác định khi tràn. Chỉ strtol
là một giải pháp khả thi.
sscanf
. (Mặc dù tôi thú nhận đôi khi tôi sử dụng atoi
, thường là cho các chương trình mà tôi không mong đợi tồn tại hơn 10 phút trước khi tôi xóa nguồn.)
Bạn có thể mã một ít atoi () cho vui:
int my_getnbr(char *str)
{
int result;
int puiss;
result = 0;
puiss = 1;
while (('-' == (*str)) || ((*str) == '+'))
{
if (*str == '-')
puiss = puiss * -1;
str++;
}
while ((*str >= '0') && (*str <= '9'))
{
result = (result * 10) + ((*str) - '0');
str++;
}
return (result * puiss);
}
Bạn cũng có thể làm cho nó đệ quy có thể cũ trong 3 dòng =)
code
((* str) - '0')code
"----1"
2) Có hành vi không xác định với int
tràn khi kết quả phải INT_MIN
. Hãy xem xétmy_getnbr("-2147483648")
Chỉ muốn chia sẻ một giải pháp cho lâu không dấu.
unsigned long ToUInt(char* str)
{
unsigned long mult = 1;
unsigned long re = 0;
int len = strlen(str);
for(int i = len -1 ; i >= 0 ; i--)
{
re = re + ((int)str[i] -48)*mult;
mult = mult*10;
}
return re;
}
const char *
.
48
có nghĩa là gì? Bạn có cho rằng đó là giá trị của '0'
nơi mã sẽ chạy? Xin đừng gây ra những giả định rộng lớn như vậy trên thế giới!
'0'
như bạn nên.
int atoi(const char* str){
int num = 0;
int i = 0;
bool isNegetive = false;
if(str[i] == '-'){
isNegetive = true;
i++;
}
while (str[i] && (str[i] >= '0' && str[i] <= '9')){
num = num * 10 + (str[i] - '0');
i++;
}
if(isNegetive) num = -1 * num;
return num;
}
Bạn luôn có thể cuộn của riêng bạn!
#include <stdio.h>
#include <string.h>
#include <math.h>
int my_atoi(const char* snum)
{
int idx, strIdx = 0, accum = 0, numIsNeg = 0;
const unsigned int NUMLEN = (int)strlen(snum);
/* Check if negative number and flag it. */
if(snum[0] == 0x2d)
numIsNeg = 1;
for(idx = NUMLEN - 1; idx >= 0; idx--)
{
/* Only process numbers from 0 through 9. */
if(snum[strIdx] >= 0x30 && snum[strIdx] <= 0x39)
accum += (snum[strIdx] - 0x30) * pow(10, idx);
strIdx++;
}
/* Check flag to see if originally passed -ve number and convert result if so. */
if(!numIsNeg)
return accum;
else
return accum * -1;
}
int main()
{
/* Tests... */
printf("Returned number is: %d\n", my_atoi("34574"));
printf("Returned number is: %d\n", my_atoi("-23"));
return 0;
}
Điều này sẽ làm những gì bạn muốn mà không lộn xộn.
strto...
họ chức năng. Chúng là hàng xách tay và tốt hơn đáng kể.
0x2d, 0x30
thay vì '-', '0'
. Không cho phép '+'
ký. Tại sao (int)
đúc trong (int)strlen(snum)
? UB nếu đầu vào là ""
. UB khi kết quả là INT_MIN
do int
tràn vớiaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Chức năng này sẽ giúp bạn
int strtoint_n(char* str, int n)
{
int sign = 1;
int place = 1;
int ret = 0;
int i;
for (i = n-1; i >= 0; i--, place *= 10)
{
int c = str[i];
switch (c)
{
case '-':
if (i == 0) sign = -1;
else return -1;
break;
default:
if (c >= '0' && c <= '9') ret += (c - '0') * place;
else return -1;
}
}
return sign * ret;
}
int strtoint(char* str)
{
char* temp = str;
int n = 0;
while (*temp != '\0')
{
n++;
temp++;
}
return strtoint_n(str, n);
}
Tham chiếu: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
atoi
bạn bè là nếu có tràn, đó là hành vi không xác định. Chức năng của bạn không kiểm tra điều này. strtol
và bạn bè làm.
Ok, tôi đã có cùng một vấn đề. Tôi đã đưa ra giải pháp này. Nó hoạt động tốt nhất cho tôi. Tôi đã thử atoi () nhưng không hiệu quả với tôi. Đây là giải pháp của tôi:
void splitInput(int arr[], int sizeArr, char num[])
{
for(int i = 0; i < sizeArr; i++)
// We are subtracting 48 because the numbers in ASCII starts at 48.
arr[i] = (int)num[i] - 48;
}
//I think this way we could go :
int my_atoi(const char* snum)
{
int nInt(0);
int index(0);
while(snum[index])
{
if(!nInt)
nInt= ( (int) snum[index]) - 48;
else
{
nInt = (nInt *= 10) + ((int) snum[index] - 48);
}
index++;
}
return(nInt);
}
int main()
{
printf("Returned number is: %d\n", my_atoi("676987"));
return 0;
}
nInt = (nInt *= 10) + ((int) snum[index] - 48);
so với nInt = nInt*10 + snum[index] - '0';
if(!nInt)
không cần thiết.
Trong C ++, bạn có thể sử dụng một chức năng như vậy:
template <typename T>
T to(const std::string & s)
{
std::istringstream stm(s);
T result;
stm >> result;
if(stm.tellg() != s.size())
throw error;
return result;
}
Điều này có thể giúp bạn chuyển đổi bất kỳ chuỗi nào thành bất kỳ loại nào như float, int, double ...
Có, bạn có thể lưu trữ số nguyên trực tiếp:
int num = 45;
Nếu bạn phải phân tích một chuỗi, atoi
hoặc strol
sẽ giành chiến thắng trong cuộc thi "số lượng mã ngắn nhất".
strtol()
thực sự đòi hỏi một số lượng mã hợp lý. Nó có thể trở lại LONG_MIN
hoặc LONG_MAX
hoặc nếu đó là giá trị chuyển đổi thực tế hoặc nếu có một underflow hoặc tràn, và nó có thể trở về 0 hoặc nếu đó là giá trị thực tế hoặc nếu không có số để chuyển đổi. Bạn cần đặt errno = 0
trước cuộc gọi và kiểm tra endptr
.