C語言的本質(26)——C標準庫之數值字串轉換

尹成發表於2014-07-17

C語言提供了幾個標準庫函式,可以將任意型別(整型、長整型、浮點型等)的數字轉換為字串。

#include <stdlib.h>
int atoi(const char *nptr);


atoi把一個字串開頭可以識別成十進位制整數的部分轉換成int型。引數nptr字串,如果第一個非空格字元存在,是數字或者正負號則開始做型別轉換,之後檢測到非數字(包括結束符 \0) 字元時停止轉換,返回整型數。否則,返回零,

例如atoi("123abc")的返回值是123,字串開頭可以有若干空格,例如atoi(" -90.6-")的返回值是-90。如果字串開頭沒有可識別的整數,例如atoi("asdf"),則返回0,而atoi("0***")也返回0,根據返回值並不能區分這兩種情況,所以使用atoi函式不能檢查出錯的情況。下面要講的strtol函式可以設定errno,因此可以檢查出錯的情況,在嚴格的場合下應該用strtol,而atoi用起來更簡便,所以也很常用。

#include<stdlib.h>
#include<stdio.h>
int main(void)
{
         floatn;
         char*str="12345.67";
         n= atoi(str);
         printf("string=%s integer=%f\n",str,n);
         return0;
}

 
#include <stdlib.h>
double atof(const char *nptr);

atof把一個字串開頭可以識別成浮點數的部分轉換成double型,相當於下面要講的strtod(nptr, (char **) NULL);。字串開頭可以識別的浮點數格式和C語言的浮點數常量相同,例如atof("31.4")的返回值是31.4,atof("3.14e+1AB")的返回值也是31.4。atof也不能檢查出錯的情況,而strtod可以。

#include<stdlib.h>
int main()
{
         char*a = "-100.23";
         char*b = "200e-2";
         doublec;
         c= atof(a) + atof(b);
         printf("c=%.2lf\n",c);
         return0;
} 


#include <stdlib.h>
long int strtol(const char *nptr, char**endptr, int base);

返回值:轉換結果,出錯時設定errnostrtol是atoi的增強版,主要體現在這幾方面:

不僅可以識別十進位制整數,還可以識別其它進位制的整數,取決於base引數,比如strtol("0XDEADbeE~~", NULL, 16)返回0xdeadbee的值,strtol("0777~~",NULL, 8)返回0777的值。

endptr是一個傳出引數,函式返回時指向後面未被識別的第一個字元。例如char *pos; strtol("123abc", &pos, 10);,strtol返回123,pos指向字串中的字母a。如果字串開頭沒有可識別的整數,例如char *pos; strtol("ABCabc", &pos, 10);,則strtol返回0,pos指向字串開頭,可以據此判斷這種出錯的情況,而這是atoi處理不了的。

如果字串中的整數值超出long int的表示範圍(上溢或下溢),則strtol返回它所能表示的最大(或最小)整數,並設定errno為ERANGE,例如strtol("0XDEADbeef~~", NULL, 16)返回0x7fffffff並設定errno為ERANGE。

 

#include<stdlib.h>
#include<stdio.h>
int main(void)
{
         char*string,*stopstring;
         doublex;
         intbase;
         longl;
         unsignedlong ul;
         string="3.1415926This stopped it";
         x=strtod(string,&stopstring);
         printf("string=%s\n",string);
         printf("strtod=%f\n",x);
         printf("Stoppedscan at:%s\n",stopstring);
         string="-10110134932This stopped it";
         l=strtol(string,&stopstring,10);
         printf("string=%s\n",string);
         printf("strtol=%ld\n",l);
         printf("Stopped scan at:%s\n",stopstring);
         string="10110134932";
         printf("string=%s\n",string);
         for(base=2;base<=8;base*=2)
         {
                   ul=strtoul(string,&stopstring,base);
                   printf("strtol=%ld(base%d)\n",ul,base);
                   printf("Stoppedscan at:%s\n",stopstring);
         }
         return0;
}

 

回想一下使用fopen的套路if ( (fp = fopen(...)) == NULL) { 讀取errno },fopen在出錯時會返回NULL,因此我們知道需要讀errno,但strtol在成功呼叫時也可能返回0x7fffffff,我們如何知道需要讀errno呢?最嚴謹的做法是首先把errno置0,再呼叫strtol,再檢視errno是否變成了錯誤碼。ManPage上有一個很好的例子:

strtol的出錯處理:

#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
 
int main(int argc, char *argv[])
{
         intbase;
         char*endptr, *str;
         longval;
 
         if(argc < 2) {
                   fprintf(stderr,"Usage: %s str [base]\n", argv[0]);
                   exit(EXIT_FAILURE);
         }
 
         str= argv[1];
         base= (argc > 2) ? atoi(argv[2]) : 10;
 
         errno= 0;  
         val= strtol(str, &endptr, base);
 
         if((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
             || (errno != 0 && val == 0)) {
                   perror("strtol");
                   exit(EXIT_FAILURE);
         }
 
         if(endptr == str) {
                   fprintf(stderr,"No digits were found\n");
                   exit(EXIT_FAILURE);
         }
 
         printf("strtol()returned %ld\n", val);
 
         if(*endptr != '\0')  
                   printf("Furthercharacters after number: %s\n", endptr);
 
         exit(EXIT_SUCCESS);
}


#include <stdlib.h>
double strtod(const char *nptr, char **endptr);

strtod()會掃描引數nptr字串,跳過前面的空格字元,直到遇上數字或正負符號才開始做轉換,到出現非數字或字串結束時('\0')才結束轉換,並將結果返回。若endptr不為NULL,則會將遇到不合條件而終止的nptr中的字元指標由endptr傳回。引數nptr字串可包含正負號、小數點或E(e)來表示指數部分。如123.456或123e-2。

#include<stdlib.h>
#include<stdio.h>
int main(void)
{
   char *endptr;
   char a[] = "12345.6789";
   char b[] = "1234.567qwer";
   char c[] = "-232.23e4";
   printf( "a=%lf\n", strtod(a,NULL) );
   printf( "b=%lf\n", strtod(b,&endptr) );
   printf( "endptr=%s\n", endptr );
   printf( "c=%lf\n", strtod(c,NULL) );
         return0;
}


 

相關文章