日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Unicode简介

發(fā)布時(shí)間:2023/12/29 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unicode简介 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Unicode簡(jiǎn)介

壹佰軟件開(kāi)發(fā)小組??整理編譯??

在第一章中,我已經(jīng)預(yù)告,C語(yǔ)言中在Microsoft Windows程序設(shè)計(jì)中扮演著重要角色的任何部分都會(huì)講述到,您也許在傳統(tǒng)文字模式程序設(shè)計(jì)中還尚未遇到過(guò)這些問(wèn)題。寬字符集和Unicode差不多就是這樣的問(wèn)題。

簡(jiǎn)單地說(shuō),Unicode擴(kuò)展自ASCII字符集。在嚴(yán)格的ASCII中,每個(gè)字符用7位表示,或者計(jì)算機(jī)上普遍使用的每字符有8位寬;而Unicode使用全16位字符集。這使得Unicode能夠表示世界上所有的書寫語(yǔ)言中可能用于計(jì)算機(jī)通訊的字符、象形文字和其它符號(hào)。Unicode最初打算作為ASCII的補(bǔ)充,可能的話,最終將代替它。考慮到ASCII是計(jì)算機(jī)中最具支配地位的標(biāo)準(zhǔn),所以這的確是一個(gè)很高的目標(biāo)。

Unicode影響到了計(jì)算機(jī)工業(yè)的每個(gè)部分,但也許會(huì)對(duì)操作系統(tǒng)和程序設(shè)計(jì)語(yǔ)言的影響最大。從這方面來(lái)看,我們已經(jīng)上路了。Windows NT從底層支持Unicode(不幸的是,Windows 98只是小部分支持Unicode)。先天即被ANSI束縛的C程序設(shè)計(jì)語(yǔ)言通過(guò)對(duì)寬字符集的支持來(lái)支持Unicode。下面將詳細(xì)討論這些內(nèi)容。

自然,作為程序?qū)懽髡?#xff0c;我們通常會(huì)面對(duì)許多繁重的工作。我已試圖透過(guò)使本書中的所有程序「Unicode化」來(lái)減輕負(fù)擔(dān)。其含義會(huì)隨著本章對(duì)Unicode的討論而清晰起來(lái)。

字符集簡(jiǎn)史

雖然不能確定人類開(kāi)始講話的時(shí)間,但書寫已有大約6000年的歷史了。實(shí)際上,早期書寫的內(nèi)容是象形文字。每個(gè)字符都對(duì)應(yīng)于發(fā)聲的字母表則出現(xiàn)于大約3000年前。雖然人們過(guò)去使用的多種書寫語(yǔ)言都用得好好的,但19世紀(jì)的幾個(gè)發(fā)明者還是看到了更多的需求。Samuel F. B. Morse在1838年到1854年間發(fā)明了電報(bào),當(dāng)時(shí)他還發(fā)明了一種電報(bào)上使用的代碼。字母表中的每個(gè)字符對(duì)應(yīng)于一系列短的和長(zhǎng)的脈沖(點(diǎn)和破折號(hào))。雖然其中大小寫字母之間沒(méi)有區(qū)別,但數(shù)字和標(biāo)點(diǎn)符號(hào)都有了自己的代碼。

Morse代碼并不是以其它圖畫的或印刷的象形文字來(lái)代表書寫語(yǔ)言的第一個(gè)例子。1821年到1824年之間,年輕的Louis Braille受到在夜間讀寫信息的軍用系統(tǒng)的啟發(fā),發(fā)明了一種代碼,它用紙上突起的點(diǎn)作為代碼來(lái)幫助盲人閱讀。Braille代碼實(shí)際上是一種6位代碼,它把字符、常用字母組合、常用單字和標(biāo)點(diǎn)進(jìn)行編碼。一個(gè)特殊的escape代碼表示后續(xù)的字符代碼應(yīng)解釋為大寫。一個(gè)特殊的shift代碼允許后續(xù)代碼被解釋為數(shù)字。

Telex代碼,包括Baudot (以一個(gè)法國(guó)工程師命名,該工程師死于1903年)以及一種被稱為CCITT #2的代碼(1931年被標(biāo)準(zhǔn)化),都是包括字符和數(shù)字的5位代碼。

美國(guó)標(biāo)準(zhǔn)

早期計(jì)算機(jī)的字符碼是從Hollerith卡片(號(hào)稱不能被折迭、卷曲或毀傷)發(fā)展而來(lái)的,該卡片由Herman Hollerith發(fā)明并首次在1890年的美國(guó)人口普查中使用。6位字符碼系統(tǒng)BCDIC(Binary-Coded Decimal Interchange Code:二進(jìn)制編碼十進(jìn)制交換編碼)源自Hollerith代碼,在60年代逐步擴(kuò)展為8位EBCDIC,并一直是IBM大型主機(jī)的標(biāo)準(zhǔn),但沒(méi)使用在其它地方。

美國(guó)信息交換標(biāo)準(zhǔn)碼(ASCII:American Standard Code for Information Interchange)起始于50年代后期,最后完成于1967年。開(kāi)發(fā)ASCII的過(guò)程中,在字符長(zhǎng)度是6位、7位還是8位的問(wèn)題上產(chǎn)生了很大的爭(zhēng)議。從可靠性的觀點(diǎn)來(lái)看不應(yīng)使用替換字符,因此ASCII不能是6位編碼,但由于費(fèi)用的原因也排除了8位版本的方案(當(dāng)時(shí)每位的儲(chǔ)存空間成本仍很昂貴)。這樣,最終的字符碼就有26個(gè)小寫字母、26個(gè)大寫字母、10個(gè)數(shù)字、32個(gè)符號(hào)、33個(gè)句柄和一個(gè)空格,總共128個(gè)字符碼。ASCII現(xiàn)在記錄在ANSI X3.4-1986字符集-用于信息交換的7位美國(guó)國(guó)家標(biāo)準(zhǔn)碼(7-Bit ASCII:7-Bit American National Standard Code for Information Interchange),由美國(guó)國(guó)家標(biāo)準(zhǔn)協(xié)會(huì)(American National Standards Institute)發(fā)布。圖2-1中所示的ASCII字符碼與ANSI文件中的格式相似。

ASCII有許多優(yōu)點(diǎn)。例如,26個(gè)字母代碼是連續(xù)的(在EBCDIC代碼中就不是這樣的);大寫字母和小寫字母可通過(guò)改變一位數(shù)據(jù)而相互轉(zhuǎn)化;10個(gè)數(shù)字的代碼可從數(shù)值本身方便地得到(在BCDIC代碼中,字符「0」的編碼在字符「9」的后面!)

最棒的是,ASCII是一個(gè)非常可靠的標(biāo)準(zhǔn)。在鍵盤、視訊顯示卡、系統(tǒng)硬件、打印機(jī)、字體文件、操作系統(tǒng)和Internet上,其它標(biāo)準(zhǔn)都不如ASCII碼流行而且根深蒂固。


 

圖2-1 ASCII字符集

國(guó)際方面

ASCII的最大問(wèn)題就是該縮寫的第一個(gè)字母。ASCII是一個(gè)真正的美國(guó)標(biāo)準(zhǔn),所以它不能良好滿足其它講英語(yǔ)國(guó)家的需要。例如英國(guó)的英鎊符號(hào)(£)在哪里?

英語(yǔ)使用拉丁(或羅馬)字母表。在使用拉丁語(yǔ)字母表的書寫語(yǔ)言中,英語(yǔ)中的單詞通常很少需要重音符號(hào)(或讀音符號(hào))。即使那些傳統(tǒng)慣例加上讀音符號(hào)也無(wú)不當(dāng)?shù)挠⒄Z(yǔ)單字,例如c鰋perate或者résumé,拼寫中沒(méi)有讀音符號(hào)也會(huì)被完全接受。

但在美國(guó)以南、以北,以及大西洋地區(qū)的許多國(guó)家,在語(yǔ)言中使用讀音符號(hào)很普遍。這些重音符號(hào)最初是為使拉丁字母表適合這些語(yǔ)言讀音不同的需要。在遠(yuǎn)東或西歐的南部旅游,您會(huì)遇到根本不使用拉丁字母的語(yǔ)言,例如希臘語(yǔ)、希伯來(lái)語(yǔ)、阿拉伯語(yǔ)和俄語(yǔ)(使用斯拉夫字母表)。如果您向東走得更遠(yuǎn),就會(huì)發(fā)現(xiàn)中國(guó)象形漢字,日本和朝鮮也采用漢字系統(tǒng)。

ASCII的歷史開(kāi)始于1967年,此后它主要致力于克服其自身限制以更適合于非美國(guó)英語(yǔ)的其它語(yǔ)言。例如,1967年,國(guó)際標(biāo)準(zhǔn)化組織(ISO:International Standards Organization)推薦一個(gè)ASCII的變種,代碼0x40、0x5B、0x5C、0x5D、0x7B、0x7C和0x7D「為國(guó)家使用保留」,而代碼0x5E、0x60和0x7E標(biāo)為「當(dāng)國(guó)內(nèi)要求的特殊字符需要8、9或10個(gè)空間位置時(shí),可用于其它圖形符號(hào)」。這顯然不是一個(gè)最佳的國(guó)際解決方案,因?yàn)檫@并不能保證一致性。但這卻顯示了人們?nèi)绾蜗氡M辦法為不同的語(yǔ)言來(lái)編碼的。

擴(kuò)展ASCII

在小型計(jì)算機(jī)開(kāi)發(fā)的初期,就已經(jīng)嚴(yán)格地建立了8位字節(jié)。因此,如果使用一個(gè)字節(jié)來(lái)保存字符,則需要128個(gè)附加的字符來(lái)補(bǔ)充ASCII。1981年,當(dāng)最初的IBM PC推出時(shí),視訊卡的ROM中燒有一個(gè)提供256個(gè)字符的字符集,這也成為IBM標(biāo)準(zhǔn)的一個(gè)重要組成部分。

最初的IBM擴(kuò)展字符集包括某些帶重音的字符和一個(gè)小寫希臘字母表(在數(shù)學(xué)符號(hào)中非常有用),還包括一些塊型和線狀圖形字符。附加的字符也被添加到ASCII控制字符的編碼位置,這是因?yàn)榇蠖鄶?shù)控制字符都不是拿來(lái)顯示用的。

該IBM擴(kuò)展字符集被燒進(jìn)無(wú)數(shù)顯示卡和打印機(jī)的ROM中,并被許多應(yīng)用程序用于修飾其文字模式的顯示方式。不過(guò),該字符集并沒(méi)有為所有使用拉丁字母表的西歐語(yǔ)言提供足夠多的帶重音字符,而且也不適用于Windows。Windows不需要圖形字符,因?yàn)樗幸粋€(gè)完全圖形化的系統(tǒng)。

在Windows 1.0(1985年11月發(fā)行)中,Microsoft沒(méi)有完全放棄IBM擴(kuò)展字符集,但它已退居第二重要位置。因?yàn)樽裱薃NSI草案和ISO標(biāo)準(zhǔn),純Windows字符集被稱作「ANSI字符集」。ANSI草案和ISO標(biāo)準(zhǔn)最終成為ANSI/ISO 8859-1-1987,即「American National Standard for Information Processing-8-Bit Single-Byte Coded Graphic Character Sets-Part 1: Latin Alphabet No 1」,通常也簡(jiǎn)寫為「Latin 1」。

在Windows 1.0的《Programmer's Reference》中印出了ANSI字符集的最初版本,如圖2-2所示。


 

圖2-2 Windows ANSI字符集(基于ANSI/ISO 8859-1)

空方框表示該位置未定義字符。這與ANSI/ISO 8859-1的最終定義一致。ANSI/ISO 8859-1僅顯示了圖形字符,而沒(méi)有控制字符,因此沒(méi)有定義DEL。此外,代碼0xA0定義為一個(gè)非斷開(kāi)的空格(這意味著在編排格式時(shí),該字符不用于斷開(kāi)一行),代碼0xAD是一個(gè)軟連字符(表示除非在行尾斷開(kāi)單詞時(shí)使用,否則不顯示)。此外,ANSI/ISO 8859-1將代碼0xD7定義為乘號(hào)(*),0xF7為除號(hào)(/)。Windows中的某些字體也定義了從0x80到0x9F的某些字符,但這些不是ANSI/ISO 8859-1標(biāo)準(zhǔn)的一部分。

MS-DOS 3.3(1987年4月發(fā)行)向IBM PC用戶引進(jìn)了代碼頁(yè)(code page)的概念,Windows也使用此概念。代碼頁(yè)定義了字符的映像代碼。最初的IBM字符集被稱作代碼頁(yè)437,或者「MS-DOS Latin US)。代碼頁(yè)850就是「MS-DOS Latin 1」,它用附加的帶重音字母(但不是圖2-2所示的Latin 1 ISO/ANSI標(biāo)準(zhǔn))代替了一些線形字符。其它代碼頁(yè)被其它語(yǔ)言定義。最低的128個(gè)代碼總是相同的;較高的128個(gè)代碼取決于定義代碼頁(yè)的語(yǔ)言。

在MS-DOS中,如果用戶為PC的鍵盤、顯示卡和打印機(jī)指定了一個(gè)代碼頁(yè),然后在PC上創(chuàng)建、編輯和打印文件,一切都很正常,每件事都會(huì)保持一致。然而,如果用戶試圖與使用不同代碼頁(yè)的用戶交換文件,或者在機(jī)器上改變代碼頁(yè),就會(huì)產(chǎn)生問(wèn)題。字符碼與錯(cuò)誤的字符相關(guān)聯(lián)。應(yīng)用程序能夠?qū)⒋a頁(yè)信息與文件一起保存來(lái)試圖減少問(wèn)題的產(chǎn)生,但該策略包括了某些在代碼頁(yè)間轉(zhuǎn)換的工作。

雖然代碼頁(yè)最初僅提供了不包括帶重音符號(hào)字母的附加拉丁字符集,但最終代碼頁(yè)的較高的128個(gè)字符還是包括了完整的非拉丁字母,例如希伯來(lái)語(yǔ)、希臘語(yǔ)和斯拉夫語(yǔ)。自然,如此多樣會(huì)導(dǎo)致代碼頁(yè)變得混亂;如果少數(shù)帶重音的字母未正確顯示,那么整個(gè)文字便會(huì)混亂不堪而不可閱讀。

代碼頁(yè)的擴(kuò)展正是基于所有這些原因,但是還不夠。斯拉夫語(yǔ)的MS-DOS代碼頁(yè)855與斯拉夫語(yǔ)的Windows代碼頁(yè)1251以及斯拉夫語(yǔ)的Macintosh代碼頁(yè)10007不同。每個(gè)環(huán)境下的代碼頁(yè)都是對(duì)該環(huán)境所作的標(biāo)準(zhǔn)字符集修正。IBM OS/2也支援多種EBCDIC代碼頁(yè)。

但等一下,你會(huì)發(fā)現(xiàn)事情變得更糟糕。

雙字節(jié)字符集

迄今為止,我們已經(jīng)看到了256個(gè)字符的字符集。但中國(guó)、日本和韓國(guó)的象形文字符號(hào)有大約21,000個(gè)。如何容納這些語(yǔ)言而仍保持和ASCII的某種兼容性呢?

解決方案(如果這個(gè)說(shuō)法正確的話)是雙字節(jié)字符集(DBCS:double-byte character set)。DBCS從256代碼開(kāi)始,就像ASCII一樣。與任何行為良好的代碼頁(yè)一樣,最初的128個(gè)代碼是ASCII。然而,較高的128個(gè)代碼中的某些總是跟隨著第二個(gè)字節(jié)。這兩個(gè)字節(jié)一起(稱作首字節(jié)和跟隨字節(jié))定義一個(gè)字符,通常是一個(gè)復(fù)雜的象形文字。

雖然中文、日文和韓文共享一些相同的象形文字,但顯然這三種語(yǔ)言是不同的,而且經(jīng)常是同一個(gè)象形文字在三種不同的語(yǔ)言中代表三件不同的事。Windows支持四個(gè)不同的雙字節(jié)字符集:代碼頁(yè)932(日文)、936(簡(jiǎn)體中文)、949(韓語(yǔ))和950(繁體漢字)。只有為這些國(guó)家(地區(qū))生產(chǎn)的Windows版本才支持DBCS。

雙字符集問(wèn)題并不是說(shuō)字符由兩個(gè)字節(jié)代表。問(wèn)題在于一些字符(特別是ASCII字符)由1個(gè)字節(jié)表示。這會(huì)引起附加的程序設(shè)計(jì)問(wèn)題。例如,字符串中的字符數(shù)不能由字符串的字節(jié)數(shù)決定。必須剖析字符串來(lái)決定其長(zhǎng)度,而且必須檢查每個(gè)字節(jié)以確定它是否為雙字節(jié)字符的首字節(jié)。如果有一個(gè)指向DBCS字符串中間的指針,那么該字符串前一個(gè)字符的地址是什么呢?慣用的解決方案是從開(kāi)始的指針?lè)治鲈撟址?#xff01;

Unicode解決方案

我們面臨的基本問(wèn)題是世界上的書寫語(yǔ)言不能簡(jiǎn)單地用256個(gè)8位代碼表示。以前的解決方案包括代碼頁(yè)和DBCS已被證明是不能滿足需要的,而且也是笨拙的。那什么才是真正的解決方案呢?

身為程序?qū)懽髡?#xff0c;我們經(jīng)歷過(guò)這類問(wèn)題。如果事情太多,用8位數(shù)值已經(jīng)不能表示,那么我們就試更寬的值,例如16位值。而且這很有趣的,正是Unicode被制定的原因。與混亂的256個(gè)字符代碼映像,以及含有一些1字節(jié)代碼和一些2字節(jié)代碼的雙字節(jié)字符集不同,Unicode是統(tǒng)一的16位系統(tǒng),這樣就允許表示65,536個(gè)字符。這對(duì)表示所有字符及世界上使用象形文字的語(yǔ)言,包括一系列的數(shù)學(xué)、符號(hào)和貨幣單位符號(hào)的集合來(lái)說(shuō)是充裕的。

明白Unicode和DBCS之間的區(qū)別很重要。Unicode使用(特別在C程序設(shè)計(jì)語(yǔ)言環(huán)境里)「寬字符集」。「Unicode中的每個(gè)字符都是16位寬而不是8位寬。」在Unicode中,沒(méi)有單單使用8位數(shù)值的意義存在。相比之下,在雙字節(jié)字符集中我們?nèi)匀惶幚?位數(shù)值。有些字節(jié)自身定義字符,而某些字節(jié)則顯示需要和另一個(gè)字節(jié)共同定義一個(gè)字符。

處理DBCS字符串非常雜亂,但是處理Unicode文字則像處理有秩序的文字。您也許會(huì)高興地知道前128個(gè)Unicode字符(16位代碼從0x0000到0x007F)就是ASCII字符,而接下來(lái)的128個(gè)Unicode字符(代碼從0x0080到0x00FF)是ISO 8859-1對(duì)ASCII的擴(kuò)展。Unicode中不同部分的字符都同樣基于現(xiàn)有的標(biāo)準(zhǔn)。這是為了便于轉(zhuǎn)換。希臘字母表使用從0x0370到0x03FF的代碼,斯拉夫語(yǔ)使用從0x0400到0x04FF的代碼,美國(guó)使用從0x0530到0x058F的代碼,希伯來(lái)語(yǔ)使用從0x0590到0x05FF的代碼。中國(guó)、日本和韓國(guó)的象形文字(總稱為CJK)占用了從0x3000到0x9FFF的代碼。

Unicode的最大好處是這里只有一個(gè)字符集,沒(méi)有一點(diǎn)含糊。Unicode實(shí)際上是個(gè)人計(jì)算機(jī)行業(yè)中幾乎每個(gè)重要公司共同合作的結(jié)果,并且它與ISO 10646-1標(biāo)準(zhǔn)中的代碼是一一對(duì)應(yīng)的。Unicode的重要參考文獻(xiàn)是《The Unicode Standard,Version 2.0》(Addison-Wesley出版社,1996年)。這是一本特別的書,它以其它文件少有的方式顯示了世界上書寫語(yǔ)言的豐富性和多樣性。此外,該書還提供了開(kāi)發(fā)Unicode的基本原理和細(xì)節(jié)。

Unicode有缺點(diǎn)嗎?當(dāng)然有。Unicode字符串占用的內(nèi)存是ASCII字符串的兩倍。(然而壓縮文件有助于極大地減少文件所占的磁盤空間。)但也許最糟的缺點(diǎn)是:人們相對(duì)來(lái)說(shuō)還不習(xí)慣使用Unicode。身為程序?qū)懽髡?#xff0c;這就是我們的工作。

寬字符和 C

對(duì)C程序?qū)懽髡邅?lái)說(shuō),16位字符的想法的確讓人掃興。一個(gè)char和一個(gè)字節(jié)同寬是最不能確定的事情之一。沒(méi)幾個(gè)程序?qū)懽髡咔宄嗀NSI/ISO 9899-1990,這是「美國(guó)國(guó)家標(biāo)準(zhǔn)程序設(shè)計(jì)語(yǔ)言-C」(也稱作「ANSI C」)通過(guò)一個(gè)稱作「寬字符」的概念來(lái)支持用多個(gè)字節(jié)代表一字符的字符集。這些寬字符與常用的字符完美地共存。

ANSI C也支持多字節(jié)字符集,例如中文、日文和韓文版本W(wǎng)indows支持的字符集。然而,這些多字節(jié)字符集被當(dāng)成單字節(jié)構(gòu)成的字符串看待,只不過(guò)其中一些字符改變了后續(xù)字符的含義而已。多字節(jié)字符集主要影響C語(yǔ)言程序執(zhí)行時(shí)期鏈接庫(kù)函數(shù)。相比之下,寬字符比正常字符寬,而且會(huì)引起一些編譯問(wèn)題。

寬字符不需要是Unicode。Unicode是一種可能的寬字符集。然而,因?yàn)楸緯慕裹c(diǎn)是Windows而不是C執(zhí)行的理論,所以我將把寬字符和Unicode作為同義語(yǔ)。

Char數(shù)據(jù)型態(tài)

假定我們都非常熟悉在C程序中使用char數(shù)據(jù)型態(tài)來(lái)定義和儲(chǔ)存字符跟字符串。但為了便于理解C如何處理寬字符,讓我們先回顧一下可能在Win32程序中出現(xiàn)的標(biāo)準(zhǔn)字符定義。

下面的語(yǔ)句定義并初始化了一個(gè)只包含一個(gè)字符的變量:

char c = 'A' ;

變量c需要1個(gè)字節(jié)來(lái)保存,并將用十六進(jìn)制數(shù)0x41初始化,這是字母A的ASCII代碼。

您可以像這樣定義一個(gè)指向字符串的指針:

char * p ;

因?yàn)閃indows是一個(gè)32位操作系統(tǒng),所以指針變量p需要用4個(gè)字節(jié)保存。您還可初始化一個(gè)指向字符串的指針:

char * p = "Hello!" ;

像前面一樣,變量p也需要用4個(gè)字節(jié)保存。該字符串保存在靜態(tài)內(nèi)存中并占用7個(gè)字節(jié)-6個(gè)字節(jié)保存字符串,另1個(gè)字節(jié)保存終止符號(hào)0。

您還可以像這樣定義字符數(shù)組:

char a[10] ;

在這種情況下,編譯器為該數(shù)組保留了10個(gè)字節(jié)的儲(chǔ)存空間。表達(dá)式sizeof(a)將返回10。如果數(shù)組是整體變量(即在所有函數(shù)外定義),您可使用像下面的語(yǔ)句來(lái)初始化一個(gè)字符數(shù)組:

char a[] = "Hello!" ;

如果您將該數(shù)組定義為一個(gè)函數(shù)的區(qū)域變量,則必須將它定義為一個(gè)static變量,如下:

static char a[] = "Hello!" ;

無(wú)論哪種情況,字符串都儲(chǔ)存在靜態(tài)程序內(nèi)存中,并在末尾添加0,這樣就需要7個(gè)字節(jié)的儲(chǔ)存空間。

寬字符

Unicode或者寬字符都沒(méi)有改變char數(shù)據(jù)型態(tài)在C中的含義。char繼續(xù)表示1個(gè)字節(jié)的儲(chǔ)存空間,sizeof (char)繼續(xù)返回1。理論上,C中1個(gè)字節(jié)可比8位長(zhǎng),但對(duì)我們大多數(shù)人來(lái)說(shuō),1個(gè)字節(jié)(也就是1個(gè)char)是8位寬。

C中的寬字符基于wchar_t數(shù)據(jù)型態(tài),它在幾個(gè)表頭文件包括WCHAR.H中都有定義,像這樣:

typedef unsigned short wchar_t ;

因此,wchar_t數(shù)據(jù)型態(tài)與無(wú)符號(hào)短整數(shù)型態(tài)相同,都是16位寬。

要定義包含一個(gè)寬字符的變量,可使用下面的語(yǔ)句:

wchar_t c = 'A' ;

變量c是一個(gè)雙字節(jié)值0x0041,是Unicode表示的字母A。(然而,因?yàn)镮ntel微處理器從最小的字節(jié)開(kāi)始儲(chǔ)存多字節(jié)數(shù)值,該字節(jié)實(shí)際上是以0x41、0x00的順序保存在內(nèi)存中。如果檢查Unicode文字的計(jì)算機(jī)儲(chǔ)存應(yīng)注意這一點(diǎn)。)

您還可定義指向?qū)捵址闹羔?#xff1a;

wchar_t * p = L"Hello!" ;

注意緊接在第一個(gè)引號(hào)前面的大寫字母L(代表「long」)。這將告訴編譯器該字符串按寬字符保存-即每個(gè)字符占用2個(gè)字節(jié)。通常,指針變量p要占用4個(gè)字節(jié),而字符串變量需要14個(gè)字節(jié)-每個(gè)字符需要2個(gè)字節(jié),末尾的0還需要2個(gè)字節(jié)。

同樣,您還可以用下面的語(yǔ)句定義寬字符數(shù)組:

static wchar_t a[] = L"Hello!" ;

該字符串也需要14個(gè)字節(jié)的儲(chǔ)存空間,sizeof (a) 將返回14。索引數(shù)組a可得到單獨(dú)的字符。a[1] 的值是寬字符「e」,或者0x0065。

雖然看上去更像一個(gè)印刷符號(hào),但第一個(gè)引號(hào)前面的L非常重要,并且在兩個(gè)符號(hào)之間必須沒(méi)有空格。只有帶有L,編譯器才知道您需要將字符串存為每個(gè)字符2字節(jié)。稍后,當(dāng)我們看到使用寬字符串而不是變量定義時(shí),您還會(huì)遇到第一個(gè)引號(hào)前面的L。幸運(yùn)的是,如果忘記了包含L,C編譯器通常會(huì)給提出警告或錯(cuò)誤信息。

您還可在單個(gè)字符文字前面使用L前綴,來(lái)表示它們應(yīng)解釋為寬字符。如下所示:

wchar_t c = L'A' ;

但通常這是不必要的,C編譯器會(huì)對(duì)該字符進(jìn)行擴(kuò)充,使它成為寬字符。

寬字符鏈接庫(kù)函數(shù)

我們都知道如何獲得字符串的長(zhǎng)度。例如,如果我們已經(jīng)像下面這樣定義了一個(gè)字符串指針:

char * pc = "Hello!" ;

我們可以呼叫

iLength = strlen (pc) ;

這時(shí)變量iLength將等于6,也就是字符串中的字符數(shù)。

太好了!現(xiàn)在讓我們?cè)囍x一個(gè)指向?qū)捵址闹羔?#xff1a;

wchar_t * pw = L"Hello!" ;

再次呼叫strlen :

iLength = strlen (pw) ;

現(xiàn)在麻煩來(lái)了。首先,C編譯器會(huì)顯示一條警告消息,可能是這樣的內(nèi)容:

'function' : incompatible types - from 'unsigned short *' to 'const char *'

這條消息的意思是:聲明strlen函數(shù)時(shí),該函數(shù)應(yīng)接收char類型的指標(biāo),但它現(xiàn)在卻接收了一個(gè)unsigned short類型的指標(biāo)。您仍然可編譯并執(zhí)行該程序,但您會(huì)發(fā)現(xiàn)iLength等于1。為什么?

字符串「Hello!」中的6個(gè)字符占用16位:

0x0048 0x0065 0x006C 0x006C 0x006F 0x0021

Intel處理器在內(nèi)存中將其存為:

48 00 65 00 6C 00 6C 00 6F 00 21 00

假定strlen函數(shù)正試圖得到一個(gè)字符串的長(zhǎng)度,并把第1個(gè)字節(jié)作為字符開(kāi)始計(jì)數(shù),但接著假定如果下一個(gè)字節(jié)是0,則表示字符串結(jié)束。

這個(gè)小練習(xí)清楚地說(shuō)明了C語(yǔ)言本身和執(zhí)行時(shí)期鏈接庫(kù)函數(shù)之間的區(qū)別。編譯器將字符串L"Hello!" 解釋為一組16位短整數(shù)型態(tài)數(shù)據(jù),并將其保存在wchar_t數(shù)組中。編譯器還處理數(shù)組索引和sizeof操作符,因此這些都能正常工作,但在連結(jié)時(shí)才添加執(zhí)行時(shí)期鏈接庫(kù)函數(shù),例如strlen。這些函數(shù)認(rèn)為字符串由單字節(jié)字符組成。遇到寬字符串時(shí),函數(shù)就不像我們所希望那樣執(zhí)行了。

您可能要說(shuō):「噢,太麻煩了!」現(xiàn)在每個(gè)C語(yǔ)言鏈接庫(kù)函數(shù)都必須重寫以接受寬字符。但事實(shí)上并不是每個(gè)C語(yǔ)言鏈接庫(kù)函數(shù)都需要重寫,只是那些有字符串參數(shù)的函數(shù)才需要重寫,而且也不用由您來(lái)完成。它們已經(jīng)重寫完了。

strlen函數(shù)的寬字符版是wcslen(wide-character string length:寬字符串長(zhǎng)度),并且在STRING.H(其中也說(shuō)明了strlen)和WCHAR.H中均有說(shuō)明。strlen函數(shù)說(shuō)明如下:

size_t __cdecl strlen (const char *) ;

而wcslen函數(shù)則說(shuō)明如下:

size_t __cdecl wcslen (const wchar_t *) ;

這時(shí)我們知道,要得到寬字符串的長(zhǎng)度可以呼叫

iLength = wcslen (pw) ;

函數(shù)將返回字符串中的字符數(shù)6。請(qǐng)記住,改成寬字節(jié)后,字符串的字符長(zhǎng)度不改變,只是位組長(zhǎng)度改變了。

您熟悉的所有帶有字符串參數(shù)的C執(zhí)行時(shí)期鏈接庫(kù)函數(shù)都有寬字符版。例如,wprintf是printf的寬字符版。這些函數(shù)在WCHAR.H和含有標(biāo)準(zhǔn)函數(shù)說(shuō)明的表頭文件中說(shuō)明。

維護(hù)單一原始碼

當(dāng)然,使用Unicode也有缺點(diǎn)。第一點(diǎn)也是最主要的一點(diǎn)是,程序中的每個(gè)字符串都將占用兩倍的儲(chǔ)存空間。此外,您將發(fā)現(xiàn)寬字符執(zhí)行時(shí)期鏈接庫(kù)中的函數(shù)比常規(guī)的函數(shù)大。出于這個(gè)原因,您也許想建立兩個(gè)版本的程序-一個(gè)處理ASCII字符串,另一個(gè)處理Unicode字符串。最好的解決辦法是維護(hù)既能按ASCII編譯又能按Unicode編譯的單一原始碼文件。

雖然只是一小段程序,但由于執(zhí)行時(shí)期鏈接庫(kù)函數(shù)有不同的名稱,您也要定義不同的字符,這將在處理前面有L的字符串文字時(shí)遇到麻煩。

一個(gè)辦法是使用Microsoft Visual C++包含的TCHAR.H表頭文件。該表頭文件不是ANSI C標(biāo)準(zhǔn)的一部分,因此那里定義的每個(gè)函數(shù)和宏定義的前面都有一條底線。TCHAR.H為需要字符串參數(shù)的標(biāo)準(zhǔn)執(zhí)行時(shí)期鏈接庫(kù)函數(shù)提供了一系列的替代名稱(例如,_tprintf和_tcslen)。有時(shí)這些名稱也稱為「通用」函數(shù)名稱,因?yàn)樗鼈兗瓤梢灾赶蚝瘮?shù)的Unicode版也可以指向非Unicode版。

如果定義了名為_(kāi)UNICODE的標(biāo)識(shí)符,并且程序中包含了TCHAR.H表頭文件,那么_tcslen就定義為wcslen:

#define _tcslen wcslen

如果沒(méi)有定義UNICODE,則_tcslen定義為strlen:

#define _tcslen strlen

等等。TCHAR.H還用一個(gè)新的數(shù)據(jù)型態(tài)TCHAR來(lái)解決兩種字符數(shù)據(jù)型態(tài)的問(wèn)題。如果定義了_UNICODE標(biāo)識(shí)符,那么TCHAR就是wchar_t:

typedef wchar_t TCHAR ;

否則,TCHAR就是Char:

typedef char TCHAR ;

現(xiàn)在開(kāi)始討論字符串文字中的L問(wèn)題。如果定義了_UNICODE標(biāo)識(shí)符,那么一個(gè)稱作__T的宏就定義如下:

#define __T(x) L##x

這是相當(dāng)晦澀的語(yǔ)法,但合乎ANSI C標(biāo)準(zhǔn)的前置處理器規(guī)范。那一對(duì)井字號(hào)稱為「粘貼符號(hào)(token paste)」,它將字母L添加到宏參數(shù)上。因此,如果宏參數(shù)是"Hello!",則L##x就是L"Hello!"。

如果沒(méi)有定義_UNICODE標(biāo)識(shí)符,則__T宏只簡(jiǎn)單地定義如下:

#define __T(x) x

此外,還有兩個(gè)宏與__T定義相同:

#define _T(x)__T(x) #define _TEXT(x)__T(x)

在Win32 console程序中使用哪個(gè)宏,取決于您喜歡簡(jiǎn)潔還是詳細(xì)。基本地,必須按下述方法在_T或_TEXT宏內(nèi)定義字符串文字:

_TEXT ("Hello!")

這樣做的話,如果定義了_UNICODE,那么該串將解釋為寬字符的組合,否則解釋為8位的字符字符串。

寬字符和 Windows

Windows NT從底層支援Unicode。這意味著Windows NT內(nèi)部使用由16位字符組成的字符串。因?yàn)槭澜缟掀渌S多地方還不使用16位字符串,所以Windows NT必須經(jīng)常將字符串在操作系統(tǒng)內(nèi)轉(zhuǎn)換。Windows NT可執(zhí)行為ASCII、Unicode或者ASCII和Unicode混合編寫的程序。即,Windows NT支持不同的API函數(shù)呼叫,這些函數(shù)接受8位或16位的字符串(我們將馬上看到這是如何動(dòng)作的。)

相對(duì)于Windows NT,Windows 98對(duì)Unicode的支持要少得多。只有很少的Windows 98函數(shù)呼叫支持寬字符串(這些函數(shù)列在《Microsoft Knowledge Base article Q125671》中;它們包括MessageBox)。如果要發(fā)行的程序中只有一個(gè).EXE文件要求在Windows NT和Windows 98下都能執(zhí)行,那么就不應(yīng)該使用Unicode,否則就不能在Windows 98下執(zhí)行;尤其程序不能呼叫Unicode版的Windows函數(shù)。這樣,將來(lái)發(fā)行Unicode版的程序時(shí)會(huì)處于更有利的位置,您應(yīng)試著編寫既為ASCII又為Unicode編譯的原始碼。這就是本書中所有程序的編寫方式。

Windows表頭文件類型

正如您在第一章所看到的那樣,一個(gè)Windows程序包括表頭文件WINDOWS.H。該文件包括許多其它表頭文件,包括WINDEF.H,該文件中有許多在Windows中使用的基本型態(tài)定義,而且它本身也包括WINNT.H。WINNT.H處理基本的Unicode支持。

WINNT.H的前面包含C的表頭文件CTYPE.H,這是C的眾多表頭文件之一,包括wchar_t的定義。WINNT.H定義了新的數(shù)據(jù)型態(tài),稱作CHAR和WCHAR:

typedef char CHAR ; typedef wchar_t WCHAR ; // wc

當(dāng)您需要定義8位字符或者16位字符時(shí),推薦您在Windows程序中使用的數(shù)據(jù)型態(tài)是CHAR和WCHAR。WCHAR定義后面的注釋是匈牙利標(biāo)記法的建議:一個(gè)基于WCHAR數(shù)據(jù)型態(tài)的變量可在前面附加上字母wc以說(shuō)明一個(gè)寬字符。

WINNT.H表頭文件進(jìn)而定義了可用做8位字符串指針的六種數(shù)據(jù)型態(tài)和四個(gè)可用做const 8位字符串指針的數(shù)據(jù)型態(tài)。這里精選了表頭文件中一些實(shí)用的說(shuō)明數(shù)據(jù)型態(tài)語(yǔ)句:

typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, * PSTR ; typedef CONST CHAR * LPCCH, * PCCH, * LPCSTR, * PCSTR ;

前綴N和L表示「near」和「long」,指的是16位Windows中兩種大小不同的指標(biāo)。在Win32中near和long指標(biāo)沒(méi)有區(qū)別。

類似地,WINNT.H定義了六種可作為16位字符串指針的數(shù)據(jù)型態(tài)和四種可作為const 16位字符串指針的數(shù)據(jù)型態(tài):

typedef WCHAR * PWCHAR, * LPWCH, * PWCH, * NWPSTR, * LPWSTR, * PWSTR ; typedef CONST WCHAR * LPCWCH, * PCWCH, * LPCWSTR, * PCWSTR ;

至此,我們有了數(shù)據(jù)型態(tài)CHAR(一個(gè)8位的char)和WCHAR(一個(gè)16位的wchar_t),以及指向CHAR和WCHAR的指標(biāo)。與TCHAR.H一樣,WINNT.H將TCHAR定義為一般的字符類型。如果定義了標(biāo)識(shí)符UNICODE(沒(méi)有底線),則TCHAR和指向TCHAR的指標(biāo)就分別定義為WCHAR和指向WCHAR的指標(biāo);如果沒(méi)有定義標(biāo)識(shí)符UNICODE,則TCHAR和指向TCHAR的指標(biāo)就分別定義為char和指向char的指標(biāo):

#ifdef UNICODE typedef WCHAR TCHAR, * PTCHAR ; typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR ; typedef LPCWSTR LPCTSTR ; #else typedef char TCHAR, * PTCHAR ; typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR ; typedef LPCSTR LPCTSTR ; #endif

如果已經(jīng)在某個(gè)表頭文件或者其它表頭文件中定義了TCHAR數(shù)據(jù)型態(tài),那么WINNT.H和WCHAR.H表頭文件都能防止其重復(fù)定義。不過(guò),無(wú)論何時(shí)在程序中使用其它表頭文件時(shí),都應(yīng)在所有其它表頭文件之前包含WINDOWS.H。

WINNT.H表頭文件還定義了一個(gè)宏,該宏將L添加到字符串的第一個(gè)引號(hào)前。如果定義了UNICODE標(biāo)識(shí)符,則一個(gè)稱作 __TEXT的宏定義如下:

#define __TEXT(quote) L##quote

如果沒(méi)有定義標(biāo)識(shí)符UNICODE,則像這樣定義__TEXT宏:

#define __TEXT(quote) quote

此外, TEXT宏可這樣定義:

#define TEXT(quote) __TEXT(quote)

這與TCHAR.H中定義_TEXT宏的方法一樣,只是不必操心底線。我將在本書中使用這個(gè)宏的TEXT版本。

這些定義可使您在同一程序中混合使用ASCII和Unicode字符串,或者編寫一個(gè)可被ASCII或Unicode編譯的程序。如果您希望明確定義8位字符變量和字符串,請(qǐng)使用CHAR、PCHAR(或者其它),以及帶引號(hào)的字符串。為明確地使用16位字符變量和字符串,請(qǐng)使用WCHAR、PWCHAR,并將L添加到引號(hào)前面。對(duì)于是8位還是16位取決于UNICODE標(biāo)識(shí)符的定義的變量或字符串,要使用TCHAR、PTCHAR和TEXT宏。

Windows函數(shù)呼叫

從Windows 1.0到Windows 3.1的16位Windows中,MessageBox函數(shù)位于動(dòng)態(tài)鏈接庫(kù)USER.EXE。在Windows 3.1軟件開(kāi)發(fā)套件的WINDOWS.H中,MessageBox函數(shù)定義如下:

int WINAPI MessageBox (HWND, LPCSTR, LPCSTR, UINT) ;

注意,函數(shù)的第二個(gè)、第三個(gè)參數(shù)是指向常數(shù)字符串的指針。當(dāng)編譯連結(jié)一個(gè)Win16程序時(shí),Windows并不處理MessageBox呼叫。程序.EXE文件中的表格,允許Windows將該程序的呼叫與USER中的MessageBox函數(shù)動(dòng)態(tài)鏈接起來(lái)。

32位的Windows(即所有版本的Windows NT,以及Windows 95和Windows 98)除了含有與16位兼容的USER.EXE以外,還含有一個(gè)稱為USER32.DLL的動(dòng)態(tài)鏈接庫(kù),該動(dòng)態(tài)鏈接庫(kù)含有32位使用者接口函數(shù)的進(jìn)入點(diǎn),包括32位的MessageBox。

這就是Windows支持Unicode的關(guān)鍵:在USER32.DLL中,沒(méi)有32位MessageBox函數(shù)的進(jìn)入點(diǎn)。實(shí)際上,有兩個(gè)進(jìn)入點(diǎn),一個(gè)名為MessageBoxA(ASCII版),另一個(gè)名為MessageBoxW(寬字符版)。用字符串作參數(shù)的每個(gè)Win32函數(shù)都在操作系統(tǒng)中有兩個(gè)進(jìn)入點(diǎn)!幸運(yùn)的是,您通常不必關(guān)心這個(gè)問(wèn)題,程序中只需使用MessageBox。與TCHAR表頭文件一樣,每個(gè)Windows表頭文件都有我們需要的技巧。

下面是MessageBoxA在WINUSER.H中定義的方法。這與MessageBox早期的定義很相似:

WINUSERAPI int WINAPI MessageBoxA (HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) ;

下面是MessageBoxW:

WINUSERAPI int WINAPI MessageBoxW (HWND hWnd, LPCWSTR lpText,LPCWSTR lpCaption, UINT uType) ;

注意,MessageBoxW函數(shù)的第二個(gè)和第三個(gè)參數(shù)是指向?qū)捵址闹羔槨?/p>

如果需要同時(shí)使用并分別匹配ASCII和寬字符函數(shù)呼叫,那么您可在Windows程序中明確地使用MessageBoxA和MessageBoxW函數(shù)。但大多數(shù)程序?qū)懽髡邔⒗^續(xù)使用MessageBox。根據(jù)是否定義了UNICODE,MessageBox將與MessageBoxA或MessageBoxW一樣。在WINUSER.H中完成這一技巧時(shí),程序相當(dāng)瑣碎:

#ifdef UNICODE#define MessageBox MessageBoxW#else#define MessageBox MessageBoxA#endif

這樣,如果定義了UNICODE標(biāo)識(shí)符,那么程序中所有的MessageBox函數(shù)呼叫實(shí)際上就是MessageBoxW函數(shù);否則,就是MessageBoxA函數(shù)。

執(zhí)行該程序時(shí),Windows將程序中不同的函數(shù)呼叫與不同的Windows動(dòng)態(tài)鏈接庫(kù)的進(jìn)入點(diǎn)連結(jié)。雖然只有少數(shù)例外,但是,在Windows 98中不能執(zhí)行Unicode版的Windows函數(shù)。雖然這些函數(shù)有進(jìn)入點(diǎn),但通常返回錯(cuò)誤代碼。應(yīng)用程序注意這些返回的錯(cuò)誤并采取一些合理的動(dòng)作。

Windows的字符串函數(shù)

正如前面談到的,Microsoft C包括寬字符和需要字符串參數(shù)的C語(yǔ)言執(zhí)行時(shí)期鏈接庫(kù)函數(shù)的所有普通版本。不過(guò),Windows復(fù)制了其中一部分。例如,下面是Windows定義的一組字符串函數(shù),這些函數(shù)用來(lái)計(jì)算字符串長(zhǎng)度、復(fù)制字符串、連接字符串和比較字符串:

ILength = lstrlen (pString) ;pString = lstrcpy (pString1, pString2) ;pString = lstrcpyn (pString1, pString2, iCount) ;pString = lstrcat (pString1, pString2) ;iComp = lstrcmp (pString1, pString2) ;iComp = lstrcmpi (pString1, pString2) ;

這些函數(shù)與C鏈接庫(kù)中對(duì)應(yīng)的函數(shù)功能相同。如果定義了UNICODE標(biāo)識(shí)符,那么這些函數(shù)將接受寬字符串,否則只接受常規(guī)字符串。寬字符串版的lstrlenW函數(shù)可在Windows 98中執(zhí)行。

在Windows中使用printf

有文字模式、命令列C語(yǔ)言程序?qū)懽鳉v史的程序?qū)懽髡咄貏e喜歡printf函數(shù)。即使可以使用更簡(jiǎn)單的命令(例如puts),但printf出現(xiàn)在Kernighan和Ritchie的「hello, world」程序中一點(diǎn)也不會(huì)令人驚奇。我們知道,增強(qiáng)后的「hello, world」最終還是需要printf的格式化輸出,因此我們最好從頭開(kāi)始就使用它。

但有個(gè)壞消息:在Windows程序中不能使用printf。雖然Windows程序中可以使用大多數(shù)C的執(zhí)行時(shí)期鏈接庫(kù)-實(shí)際上,許多程序?qū)懽髡吒敢馐褂肅內(nèi)存管理和文件I/O函數(shù)而不是Windows中等效的函數(shù)-Windows對(duì)標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出沒(méi)有概念。在Windows程序中可使用fprintf,而不是printf。

還有一個(gè)好消息,那就是仍然可以使用sprintf及sprintf系列中的其它函數(shù)來(lái)顯示文字。這些函數(shù)除了將內(nèi)容格式化輸出到函數(shù)第一個(gè)參數(shù)所提供的字符串緩沖區(qū)以外,其功能與printfI相同。然后便可對(duì)該字符串進(jìn)行操作(例如將其傳給MessageBox)。

如果您從未使用過(guò)sprintf (我第一次開(kāi)始寫Windows程序時(shí)也沒(méi)用過(guò)此函數(shù)),這里有一個(gè)簡(jiǎn)短的執(zhí)行實(shí)體,printf函數(shù)說(shuō)明如下:

int printf (const char * szFormat, ...) ;

第一個(gè)參數(shù)是一個(gè)格式字符串,后面是與格式字符串中的代碼相對(duì)應(yīng)的不同類型多個(gè)參數(shù)。

sprintf函數(shù)定義如下:

int sprintf (char * szBuffer, const char * szFormat, ...) ;

第一個(gè)參數(shù)是字符緩沖區(qū);后面是一個(gè)格式字符串。Sprintf不是將格式化結(jié)果標(biāo)準(zhǔn)輸出,而是將其存入szBuffer。該函數(shù)返回該字符串的長(zhǎng)度。在文字模式程序設(shè)計(jì)中,

printf ("The sum of %i and %i is %i", 5, 3, 5+3) ;

的功能相同于

char szBuffer [100] ;sprintf (szBuffer, "The sum of %i and %i is %i", 5, 3, 5+3) ;puts (szBuffer) ;

在Windows中,使用MessageBox顯示結(jié)果優(yōu)于puts。

幾乎每個(gè)人都經(jīng)歷過(guò),當(dāng)格式字符串與被格式化的變量不合時(shí),可能使printf執(zhí)行錯(cuò)誤并可能造成程序當(dāng)?shù)簟J褂胹printf時(shí),您不但要擔(dān)心這些,而且還有一個(gè)新的負(fù)擔(dān):您定義的字符串緩沖區(qū)必須足夠大以存放結(jié)果。Microsoft專用函數(shù)_snprintf解決了這一問(wèn)題,此函數(shù)引進(jìn)了另一個(gè)參數(shù),表示以字符計(jì)算的緩沖區(qū)大小。

vsprintf是sprintf的一個(gè)變形,它只有三個(gè)參數(shù)。vsprintf用于執(zhí)行有多個(gè)參數(shù)的自訂函數(shù),類似printf格式。vsprintf的前兩個(gè)參數(shù)與sprintf相同:一個(gè)用于保存結(jié)果的字符緩沖區(qū)和一個(gè)格式字符串。第三個(gè)參數(shù)是指向格式化參數(shù)數(shù)組的指針。實(shí)際上,該指針指向在堆棧中供函數(shù)呼叫的變量。va_list、va_start和va_end宏(在STDARG.H中定義)幫助我們處理堆棧指針。本章最后的SCRNSIZE程序展示了使用這些宏的方法。使用vsprintf函數(shù),sprintf函數(shù)可以這樣編寫:

int sprintf (char * szBuffer, const char * szFormat, ...){int iReturn ;va_list pArgs ;va_start (pArgs, szFormat) ;iReturn = vsprintf (szBuffer, szFormat, pArgs) ;va_end (pArgs) ;return iReturn ;}

va_start宏將pArg設(shè)置為指向一個(gè)堆棧變量,該變量地址在堆棧參數(shù)szFormat的上面。

由于許多Windows早期程序使用了sprintf和vsprintf,最終導(dǎo)致Microsoft向Windows API中增添了兩個(gè)相似的函數(shù)。Windows的wsprintf和wvsprintf函數(shù)在功能上與sprintf和vsprintf相同,但它們不能處理浮點(diǎn)格式。

當(dāng)然,隨著寬字符的發(fā)表,sprintf類型的函數(shù)增加許多,使得函數(shù)名稱變得極為混亂。表2-1列出了Microsoft的C執(zhí)行時(shí)期鏈接庫(kù)和Windows支持的所有sprintf函數(shù)。

表2-1

?

?

ASCII

寬字符

常規(guī)

參數(shù)的變數(shù)個(gè)數(shù)

???

標(biāo)準(zhǔn)版

sprintf

swprintf

_stprintf

最大長(zhǎng)度版

_snprintf

_snwprintf

_sntprintf

Windows版

wsprintfA

wsprintfW

wsprintf

參數(shù)數(shù)組的指針

???

標(biāo)準(zhǔn)版

vsprintf

vswprintf

_vstprintf

最大長(zhǎng)度版

_vsnprintf

_vsnwprintf

_vsntprintf

Windows版

wvsprintfA

wvsprintfW

wvsprintf

在寬字符版的sprintf函數(shù)中,將字符串緩沖區(qū)定義為寬字符串。在寬字符版的所有這些函數(shù)中,格式字符串必須是寬字符串。不過(guò),您必須確保傳遞給這些函數(shù)的其它字符串也必須由寬字符組成。

格式化消息框

程序2-1所示的SCRNSIZE程序展示了如何實(shí)作MessageBoxPrintf函數(shù),該函數(shù)有許多參數(shù)并能像printf那樣編排它們的格式。

程序2-1 SCRNSIZESCRNSIZE.C/*---------------------------------------------------------------------------SCRNSIZE.C -- Displays screen size in a message box(c) Charles Petzold, 1998----------------------------------------------------------------------------*/#include <windows.h>#include <tchar.h> #include <stdio.h> int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, ...){TCHAR szBuffer [1024] ;va_list pArgList ;// The va_start macro (defined in STDARG.H) is usually equivalent to:// pArgList = (char *) &szFormat + sizeof (szFormat) ;va_start (pArgList, szFormat) ;// The last argument to wvsprintf points to the arguments_vsntprintf ( szBuffer, sizeof (szBuffer) / sizeof (TCHAR),szFormat, pArgList) ;// The va_end macro just zeroes out pArgList for no good reasonva_end (pArgList) ;return MessageBox (NULL, szBuffer, szCaption, 0) ;}int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){int cxScreen, cyScreen ;cxScreen = GetSystemMetrics (SM_CXSCREEN) ;cyScreen = GetSystemMetrics (SM_CYSCREEN) ;MessageBoxPrintf ( TEXT ("ScrnSize"),TEXT ("The screen is %i pixels wide by %i pixels high."),cxScreen, cyScreen) ;return 0 ;}

經(jīng)由從GetSystemMetrics函數(shù)得到的信息,該程序以圖素為單位顯示了視訊顯示的寬度和高度。GetSystemMetrics是一個(gè)能用來(lái)獲得Windows中不同對(duì)象的尺寸信息的函數(shù)。事實(shí)上,我將在第四章用GetSystemMetrics函數(shù)向您展示如何在一個(gè)Windows窗口中顯示和滾動(dòng)多行文字。

本書與國(guó)際化

為國(guó)際市場(chǎng)準(zhǔn)備的Windows程序不光要使用Unicode。國(guó)際化超出了本書的范圍,但在Nadine Kano所寫的《Developing International Software for Windows 95 and Windows NT》(Microsoft Press,1995年)一書中涉獵了許多。

本書中的程序?qū)懽鲿r(shí)被限制成既可使用也可不使用定義的UNICODE標(biāo)識(shí)符來(lái)編譯。這包括對(duì)所有字符和字符串定義使用TCHAR,對(duì)字符串文字使用TEXT宏,以及注意不要混淆字節(jié)和字符。例如,注意SCRNSIZE中的 _vsntprintf呼叫。第二個(gè)參數(shù)是緩沖區(qū)的字符大小。通常,您使用sizeof (szBuffer)。但如果緩沖區(qū)中有寬字符,則返回的不是緩沖區(qū)的字符長(zhǎng)度,而是緩沖區(qū)的字節(jié)大小。您必須用sizeof(TCHAR)將其分開(kāi)。

通常,在Visual C++ Developer Studio中,可使用兩種不同的設(shè)定來(lái)編譯程序:Debug和Release。為簡(jiǎn)便起見(jiàn),對(duì)本書的范例程序,我已修改了Debug設(shè)定,以便于定義UNICODE標(biāo)識(shí)符。如果程序使用了需要字符串作參數(shù)的C鏈接庫(kù)函數(shù),那么_UNICODE標(biāo)識(shí)符也在Debug設(shè)定中定義(要了解這是在哪里完成的,請(qǐng)從「Project」菜單中選擇「Settings」,然后單擊「C/C++」標(biāo)簽)。使用這種方式,這些程序就可以方便地被重新編譯和連結(jié)以供測(cè)試。

本書中所有程序-無(wú)論是否為Unicode編譯-都可以在Windows NT下執(zhí)行。只有極少數(shù)情況例外。本書中按Unicode編譯的程序不能在Windows 98中執(zhí)行,而非Unicode版則可以。本章和第一章的程序就是兩個(gè)特例。MessageBoxW是Windows 98支持的少數(shù)寬字符Windows函數(shù)之一。在SCRNSIZE.C中,如果用Windows函數(shù)wprintf代替了_vsntprintf(您還必須刪除該函數(shù)的第二個(gè)參數(shù)),那么SCRNSIZE.C的Unicode版將不能在Windows 98下執(zhí)行,這是因?yàn)閃indows 98不支持wprintfW。

在本書的后面(特別在第六章,介紹鍵盤的使用時(shí)),我們將看到,編寫能處理遠(yuǎn)東版Windows雙字符集的Windows程序不是一件容易的事情。本書沒(méi)有說(shuō)明如何去做,并且基于這個(gè)原因,本書中的某些非Unicode版本的程序在遠(yuǎn)東版的Windows下不能正常執(zhí)行。這也是Unicode對(duì)將來(lái)的程序設(shè)計(jì)如此重要的一條理由。Unicode允許程序更容易地跨越國(guó)界。

轉(zhuǎn)載于:https://www.cnblogs.com/caiyonghai/p/cailu.html

總結(jié)

以上是生活随笔為你收集整理的Unicode简介的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。