ฉันพยายามค้นหาว่ามีวิธีอื่นในการแปลงสตริงเป็นจำนวนเต็มใน C หรือไม่
ฉันทำรูปแบบต่อไปนี้เป็นประจำในรหัสของฉัน
char s[] = "45";
int num = atoi(s);
ดังนั้นจะมีวิธีที่ดีกว่าหรืออีกวิธีหนึ่งหรือไม่
ฉันพยายามค้นหาว่ามีวิธีอื่นในการแปลงสตริงเป็นจำนวนเต็มใน C หรือไม่
ฉันทำรูปแบบต่อไปนี้เป็นประจำในรหัสของฉัน
char s[] = "45";
int num = atoi(s);
ดังนั้นจะมีวิธีที่ดีกว่าหรืออีกวิธีหนึ่งหรือไม่
คำตอบ:
นอกจากนี้strtol
ที่ดี IMO นอกจากนี้ฉันยังชอบที่strtonum
จะใช้ดังนั้นถ้าคุณมีมัน (แต่จำไว้ว่ามันไม่ได้พกพา):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
คุณอาจสนใจstrtoumax
และstrtoimax
เป็นฟังก์ชันมาตรฐานใน C99 ตัวอย่างเช่นคุณสามารถพูดได้:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
อย่างไรก็ตามอยู่ห่างจากatoi
:
การเรียก atoi (str) จะเทียบเท่ากับ:
(int) strtol(str, (char **)NULL, 10)
ยกเว้นว่าการจัดการข้อผิดพลาดอาจแตกต่างกัน ถ้าค่าไม่สามารถแสดงพฤติกรรมจะไม่ได้กำหนด
strtonum
อะไรเพื่อ ฉันได้รับคำเตือนเรื่องการประกาศโดยปริยาย
#<stdlib.h>
ในระบบที่มันใช้ได้ก็ควรจะประกาศใน อย่างไรก็ตามคุณสามารถใช้strtoumax
ทางเลือกมาตรฐาน
strtol
โซลูชันพื้นฐานที่แข็งแกร่ง C89
ด้วย:
atoi
ครอบครัว)strtol
(เช่นไม่มีช่องว่างนำหน้าหรือถังขยะต่อท้าย)#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;
}
GitHub ต้นน้ำ
อ้างอิงจาก: https://stackoverflow.com/a/6154614/895245
str2int()
ดี อวดความรู้: isspace((unsigned char) s[0])
การใช้งาน
(unsigned char)
ไหมว่าทำไมนักแสดงถึงสร้างความแตกต่าง?
l > INT_MAX
และl < INT_MIN
เป็นการเปรียบเทียบจำนวนเต็มแบบไม่มีจุดหมายเนื่องจากผลลัพธ์ใด ๆ เป็นเท็จเสมอ จะเกิดอะไรขึ้นถ้าฉันเปลี่ยนพวกเขาเป็นl >= INT_MAX
และl <= INT_MIN
เพื่อล้างคำเตือน? บนแขน C, ยาวและintเป็น 32 บิตลงนามชนิดข้อมูลพื้นฐานใน ARM C และ C ++
l >= INT_MAX
incurs ฟังก์ชันการทำงานที่ไม่ถูกต้อง: ตัวอย่างกลับมาSTR2INT_OVERFLOW
ด้วยการป้อนข้อมูล"32767"
และ int
16 ใช้การคอมไพล์แบบมีเงื่อนไข ตัวอย่าง
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
จะดีกว่าที่เป็นif (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
ที่จะอนุญาตให้เรียกรหัสที่จะใช้errno
ในการint
ออกจากช่วง if (l < INT_MIN...
สำหรับเดียวกัน
อย่าใช้ฟังก์ชั่นจากato...
กลุ่ม สิ่งเหล่านี้แตกและไร้ประโยชน์อย่างแท้จริง ทางออกที่ดีกว่าในระดับปานกลางคือการใช้งานsscanf
แม้ว่ามันจะไม่สมบูรณ์ก็ตาม
ในการแปลงสตริงเป็นจำนวนเต็มstrto...
ควรใช้ฟังก์ชันจากกลุ่ม ในกรณีเฉพาะของคุณมันจะเป็นstrtol
ฟังก์ชั่น
sscanf
ที่จริงแล้วมีพฤติกรรมที่ไม่ได้กำหนดหากพยายามแปลงตัวเลขที่อยู่นอกช่วงของประเภท (ตัวอย่างเช่นsscanf("999999999999999999999", "%d", &n)
)
atoi
ไม่ให้ข้อเสนอแนะความสำเร็จ / ล้มเหลวที่มีความหมายและมีพฤติกรรมที่ไม่ได้กำหนดเกี่ยวกับการล้น sscanf
แสดงความคิดเห็นย้อนกลับที่ประสบความสำเร็จ / ล้มเหลว (ค่าส่งคืนซึ่งเป็นสิ่งที่ทำให้ "ดีขึ้นพอสมควร") แต่ก็ยังมีพฤติกรรมที่ไม่ได้กำหนดในการล้น เพียงstrtol
เป็นโซลูชั่นที่ทำงานได้
sscanf
ฉันแค่อยากจะเน้นปัญหาร้ายแรงอาจเกิดขึ้นกับ (แม้ว่าฉันจะยอมรับว่าบางครั้งฉันก็atoi
มักใช้สำหรับโปรแกรมที่ฉันไม่คาดหวังว่าจะอยู่รอดนานกว่า 10 นาทีก่อนที่ฉันจะลบซอร์ส)
คุณสามารถเขียนโค้ด atoi () เพื่อความสนุกสนาน:
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);
}
นอกจากนี้คุณยังสามารถทำให้มันซ้ำซึ่งสามารถเก่าใน 3 บรรทัด =)
code
((* str) - '0')code
"----1"
2) มีพฤติกรรมที่ไม่ได้กำหนดที่มีล้นเมื่อผลที่ควรจะเป็นint
INT_MIN
พิจารณาmy_getnbr("-2147483648")
เพียงแค่ต้องการแบ่งปันวิธีแก้ปัญหาสำหรับผู้ที่ไม่ได้ลงนามและ
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
หมายความว่าอะไร คุณสมมติว่าเป็นมูลค่าของ'0'
รหัสที่จะทำงานหรือไม่ โปรดอย่าทำข้อสมมุติอย่างกว้าง ๆ บนโลกนี้!
'0'
อย่างที่ควร
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;
}
คุณสามารถม้วนของคุณเอง!
#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;
}
สิ่งนี้จะทำสิ่งที่คุณต้องการโดยไม่เกะกะ
strto...
ฟังก์ชัน พวกมันพกพาสะดวกและดีกว่ามาก
0x2d, 0x30
'-', '0'
ไม่อนุญาตให้'+'
ลงชื่อ ทำไม(int)
หล่อ(int)strlen(snum)
? UB ""
ถ้าใส่เป็น UB เมื่อผลลัพธ์INT_MIN
เกิดจากการint
ล้นด้วยaccum += (snum[strIdx] - 0x30) * pow(10, idx);
ฟังก์ชั่นนี้จะช่วยคุณ
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);
}
Ref: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
atoi
และเพื่อนคือถ้ามีมากเกินไปก็เป็นพฤติกรรมที่ไม่ได้กำหนด ฟังก์ชั่นของคุณไม่ได้ตรวจสอบสิ่งนี้ strtol
และเพื่อนทำ
ตกลงฉันมีปัญหาเดียวกันฉันมาด้วยวิธีนี้มันทำงานได้ดีที่สุดสำหรับฉันฉันลอง atoi () แต่ไม่ได้ผลดีสำหรับฉันดังนั้นนี่คือวิธีแก้ปัญหาของฉัน:
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);
เทียบกับnInt = nInt*10 + snum[index] - '0';
if(!nInt)
ไม่จำเป็น
ใน C ++ คุณสามารถใช้ฟังก์ชันดังกล่าวได้:
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;
}
สิ่งนี้สามารถช่วยคุณแปลงสตริงให้เป็นชนิดใดก็ได้เช่น float, int, double ...
ใช่คุณสามารถเก็บจำนวนเต็มได้โดยตรง:
int num = 45;
หากคุณต้องแยกสตริงatoi
หรือstrol
จะชนะการประกวด "รหัสจำนวนที่สั้นที่สุด"
strtol()
จริง ๆ ต้องมีรหัสจำนวนพอใช้ มันสามารถกลับLONG_MIN
หรือLONG_MAX
อย่างใดอย่างหนึ่งถ้าว่าเป็นค่าที่แปลงที่เกิดขึ้นจริงหรือถ้ามีการ underflow หรือล้นและมันสามารถกลับ 0 ทั้งถ้านั่นคือค่าจริงหรือถ้ามีเป็นจำนวนที่จะแปลง คุณจะต้องตั้งก่อนที่จะเรียกและตรวจสอบerrno = 0
endptr