C tamsayı dize dönüştürme alternatif bir yol olup olmadığını bulmaya çalışıyorum.
Kodumda düzenli olarak aşağıdakileri düzenlerim.
char s[] = "45";
int num = atoi(s);
Peki, daha iyi ya da başka bir yol var mı?
C tamsayı dize dönüştürme alternatif bir yol olup olmadığını bulmaya çalışıyorum.
Kodumda düzenli olarak aşağıdakileri düzenlerim.
char s[] = "45";
int num = atoi(s);
Peki, daha iyi ya da başka bir yol var mı?
Yanıtlar:
Orada strtol
daha iyi IMO olan. Ayrıca ben bir sevme aldı strtonum
, bu yüzden varsa (ama taşınabilir olmadığını unutmayın) kullanın:
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
Ayrıca ilginizi çekebilir strtoumax
vestrtoimax
bunlar C99'da standart işlevlerdir. Örneğin şöyle diyebilirsiniz:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
Her neyse, uzak durun atoi
:
Çağrı atoi (str) aşağıdakilere eşit olacaktır:
(int) strtol(str, (char **)NULL, 10)
hataların ele alınması farklı olabilir. Değer temsil edilemiyorsa, davranış tanımsızdır .
strtonum
? Örtülü bir bildirim uyarısı alıyorum
#<stdlib.h>
. Ancak, standart strtoumax
alternatifi kullanabilirsiniz .
Sağlam C89 strtol
tabanlı çözüm
İle:
atoi
ailede olduğu gibi)strtol
(ör. önde gelen boşluk veya sondaki çöp karakteri yok)#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()
. Bilgiçlik taslayan: kullanın isspace((unsigned char) s[0])
.
(unsigned char)
fark yaratabileceğini biraz daha açıklayabilir misiniz ?
l > INT_MAX
ve l < INT_MIN
her iki sonuç da her zaman yanlış olduğundan anlamsız tamsayı karşılaştırmasıdır. Uyarıları değiştirir l >= INT_MAX
ve l <= INT_MIN
uyarıları temizlersem ne olur ? ARM C'de, uzun ve int 32-bit imzalı ARM C ve C ++ 'da temel veri türleri
l >= INT_MAX
yanlış işlevsellik için kod değiştirme : STR2INT_OVERFLOW
Giriş "32767"
ve 16 bit ile dönen örnek int
. Koşullu bir derleme kullanın. Örnek .
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
olarak daha iyi olurdu if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
kullanmak için kod çağıran izin errno
üzerine int
dışarı aralık. Aynı if (l < INT_MIN...
.
ato...
Gruptaki işlevleri kullanmayın . Bunlar kırık ve neredeyse işe yaramaz. Mükemmel sscanf
olmasa da, nispeten daha iyi bir çözüm kullanmak olacaktır .
Dizeyi tamsayıya dönüştürmek için strto...
gruptan işlevler kullanılmalıdır. Özel durumunuzda bu strtol
işlev olacaktır .
sscanf
aslında bir sayıyı kendi türünün aralığının dışına dönüştürmeye çalışırsa tanımlanmamış bir davranışı vardır (örneğin, sscanf("999999999999999999999", "%d", &n)
).
atoi
anlamlı bir başarı / başarısızlık geri bildirimi sağlamaz ve taşma konusunda tanımlanmamış bir davranışa sahiptir. sscanf
türlerin başarı / başarısızlık geri bildirimlerini sağlar (dönüş değeri, "orta derecede daha iyi" yapan şeydir), ancak taşma konusunda hala tanımlanmamış bir davranışa sahiptir. Sadece strtol
uygulanabilir bir çözümdür.
sscanf
. (Her ne kadar itiraf etsem de atoi
, genellikle kaynağı silmeden önce 10 dakikadan fazla hayatta kalmayı beklemediğim programlar için kullanıyorum .)
Eğlenmek için biraz atoi () kodlayabilirsiniz:
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);
}
Ayrıca 3 satırda eski olabilir özyinelemeli yapabilirsiniz =)
code
((* str) - '0')code
"----1"
2) int
Sonuç olması gerektiğinde taşma ile tanımlanmamış bir davranışa sahiptir INT_MIN
. Düşününmy_getnbr("-2147483648")
Sadece imzasız uzun bir süre için bir çözüm paylaşmak istedim.
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
anlama geliyor? '0'
Kodun çalışacağı yerin değeri olduğunu mu düşünüyorsunuz? Lütfen dünyaya bu kadar geniş varsayımlar getirmeyin!
'0'
gerektiği gibi kullanı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;
}
Sen her zaman kendi rulo!
#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;
}
Bu, dağınıklık olmadan istediğinizi yapar.
strto...
Fonksiyon ailesini kullanmamak için hiçbir sebep yok . Taşınabilir ve çok daha iyi.
0x2d, 0x30
Bunun yerine kullanmak garip '-', '0'
. İşarete izin vermiyor '+'
. Neden (int)
girmelisin (int)strlen(snum)
? Giriş ise UB ""
. Sonucudur UB INT_MIN
nedeniyle int
taşmaaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Bu işlev size yardımcı olacaktır
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
Ve arkadaşları ile en büyük sorunlarından biri taşma varsa, tanımsız davranış olmasıdır. İşleviniz bunu kontrol etmez. strtol
ve arkadaşlar.
Tamam, ben de aynı sorunu yaşadım. Bu çözümü buldum. Benim için en iyisi çalıştı.
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);
vs. nInt = nInt*10 + snum[index] - '0';
if(!nInt)
gerekli değildir.
C ++ 'da böyle bir işlev kullanabilirsiniz:
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;
}
Bu, herhangi bir dizeyi float, int, double gibi herhangi bir türe dönüştürmenize yardımcı olabilir ...
Evet, tamsayıyı doğrudan saklayabilirsiniz:
int num = 45;
Bir dizeyi ayrıştırmanız gerekiyorsa atoi
veya strol
"en kısa kod miktarı" yarışmasını kazanacaksanız.
strtol()
aslında makul miktarda kod gerektirir. Dönebilir LONG_MIN
ya LONG_MAX
da gerçek dönüştürülmüş değerse veya bir taşma veya taşma varsa ve gerçek değerse veya dönüştürülecek sayı yoksa 0 döndürebilir. errno = 0
Aramadan önce ayarlamanız ve öğesini kontrol etmeniz gerekir endptr
.