編碼(ACSII unicod UTF-8)、QT輸出中文亂碼深入分析

来源:https://www.cnblogs.com/flowingwind/archive/2018/01/02/8175551.html
-Advertisement-
Play Games

總結: 1. qt輸出中文亂碼原因分析 qt的編程環境預設是utf-8編碼格式(關於編碼見下文知識要點一); 程式運行,程式並不認識ANSI,UTF-8以及任何其他編碼.系統只知道處理你給它的字元的二進位表示. 關於 "中""文" 的3種編碼二進位內容: ANSI(GBK): 0xd6d0 0xce ...


總結:

1. qt輸出中文亂碼原因分析

qt的編程環境預設是utf-8編碼格式(關於編碼見下文知識要點一);

cout << "中文" << endl;

程式運行,程式並不認識ANSI,UTF-8以及任何其他編碼.系統只知道處理你給它的字元二進位表示.

 

關於  "中""文" 的3種編碼二進位內容:

 

ANSI(GBK): 0xd6d0  0xcec4

 

UTF-8: 0xe4b8ad 0xe69687

 

Unicode: 0x4e2d 0x6587

1)在簡體中文Windows下的控制台顯示環境是ANSI編碼(代碼頁936, GBK),先明確這點.

重點區別,MinGW看到的是"0xe4b8ad"和"0xe69687"(gcc預設UTF-8).註意,用MinGW編譯的源文件中有中文寬字元必須保存為UTF-8編碼.

2)測試代碼:

#include <iostream>
using namespace std;

int main()
{
    char a[] = "中文";
    cout << a << endl;
    return 0;
}

3)經在qt5.8中測試亂碼;

分析:參見(下文知識要點一,知識要點二)不難發現UTF-8只是一種編碼實行方案,並不是實際編碼;再參見(知識要點五),程式運行是能過最後編譯完成的二進位碼輸出

在vs2017中,用unicode編碼方式,編譯運行輸出正常;原因我想很好理解了,當程式編譯後保存的是“中文”unicode二進位編碼,而控制台輸出時CodePage (GBK 936) 這個CodePage就會根據映射表去一一對應GBK中的中文字,再進行輸出;

而在qt5.8(MinGW)中,輸出則是亂碼;因為qt5.8預設的編碼方式是UTF-8;當程式編譯後保存的是“中文”UTF-8二進位編碼,而控制台輸出時CodePage (GBK 936) 這個CodePage就會根據映射表去一一對應GBK中的中文字,好像哪裡不對,好了,問題就出在這兒了,CodePage是各國與unicode的映射表,並不是與UTF-8的(知識要點二CodePage),在qt5.8(MinGW)中,原程被編譯二進位文件,保存下來的“中文”地址是,UTF-8編碼,而映射表是在unicode中找內容,再進行輸出,自然就是亂碼;

網上解決方法1.修改註冊表CodePage 65001  經測試還是亂碼

理論分析:CodePage(GBK 936)找不到映射,那麼把控制台換成UTF-8;那麼然先保存的,UTF-8中文,再通過UTF-8對應的漢字碼,不就能輸出漢字;理論好像可行,但在我的win7 64位中文系統上,qt5.8,vs2017均失敗;

可能性原因:我系統中cmd控制台並不支持UTF-8編碼方式(有機會在win10中測試後再做補充)

解決方法2:通過(知識點一,二, 五),總結,當要在控制台進行中文輸出時,編碼方式應該保存為unicode,或ACSI(GBK);

4)關於寬位元組輸出亂碼的問題;

輸出寬位元組中文(詳見知識要點四):例

#include <iostream>
using namespace std;

int main()
{
    wcout << L"中文" << endl;
    return 0;
}

輸出則要用wcout而不能是cout;關於寬字元詳見;知識要點二後續,知識要點三

在vs2017中,輸出中文,為空;

1、cout和wcout

 在C++下,cout可以直接輸出中文,但對於wcout卻不行。對於wcout,需要將其locale設為本地語言才能輸出中文:

 wcout.imbue(locale(locale(),"",LC_CTYPE));

 也有人用如下語句的,但這會改變wcout的所有locale設置,比如數字“1234”會輸出為“1,234”。

 wcout.imbue(locale(""));

 在C語言下,locale設置為本地語言(C語言中只有全局locale)就可以正常輸出了:

 setlocale(LC_CTYPE, "");

 在qt5.8(MinGW)環境中,以上並不實用,目前還沒找到輸出中文的方法,未完待續;

 

知識要點一:編碼

ASCII: 早期的字元集,7位,128個字元,包括大小寫a-z字母,0-9數字以及一些控制字元.

  擴展ASCII: 1個位元組8位,只用7位不合理.於是第8位用於擴展ASCII字元集,這樣就又多了128個字元.於是用著後128個字元來擴展表示如拉丁字母,希臘字母等特殊符號.但問題是歐洲那一票國家很多互相都擁有不相同的特殊字母,一起塞進後128個明顯不夠,於是代碼頁出現了.

  Code Page(代碼頁): 1個位元組前128個字元大家統一和ASCII一樣,而後128個字元,根據不同系統所謂代碼頁來區分各個語言不相同的字母和符號.

  DBCS(雙位元組字元集): 對於亞洲國家,後128個字元依然無法包含大量的象形文字,DBCS正是為此的一個解決方案.DBCS由一個或兩個位元組表示一個字元,這說明DBCS並不一定是兩個位元組,對於如英文字母,是向ASCII相容的,依然由1個位元組表示,而對於如中文則用2個位元組表示.英文和中文可以統一地處理,而區分是否為中文編碼的方法是2個位元組中的高位元組的首位為1,就必須檢查後面跟隨的那個位元組,2個位元組一起解釋為1個字元.GB2312,GBK到GB18030都屬於DBCS.另外,簡體中文Windows下的ANSI編碼通常是指GBK(代碼頁936).

DBCS很大問題在於字元串的字元數不能通過位元組數來決定,如"中文abc",字元數是5,而位元組數是7.對於用++或--運算符來遍歷字元串的程式員來說,這簡直就是夢魘!

  Unicode: 學名為"Universal Multiple-Octet Coded Character Set",簡稱"UCS".UCS可以看作是"Unicode Character Set"的縮寫.

也是一種字元集/字元編碼方法,它統一用唯一的字元集來包含這個星球上多數語言的書寫系統.UCS向ASCII相容(即前128個字元是一致的),但並不相容DBCS,因為其他字元在UCS中被重新編碼(重新安排位置).

UCS有兩種格式:UCS-2和UCS-4.前者用2個位元組(16位)編碼,後者用4個位元組(實際上只用31位)編碼.USC-4前2個位元組都為0的部分稱為BMP(基本多語言平面),就是說BMP去掉前2個零位元組就是UCS-2.目前的UCS-4規範中還沒有任何字元被分配在BMP之外.(說白了,USC-4就是為當16位的USC-2都被分配完時候做再做擴展用的,現在還沒用到)

  UTF-8,UTF-16,UTF-32: "Unicode transformation format"(UTF) ,即Unicode的傳輸格式.Unicode規定了怎麼編碼字元,而UTF規定怎麼將一個Unicode字元單元映射到位元組序來傳輸或保存.

UTF-16UTF-32分別表示以16位和32位為一個Unicode單元進行編碼,其實UTF-16對應就是UCS-2,UTF-32對應就是UCS-4(UCS-2和UCS-4是陳舊的說法,應拋棄). 另外,通常說的Unicode就是指UTF-16.

UTF-8是關鍵!如果統一Unicode都用2位元組表示,英文字母覺得自己就很吃虧(高位元組始終是0位元組).UTF-8提供了一種靈活的解決辦法:以單位元組(8bit)作為編碼單元,變長多位元組編碼方式.如ASCII字母繼續使用1位元組儲存,中文漢字用3位元組儲存,其他最多可直6位元組.

UTF-16和UTF-32需要有位元組序標誌BOM(FEFF)解決大端小端問題.UTF-8沒有位元組序的問題(因為以1個位元組為單元).

 

===============================================================================

其他註意點:

DBCS準確說,應該是MBCS(Multi-Byte Chactacter System, 多位元組字元系統).

字元集(Charset)和編碼(Encoding)註意區別.如GBK,GB2312以及Unicode都既是字元集,也是編碼方式,而UTF-8只是編碼方式,並不是字元集.

Linux下The GUN C Library(從glibc 2.2開始)中寬字元wchar_t是以32位的Unicode(USC-4)表示.如寬字元"中"字為 "0x00004e2d".而Windows下的CRT使用寬字元仍是16位的.

 

知識要點二:關於Unicode的認知(加深對編碼的理解)

析Unicode和UTF-8 

一、首先說明一下現在常用的一些編碼方案:
1. 在中國,大陸最常用的就是GBK18030編碼,除此之外還有GBK,GB2312,這幾個編碼的關係是這樣的。
最早制定的漢字編碼是GB2312,包括6763個漢字和682個其它符號
95年重新修訂了編碼,命名GBK1.0,共收錄了21886個符號。
之後又推出了GBK18030編碼,共收錄了27484個漢字,同時還收錄了藏文、蒙文、維吾爾文等主要的少數民族文字,現在WINDOWS平臺必需要支持GBK18030編碼。
按照GBK18030、GBK、GB2312的順序,3種編碼是向下相容,同一個漢字在三個編碼方案中是相同的編碼。
2.  臺灣,香港等地使用的是BIG5編碼
3.  日本:SJIS編碼
二、Unicode
  如果把各種文字編碼形容為各地的方言,那麼Unicode就是世界各國合作開發的一種語言。
  在這種語言環境下,不會再有語言的編碼衝突,在同屏下,可以顯示任何語言的內容,這就是Unicode的最大好處。
  那麼Unicode是如何編碼的呢?其實非常簡單。
  就是將世界上所有的文字用2個位元組統一進行編碼。可能你會問,2個位元組最多能夠表示65536個編碼,夠用嗎?
  南韓和日本的大部分漢字都是從中國傳播過去的,字型是完全一樣的。
  比如:“文”字,GBK和SJIS中都是同一個漢字,只是編碼不同而已。
  那樣,像這樣統一編碼,2個位元組就已經足夠容納世界上所有的語言的大部分文字了。
UCS-2 與UCS-4
  Unicode的學名是"Universal Multiple-Octet Coded Character Set",簡稱為UCS。
  現在用的是UCS-2,即2個位元組編碼,而UCS-4是為了防止將來2個位元組不夠用才開發的。UCS-2也稱為基本多文種平面。
  UCS-2轉換到UCS-4只是簡單的在前面加2個位元組0。
  UCS-4則主要用於保存輔助平面,例如Unicode 4.0中的第二輔助平面
  20000-20FFF - 21000-21FFF - 22000-22FFF - 23000-23FFF - 24000-24FFF - 25000-25FFF -   26000-26FFF   - 27000-27FFF - 28000-28FFF - 29000-29FFF - 2A000-2AFFF - 2F000-2FFFF
  總共增加了16個輔助平面,由原先的65536個編碼擴展至將近100萬編碼。
三、 相容codepage
  那麼既然統一了編碼,如何相容原先各國的文字編碼呢?
  這個時候就需要codepage了。
  什麼是codepage?codepage就是各國的文字編碼和Unicode之間的映射表。
  比如簡體中文和Unicode的映射表就是CP936,點這裡查看官方的映射表。
  以下是幾個常用的codepage,相應的修改上面的地址的數字即可。
  codepage=936 簡體中文GBK
  codepage=950 繁體中文BIG5
  codepage=437 美國/加拿大英語
  codepage=932 日文
  codepage=949 韓文
  codepage=866 俄文
  codepage=65001 unicode UFT-8
最後一個65001,據個人理解,應該只是一個虛擬的映射表,實際只是一個演算法而已。
從936中隨意取一行,例如:
0x9993 0x6ABD #CJK UNIFIED IDEOGRAPH
前面的編碼是GBK的編碼,後面的是Unicode。
通過查這張表,就能簡單的實現GBK和Unicode之間的轉換。
四、UTF-8
  現在明白了Unicode,那麼UTF-8又是什麼呢?又為什麼會出現UTF-8呢?
  ASCII轉換成UCS-2,只是在編碼前插入一個0x0。用這些編碼,會包括一些控制符,比如 '' 或 '/',這在UNIX和一些C函數中,將會產生嚴重錯誤。因此可以肯定,UCS-2不適合作為Unicode的外部編碼。
  因此,才誕生了UTF-8。那麼UTF-8是如何編碼的?又是如何解決UCS-2的問題呢?
例:
E4 BD A0        11100100 10111101 10100000
這是“你”字的UTF-8編碼
4F 60          01001111 01100000
這是“你”的Unicode編碼
關於漢字按照UTF-8的編碼規則,分解如下:xxxx0100 xx111101 xx100000
把除了x之外的數字拼接在一起,就變成“你”的Unicode編碼了。
註意UTF-8的最前面3個1,表示整個UTF-8串是由3個位元組構成的。
經過UTF-8編碼之後,再也不會出現敏感字元了,因為最高位始終為1。
以下是Unicode和UTF-8之間的轉換關係表:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx、
Unicode編碼轉換到UTF-8,針對中文,簡單的把Unicode位元組流套到x中就變成UTF-8了。

續篇:

unicode在windows api中的應用
    實際上,常提到的Win32 API的名稱並不是它們的真實名稱。這些名稱僅僅是一些巨集,你可以在PSDK的頭文件中找到這些巨集對用的函數名稱。所以,如果PSDK的文檔提到一個函數,如CreateFile,開發人員應該意識到它僅僅是一個巨集。它的真實名稱是CreateFileA和CreateFileW。是的,它代表了“兩個”函數名,而不是一個,是同一個函數在不同Win32函數的兩個不同的版本。以'A'結尾的函數接受ANSI字元串(char *),即Unicode字元串(wchar_t *)而在vs中可以用WCHAR巨集代替,即wchar_ts型字元串。兩種版本的函數都在模塊kernel32.dll中實現,如果你的編程環境是Unicode則,則巨集CreateFile在編譯是會被CreateFileW代替,否則用CreateFileA代替。

PSDK的字元串解決方案:TCHARs
    為了避免為不同的windows操作系統開發不同版本的PSDK,微軟制訂了一個統一的字元串類型TCHARs。TCHAR以及其他的相應的巨集在頭文件WinNT.h中有定義。程式員在程式中不需要為使用char還是wchar_t而糾結,只需要使用巨集TCHAR就可以了。根據Unicode環境是否存在,編譯器會自動進行相應的轉換。同樣道理,程式員不需要為使用'A'還是'W'型Win32 API函數糾結。

對於較早期的系統均採用ACSI編碼,而在新型系統中則都統一為unicode編碼(如:手機系統)

 

知識要點三: L"......", _T(), _TEXT ,TEXT()

L"......": L是表示字元串資源轉為寬字元的保存(通常轉為unicode),卻未必是unicode字元,這與編譯器實現相關。

_T(" ……") 是一個適配的巨集     #ifdef _UNICODE(當系統環境是unicod下) _T就是L   而當系統環境是ACSI  _T就是ANSI的。(有便於早期windows系編程文件的移植,達到新舊系統交互)

_T、_TEXT、TEXT 三者效果相同

tchar.h是運行時的頭文件,_T、_TEXT 根據_UNICODE來確定巨集
winnt.h是Win的頭文件根據,TEXT 根據UNICODE 來確定巨集

如果需要同時使用這3個巨集,則需同時定義 UNICODE 和 _UNICODE
VS2010以後的版本中 ,設置:項目–屬性–配置屬性–常規–字元集–使用Unicode字元集, 那麼編譯器命令選項中的確同時加入了_UNICODE和UNICODE。

知識要點四: c++ 的cout 與 wcout

cout << "hello world!" << endl; //ACSI 編碼輸出

cout << L“hello world!” <<endl;// unicode 輸出

當輸出雙位元組編碼到控制台時,cout輸出的將是地址而並非內容這時就要用到wcout;

改為:

cout << "hello world!" << endl; //ACSI 編碼輸出

wcout << L“hello world!” <<endl;// unicode 輸出

 

知識要點五:編譯連接過程

1.預處理 生成.i文件

C++的預處理是指在C++程式源代碼被編譯之前,由預處理器對C++程式源代碼進行的處理。這個過程並不對程式的源代碼進行解析。

這裡的預處理器(preprocessor)是指真正的編譯開始之前由編譯器調用的一個獨立程式。

預處理器主要負責以下的幾處

1.巨集的替換

2.刪除註釋

3.處理預處理指令,如#include,#ifdef

 2.編譯和優化 生成彙編.s原文件

詞法分析 -- 識別單詞,確認詞類;比如int i;知道int是一個類型,i是一個關鍵字以及判斷i的名字是否合法
語法分析 -- 識別短語和句型的語法屬性;

語義分析 -- 確認單詞、短語和句型的語義特征;

代碼優化 -- 修辭、文本編輯;

代碼生成 -- 生成譯文。

3.生成.o目標文件

彙編過程實際上指把彙編語言代碼翻譯成目標機器指令的過程。

在最終的目標文件中

除了擁有自己的數據和二進位代碼之外,還要至少提供2個表:未解決符號表和導出符號表,分別告訴鏈接器自己需要什麼和能夠提供什麼。

編譯器把一個cpp編譯為目標文件的時候,除了要在目標文件里寫入cpp里包含的數據和代碼,還要至少提供3個表:未解決符號表,導出符號表和地址重定向表。
未解決符號表提供了所有在該編譯單元里引用但是定義並不在本編譯單元里的符號及其出現的地址。
導出符號表提供了本編譯單元具有定義,並且願意提供給其他編譯單元使用的符號及其地址。
地址重定向表提供了本編譯單元所有對自身地址的引用的記錄。

4.鏈接

由彙編程式生成的目標文件並不能立即就被執行,其中可能還有許多沒有解決的問題。例如,某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變數或者函數調用等);在程式中可能調用了某個庫文件中的函數,等等。所有的這些問題,都需要經鏈接程式的處理方能得以解決。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Promise API 簡介 譯者註: 到處是回調函數,代碼非常臃腫難看, Promise 主要用來解決這種編程方式, 將某些代碼封裝於內部。 Promise 直譯為“承諾”,但一般直接稱為 Promise; 代碼的可讀性非常重要,因為開發人員支出一般比電腦硬體的支出要大很多倍。 雖然同步代碼更容 ...
  • 在HTML5的規範中,我們可以通過為元素增加`draggable="true"`來設置此元素是否可以進行拖拽操作,其中圖片、鏈接預設是開啟的。 1. 拖拽元素:設置了`draggable="true"`的元素 當拖動某元素時,將依次觸發下列事件: 1. dragstart(按下滑鼠並開始移動滑鼠時, ...
  • 13.Label的作用是什麼?是怎麼用的? label標簽來定義表單控制間的關係,當用戶選擇該標簽時,瀏覽器會自動將焦點轉到和標簽相關的表單事件上。 <label for="Name">Number:</label> <input type="text" name="Name" id="Name"/ ...
  • 開篇 天天逛博客園,就是狠不下心來寫篇博客,忙是一方面,但是說忙能有多忙呢,都有時間逛博客園,寫篇博客的時間都沒有?(這還真不好說) 每次想到寫一篇新的設計模式,我總會問自己: 1,自己理解了嗎? 2,我能以一種簡單且有邏輯的方式把它說出來嗎? 不說做到有的放矢,但是一本正經的胡說八道還是要有吧,起 ...
  • Cache緩存在電腦領域是一個被普遍使用的概念。硬體中CPU有一級緩存,二級緩存, 瀏覽器中有緩存,軟體開發中也有分散式緩存memcache, redis。緩存無處不在的原因是它能夠極大地提高硬體和軟體的運行速度。在項目開發中,性能慢的地方常常是IO操作頻繁的地方,讀取資料庫是我們常見的消耗性能的 ...
  • Redis是個好東西,經過上兩個星期的研究和實踐,目前正在項目里大規模的替換掉原來的本地記憶體cache。但是替換過程中卻發現,Redis這東西高端,大氣上檔次,似乎不是我想象里的使用方法。 在沒有深入Redis之前,在我的概念里,緩存,就是key-value。而使用方式肯定不需要改動多少代碼,一切都 ...
  • 背景 目前公司項目中有用到activemq,兩台機器上分別通過共用文件方式搭建了master-slave集群,但兩台機器之間並未組建broker cluster,而是在客戶端通過軟負載的方式隨機選擇一組提供服務來達到集群擴展的目的。 上面的方案主要問題在於需要通過軟負載去實現分散式的負載均衡演算法,需 ...
  • 單例模式的作用就是在整個應用程式的生命周期中,單例類的實例都只存在一個,同時這個類還必須提供一個訪問該類的全局訪問點。 首先創建一個單例類,可以直接使用這個單例類獲得唯一的實例對象,也可以繼承該類,使用子類實例化對象。 下麵的代碼使用子類進行實例對象創建 Singleton.php文件 Single ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...