1. 程式人生 > >scanf sscanf,sscanf_s及其相關用法

scanf sscanf,sscanf_s及其相關用法

#include<stdio.h>

 定義函式 int sscanf (const char *str,const char *format,........);

函式說明 
 sscanf()會將引數str的字串根據引數format字串來轉換並格式化資料。格式轉換形式請參考scanf()。轉換後的結果存於對應的引數內。

返回值 成功則返回引數數目,失敗則返回-1,錯誤原因存於errno中。 返回0表示失敗    否則,表示正確格式化資料的個數    例如:sscanf(str,"%d%d%s", &i,&i2,&s);    如果三個變成都讀入成功會返回3。    如果只讀入了第一個整數到i則會返回1。證明無法從str讀入第二個整數。

           main() 
            { 
            int i; 
            unsigned intj; 
            char input[]=”10 0x1b aaaaaaaa bbbbbbbb”; 
            chars[5]; 
           sscanf(input,”%d %x %5[a-z] %*s %f”,&i,&j,s,s); 
            printf(“%d%d %s ”,i,j,s); 
            }

           執行 10 27 aaaaa

大家都知道sscanf是一個很好用的函式,利用它可以從字串中取出整數、浮點數和字串等等。它的使用方法簡單,特別對於整數和浮點數來說。但新手可能並不知道處理字串時的一些高階用法,這裡做個簡要說明吧。

  1.常見用法。

  charstr[512]={0};
  sscanf("123456","%s",str);
  printf("str=%s",str);

  2.取指定長度的字串。如在下例中,取最大長度為4位元組的字串。

  sscanf("123456","%4s",str);
  printf("str=%s",str);

  3.取到指定字元為止的字串。如在下例中,取遇到空格為止字串。

  sscanf("123456abcdedf","%[^]",str);
  printf("str=%s",str);

 4.取僅包含指定字符集的字串。如在下例中,取僅包含1到9和小寫字母的字串。

  sscanf("123456abcdedfBCDEF","%[1-9a-z]",str);
  printf("str=%s",str);

  5.取到指定字符集為止的字串。如在下例中,取遇到大寫字母為止的字串。

  sscanf("123456abcdedfBCDEF","%[^A-Z]",str);
  printf("str=%s",str);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

可以用如下程式碼將字串形式的ip地址轉換為四個整數:

1.           char * inputIp  

2.           int ip[4];  

3.           sscanf_s(inputIp, "%d.%d.%d.%d", &ip[0], &ip[1],&ip[2],&ip[3]);  

     注意sscanf_s,當讀入的型別是整數或其它長度可以確定的型別時,不能在型別後面跟上長度,但是對於字串型別(char *)長度無法得知則必須在型別後面明確指出字串的最大長度(即可以容納的空間)。舉例如下:

1.           // crt_sscanf_s.c  

2.           // This program uses sscanf_s to read data items  

3.           // from a string named tokenstring, then displays them.  

4.            

5.           #include <stdio.h>  

6.           #include <stdlib.h>  

7.             

8.           int main( void )  

9.           {  

10.            char  tokenstring[] = "15 12 14...";  

11.            char  s[81];  

12.            char  c;  

13.            int   i;  

14.            float fp;  

15.           

16.            // Input various data from tokenstring:  

17.            // max 80 character string plus NULL terminator  

18.            sscanf_s( tokenstring, "%s", s, _countof(s) );  

19.            sscanf_s( tokenstring, "%c", &c, sizeof(char) );  

20.            sscanf_s( tokenstring, "%d", &i );  

21.            sscanf_s( tokenstring, "%f", &fp );  

22.           

23.            // Output the data read  

24.            printf_s( "String    = %s\n", s );  

25.            printf_s( "Character = %c\n", c );  

26.            printf_s( "Integer:  = %d\n", i );  

27.            printf_s( "Real:     = %f\n", fp );  

28.         }  

      對於多個字串讀入的情況,程式碼如下:

1.           sscanf_s(inputString, "%s.%s.%s.%s", s1, s1.length, s2, s2.length, s3, s3.length, s4, s4.length); 

sscanf 函式非常好用,居然我以前一直不知道這個函式。最近朋友用VS2008寫程式時用到這個函式的安全版本 sscanf_s ,卻出現異常問題,無法解析字串不說,還會崩潰。

int sscanf_s(
   const char *buffer,
   const char *format [,
      argument ] ...
);

這是MSDN裡面關於函式的定義,沒有繼續詳細檢視後面的備註,以及例項的情況下。根本感覺不到sscanf 與 sscanf_s 的區別。以為仍然是像sscanf一樣使用,以致出現奇怪問題。

Example:

// crt_sscanf_s.c

// This program usessscanf_s to read data items

// from a stringnamed tokenstring, then displays them.

#include<stdio.h>

#include<stdlib.h>

int main( void )

{

   char tokenstring[] = "15 12 14...";

   char s[81];

   char c;

   int  i;

   float fp;

   // Input various data from tokenstring:

   // max 80 character string plus NULLterminator

   sscanf_s( tokenstring, "%s", s,_countof(s) );

   sscanf_s( tokenstring, "%c",&c, sizeof(char) );

   sscanf_s( tokenstring, "%d",&i );

   sscanf_s( tokenstring, "%f",&fp );

   // Output the data read

   printf_s( "String    = %s\n", s );

   printf_s( "Character = %c\n", c );

   printf_s( "Integer:  = %d\n", i );

   printf_s( "Real:     = %f\n", fp );

}

直到看完整個文件,看到這個例項,才發現原來還有貓膩!sscanf_s 取值的時候,需要在每個取值後面指定取值的最大大小。

在使用VS2005編譯一個程式時,出現了很多警告,說是用的函式是不安全的,應當使用安全版本,即函式名稱增加“_s”的版本。
 
 警告內容:
 warning C4996: 'sscanf': This function or variable may be unsafe.Consider using sscanf_s instead.
 
據瞭解,“_s”版本函式是微軟後來對c++做得擴充套件,用來替代原先不安全的函式,例如:printf、scanf、strcpy、fopen等等。

詳細參考:
ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_vccrt/html/d9568b08-9514-49cd-b3dc-2454ded195a3.htm

原來安全版本的函式,對引數和緩衝邊界做了檢查,增加了返回值和丟擲異常。這樣增加了函式的安全性,減少了出錯的機率。
同時這也意味著在使用這些函式時,有時你不得不輸入更多的關於緩衝區大小的引數,多敲幾下鍵盤能換來更少的麻煩,值得!

下面總結了sscanf的以及sscanf_s的常用方法,也體現了“_s”版本函式與原函式的特別之處:

1、sscanf和scanf的不同是輸入來源,前者是一個字串,後者則是標準輸入裝置

2、sscanf的使用,以解析時間字串為例,將字串“2009-01-02_11:12:13”解析為整型年月日時分秒

//定義
 char cc;
 tm tm_temp={0};
 string stime("2009-01-02_11:12:13");

//(1) 必須嚴格按照分隔符形式匹配填寫,若遇到不匹配項則終止解析


 sscanf(stime.c_str(), "%4d-%2d-%2d_%2d:%2d:%2d",
  &tm_temp.tm_year,
  &tm_temp.tm_mon,
  &tm_temp.tm_mday,
  &tm_temp.tm_hour,
  &tm_temp.tm_min,
  &tm_temp.tm_sec
  );
  
//(2) 可以不按照分割符號形式填寫,字元數必須一致,例如可以正確解析“2009/01/02_11:12:13”


 sscanf(stime.c_str(), "%4d%c%2d%c%2d%c%2d%c%2d%c%2d",
  &tm_temp.tm_year, &cc,
  &tm_temp.tm_mon, &cc,
  &tm_temp.tm_mday, &cc,
  &tm_temp.tm_hour, &cc,
  &tm_temp.tm_min, &cc,
  &tm_temp.tm_sec
  );
 
//(3) 可以不按照分割符號形式填寫,字元數必須一致,同上,%1s可以等同於%c


 sscanf(stime.c_str(), "%4d%1s%2d%1s%2d%1s%2d%1s%2d%1s%2d",
  &tm_temp.tm_year, &cc,
  &tm_temp.tm_mon, &cc,
  &tm_temp.tm_mday, &cc,
  &tm_temp.tm_hour, &cc,
  &tm_temp.tm_min, &cc,
  &tm_temp.tm_sec
  );

//(4) 可以不按照分割符形式和數量填寫,型別必須一致,例如可以正確解析“2009/01/02___11:12:13”
//這裡使用了sscanf的正則表示式,與通用的正則表示類似但不完全相同,%*c表示忽略連續多個字元


 sscanf(stime.c_str(), "%4d%*c%2d%*c%2d%*c%2d%*c%2d%*c%2d",
  &tm_temp.tm_year,
  &tm_temp.tm_mon,
  &tm_temp.tm_mday,
  &tm_temp.tm_hour,
  &tm_temp.tm_min,
  &tm_temp.tm_sec
  );
  
3、sscanf_s的使用

 //定義
 char cc[2];
 tm tm_temp={0};
 string stime("2009-01-02_11:12:13");

//(1) 與sscanf第一種方法相同,可以使用"%4d-%2d-%2d_%2d:%2d:%2d"格式匹配解析


 sscanf_s(stime.c_str(), "%4d-%2d-%2d_%2d:%2d:%2d",
   &tm_temp.tm_year,
   &tm_temp.tm_mon,
   &tm_temp.tm_mday,
   &tm_temp.tm_hour,
   &tm_temp.tm_min,
   &tm_temp.tm_sec
   );
  
//(2) 使用%c格式對資料解析時,必須對相應的緩衝區增加長度引數,否則將會出錯


 sscanf_s(stime.c_str(), "%4d%c%2d%c%2d%c%2d%c%2d%c%2d",
  &tm_temp.tm_year, &cc, 1,
  &tm_temp.tm_mon, &cc, 1,
  &tm_temp.tm_mday, &cc, 1,
  &tm_temp.tm_hour, &cc, 1,
  &tm_temp.tm_min, &cc, 1,
  &tm_temp.tm_sec
  );
  
//(3) 使用%s格式對資料解析時,緩衝長度必須大於字串長度,否則不予解析


 sscanf_s(stime.c_str(), "%4d%1s%2d%1s%2d%1s%2d%1s%2d%1s%2d",
   &tm_temp.tm_year, &cc, 2,
   &tm_temp.tm_mon, &cc, 2,
   &tm_temp.tm_mday, &cc, 2,
   &tm_temp.tm_hour, &cc, 2,
   &tm_temp.tm_min, &cc, 2,
   &tm_temp.tm_sec
   );

//(4) 與sscanf一樣,sscanf_s同樣支援正則表示式


 sscanf_s(stime.c_str(), "%4d%*c%2d%*c%2d%*c%2d%*c%2d%*c%2d",
  &tm_temp.tm_year,
  &tm_temp.tm_mon,
  &tm_temp.tm_mday,
  &tm_temp.tm_hour,
  &tm_temp.tm_min,
  &tm_temp.tm_sec
  );
  
通過以上對比sscanf與sscanf_s的使用,可以看出後者對緩衝區安全有了更多的考慮,從而避免了許多不經意的煩惱。