国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

淺談C語言字符串格式化

2012-08-18 02:00:34彭秋生
湖南生態(tài)科學學報 2012年1期
關鍵詞:格式化字符串C語言

雷 金, 彭秋生

(湖南環(huán)境生物職業(yè)技術學院護理學院,湖南衡陽421005)

談起C語言,稍懂程序設計的人都不陌生.因為在眾多程序設計語言中,C語言是基礎.它功能強大,效率高,是目前眾多計算機語言中舉世公認的優(yōu)秀的結構程序設計語言之一.它既可以作為工作系統(tǒng)設計語言,編寫系統(tǒng)應用程序,如unix操作系統(tǒng);也可以作為應用程序設計語言,編寫不依賴計算機硬件的應用程序.因此,它的應用范圍廣泛,不僅僅是在軟件開發(fā)上,而且各類科研都需要用到C語言.

C語言的數據類型豐富,但基本的數據類型很少,僅僅包括:整型、浮點型和字符型等.對字符型數據的處理主要集中在字符型數組,也就是字符串的操作上.實際上,在普通的程序設計中,絕大部分的程序代碼都是在對字符串進行處理,只有少數的程序代碼是對整數和單(雙)精度數據進行操作.況且,如果沒有專門設計的處理器和系統(tǒng),大數值和高精度的數值計算也只有依靠字符串才能達到要求.因為整型和浮點型變量的最大值和精度都是有限的,只有字符串變量的長度才是接近無限的(只要內存足夠).由此可見,高效的字符串處理和操作顯得尤其重要.

C語言提供了大量的字符串操作函數.在普通的教學過程中,介紹得最多的通常是字符串拷貝strcpy、字符串追加strcat,字符串比較strcmp,求字符串長度strlen等函數.但這幾個函數遠遠無法滿足我們的字符串處理要求.比如:我們需要一個20位的定長序號,使用上述函數的一個較為常見的實現如下:

顯然,對于這類問題,上述幾個函數處理起來顯得有點麻煩,但是這類問題又具有普通性.這個時候,字符串格式化函數處理起來就方便得多.

1 字符串格式化輸出

最常見的是格式化輸出函數printf,但它是將數據格式化輸出到標準輸出設備,一般是顯示器.而大多數情況下,我們并不需要輸出到顯示設備,而是輸出到某一個指定的字符串變量.有沒有一個將數據格式化后輸入到字符串變量的函數呢?答案是肯定的.sprintf函數就能滿足我們的要求,它的用法和返回值與printf幾乎一模一樣,只是比后者多了一個字符串指針參數.對兩個函數的原型進行比較就很容易看出這一點:

相比改進前的函數,這次的代碼要簡潔多了.事實上,除了printf和sprintf外,還有好幾個其它功能類似的函數,如:?fprintf、snprintf、vsnprintf?、vprintf、vsprintf、vfprintf等,這類函數統(tǒng)稱為 printf類函數,屬于格式化輸入輸出的一部分,均定義在頭文件 stdio.h中.

sprintf函數功能強大,很多情況下可以用它來達到與 strcpy,strcat,strlen 同樣的效果.如:

① int nlength=sprintf(strDest,"%s",strSrc)

將字符串strSrc拷貝至strDest,同時返回所拷貝字符串的長度.顯然,這里既實現了數據拷貝,同時又返回出了字符串的長度.

② int nlength = sprintf(strDest,"%s%s",strDest,strSrc)

將字符串strDest和strSrc合并后,再賦給strD-est,效果與 strcat(strDest,strSrc)一致.

函數sprintf的功能當然不限于此,前文提到它與printf的功能幾乎是一樣的,唯一的區(qū)別就是sprintf將數據格式化后將結果賦給指定字符串變量.但是功能強大是以犧牲效率為代價的,sprintf功能強大,但效率卻與strcpy,strcat,strlen相差甚遠.

2 sprintf工作原理及可變參數

有時候不得不承認,printf類函數功能的確很強大.不僅能自動識別格式化串format,還能自動判斷參數的個數和類型.前者我們暫不討論,對于后者,說起來其實一點也不神秘,就是C語言的可變參數在起作用.我們簡單的來實現一下 sprintf函數,如下:

參數 str,format稱為固定參數,后面的“...”稱為可變參數,在實際函數調用時,可以用0個或多個實際參數來替換它.sprintf對于可變參數的處理流程采用了C語言中最通用的模式,如下:

①定義一個va_list變量ap,用來作為指向可變參數列表的指針.

②調用va_start宏初始化剛定義的va_list變量ap,使ap指向可變參數列表中第一個可變參數的地址.va_start宏的第二個參數必須是最后一個固定參數(非可變參數)的地址,這里為format.

③調用vsprintf函數將可變參數按format格式化后,賦給str變量.

④最后調用va_end宏結束可變參數的獲取,釋放指針ap,使它不再指向參數列表.

實際上,與C語言中用來可變參數相關的還有一個宏va_arg(ap,type),它的作用是獲取ap所指向的可變參數的值,并將它強制轉換為type類型后作為返回值返回,同時移動指針ap指向下一個可變參數的地址,以便后續(xù)處理.在大多數處理程序中,需要循環(huán)調用va_arg來獲取可變參數的值,以便進行相應的處理,只是sprintf函數將這個處理過程放在了vsprintf函數中.顯然,vsprintf是sprintf的核心所在,sprintf僅僅是vsprintf封裝好的一個對外接口函數,了解vsprintf的工作原理就顯得很重要.函數vsprintf原型如下:

int vsprintf(char*str,const char*format,va_list arg);

函數vsprintf實現起來比較復雜,這也是影響sprintf效率的原因之一.為了提高工作效率,vsprintf并不是在將所有可變參數arg取出,按格式化串format所規(guī)定的格式進行組裝后,一次性將結果寫入目標串str,而是在對format串解析過程中,逐步取出可變參數值,將格式化的結果同步寫入目標串str.也就是說,函數vsprintf是一邊解析格式化串format,一邊將格式化結果寫入目標串的.這一點很重要,它決定了在sprintf函數中,除了第一個可變參數外,其它可變參數決不能與目標參數相同,否則,會引起意想不到的結果.下面這種寫法是錯誤的:

sprintf(strDest,"%s%s",strSrc,strDest)

這樣的語句會產生什么樣的結果呢?sprintf先將strSrc的值寫入目標參數strDest,然后從最末參數strDest所指向的地址開始取值追加到目標參數strDest的后部,但此時,strDest指向的內容已被改變成了strSrc的值,因此strDest中會重復出現strSrc的值,這顯然不是我們想要的結果.

我們用下面的代碼來驗證一下:

這段程序輸出的結果是:strDest=[world,world,world],很好地驗證了我們的結論.

3 構造格式化輸出函數

了解了sprintf函數的工作原理后,我們就可以構造自己的格式化輸出函數.

首先看看spinrtf函數有什么地方可以改進!我們發(fā)現strcpy,strcat等函數的返回值并不是整數,而是一個指向目標串的指針.這樣做的目的是為了便于函數的連續(xù)調用,如:

strcpy(strDest,strcat(strSrc1,strSrc2));

多個操作在同一條語句中完成,簡潔又高效.對sprintf函數來說,這個特性是很容易滿足的,對它進行稍微改進就可以了.改造后函數如下:

參數len是為了不丟棄原有返回值,特將其放入函數參數中返回的.但更多的時候,格式化后字符串的長度并不受人關注,為了簡化函數原型,方便函數調用,可以將字符串長度參數去掉.去掉長度參數的函數如下:

若有時確實需要用到格式化后字符串的長度,也可以采用如下方式獲取:

strlen(mystr(strDest,format,para1,para2,…));

改前文提到sprintf函數屬于格式化輸入輸出類的函數,性能上不能與strcpy等同日而語.因此,在系統(tǒng)核心模塊中,應該避免多次使用sprintf,否則,可能會影響系統(tǒng)的系統(tǒng),形成系統(tǒng)瓶頸.很多系統(tǒng)提供的公共函數中一般不帶可變參數,而是直接提供字符串形式的參數.如:日志打印函數 ErrLogPrt,假設原型如下:

void LogPrt(const char* sErrMsg);

對于疊加在系統(tǒng)上的某一個具體的應用程序而言,性能上沒有核心模塊這么嚴格的要求.而且,當程序出錯,我們希望日志越多、越清晰更好,更好能夠將與出錯有關的信息全部打印出來,如出錯源文件名,源代碼行,以及相關的變量值等.當然使用mystr可以做到這一點,如下:

char sErrMsg[2048];

memset(sErrMsg,0,sizeof(sErrMsg));/* 初始化字符串*/

mystr(sErrMsg,"查詢數據庫出錯,文件:%s,行號:%ld,查詢序號:%s,名稱:%s",

__FILE__,__LINE__,nIndex,sName);

LogPrt(sErrMsg);

顯然,每次打印一個日志都要寫多行代碼,顯得有些煩瑣.既然mystr能夠將目標串的地址返回,為什么不直接使用返回值!我們改造一下:

char sErrMsg[2048];

memset(sErrMsg,0,sizeof(sErrMsg));

LogPrt(mystr(sErrMsg,"查詢數據庫出錯,文件:%s,行號:%ld,查詢序號:%s,名稱:%s",

__FILE__,__LINE__,nIndex,sName);

代碼減少了一行,但改善程度極其有限,如果能再將sErrMsg變量去掉就好了,因為它在這里沒有起多少作用.那么,可以再將 mystr函數改進一下:

C語言函數內部的局部變量,只在函數內部起作用,在調用完函數后,其占用空間會自動釋放,格式化后字符串的值無法獲取.因此,mystr函數內部采用靜態(tài)局部變量,使其在整個應用程序運行過程中一直有效.

除了返回值外,現在的函數跟printf函數用法完全一樣了.任何接解C語言的人,只要知道printf的用法,也就知道m(xù)ystr的用法.好了,現在的函數調用起來方便多了,一條語句就可以實現日志打印,如下:

LogPrt(mystr("查詢數據庫出錯,文件:%s,行號:%ld,查詢序號:%s,名稱:%s",

__FILE__,__LINE__,nIndex,sName);實際上,還可以通過宏定義的方式,將函數進行進一步封裝,放在頭文件中,以供其它源文件共用.也可以通過單獨編譯成函數庫的方式,源代碼可以直接調用該函數,編譯時只需將庫鏈接過來即可.

4 總結

本文首先描述了格式式化輸出函數sprintf的用法,其次對該函數的實現原理和C語言可變參數的工作原理進行了較為詳細的闡述,并在此基礎上,根據sprintf函數的工作原理構造了一個方便通用的格式化輸出函數mystr.該函數的用法與大家熟悉的printf用法一致,非常容易掌握,為數據數據格式化操作提供了一條便捷有效的處理途徑.

[1] 譚浩強.C語言程序設計[M]北京:清華大學出版社,2010.

[2] Eric Huss.The C Library Reference Guide.http://www.acm.uiuc.edu/webmonkeys/book/c_guide/.

[3] c語言可變參數(va_start,va_end)(轉載)http://www.360doc.com/content/07/0423/16/3630_462811.shtml.

猜你喜歡
格式化字符串C語言
現代人守則:昏死之前請把手機格式化
基于Visual Studio Code的C語言程序設計實踐教學探索
計算機教育(2020年5期)2020-07-24 08:52:56
基于C語言的計算機軟件編程
電子制作(2018年16期)2018-09-26 03:27:08
高職高專院校C語言程序設計教學改革探索
格式化
詩林(2016年5期)2016-10-25 07:51:39
論子函數在C語言數據格式輸出中的應用
一種新的基于對稱性的字符串相似性處理算法
依據字符串匹配的中文分詞模型研究
一種針對Java中字符串的內存管理方案
樟树市| 敖汉旗| 黎川县| 明溪县| 门头沟区| 横山县| 资中县| 辽阳县| 沙坪坝区| 博野县| 诏安县| 庆城县| 阿拉善盟| 上饶市| 天全县| 游戏| 睢宁县| 库伦旗| 馆陶县| 盘锦市| 通州区| 通道| 当涂县| 乌兰察布市| 赣榆县| 区。| 南宁市| 阳江市| 沂源县| 花莲市| 宜城市| 柞水县| 通城县| 靖州| 阿巴嘎旗| 安阳县| 佛冈县| 海口市| 休宁县| 苍溪县| 夹江县|