reeBSD 是一種類UNIX操作系統(tǒng),先進的網(wǎng)絡(luò)、安全性和存儲方面的特色使得它成為許多大型網(wǎng)站以及最普遍的嵌入式網(wǎng)絡(luò)與存儲設(shè)備的平臺選擇。今天筆者用vmware很順利的進行了安裝,但也存在一個致命的問題,即中文亂碼,一直未能解決。
一、安裝虛擬機
安裝VMware虛擬機
我這里使用的版本是vmware15 pro,雙擊運行,一路 next,即可。
二、下載 FreeBSD13.1
官網(wǎng)下載:https://www.freebsd.org/
三、安裝FreeBSD13.1
1、打開vmware,點擊新建虛擬機。
2、不會的默認就好。
3、選擇你下載的安裝文件(.iso)
Bug:明明下載的是Freebsd13.1,下圖卻顯示FreeBSD version 10 and earlier64-bit detected.
不過不影響安裝過程。
4、選擇安裝路徑
5、指定磁盤大小
6、點擊Finish,
7、開始安裝FreeBSD
8、FreeBSD安裝教程網(wǎng)上有很多,這里不再一一展示。
四、安裝完成后
英文正常,中文亂碼,不知如何配置
前段時間,我在其他平臺發(fā)布了一篇介紹“輕松玩轉(zhuǎn)windows控制臺”的文章,被不少粉絲私信批評,甚至還有個別言辭激烈的。
是因為文章寫的不好嗎?應(yīng)該不是,因為兩天時間就突破了90多個收藏,說明大家還是挺喜歡這種實用的、技巧性的技術(shù)文章,但是為什么還是被批評呢?
因為文章通篇以代碼為主,雖然功能很多,但并沒有詳細的解釋,導(dǎo)致大家在用的時候,有很多地方出現(xiàn)問題。
基于此,我決定圍繞“輕松玩轉(zhuǎn)windwos控制臺”主題,寫一個系列性的文章,通過詳細介紹控制臺方面的各種操作技巧,從而提高c語言初學(xué)者的學(xué)習(xí)興趣。
我大致的規(guī)劃主要分為這幾塊:
第一個系列是對控制臺的各種“魔改”,包括標題修改、輸出文字的字體大小、字體樣式、文字顏色、屏幕背景顏色、控制臺窗口的移動、居中顯示、窗口大小、以及文字在窗口內(nèi)任意位置的顯示、中文字符的正確顯示等等。
這個系列看完之后,就開始發(fā)布控制臺窗口的圖形化操作的系列文章。
比如控制臺如何控制鼠標、鍵盤,如何將屏幕背景改成彩色圖片,如何在控制臺內(nèi)實現(xiàn)動畫效果等等。為后面第三個系列控制臺圖形游戲開發(fā)打下基礎(chǔ)。
第三個系列我準備圍繞一個游戲的一步步實現(xiàn)來發(fā)布文章,可以是一個超級瑪麗游戲,或者一個臺球游戲都可以,游戲本身我早就已經(jīng)調(diào)測運行。
話不多說,開始我們的第一個“玩轉(zhuǎn)之旅”。
本段是控制臺的基礎(chǔ)介紹,大家不感興趣,可以直接跳過,也不影響后面閱讀。
我們用c語言剛剛編寫程序時,一般都是會在屏幕上彈出一個黑乎乎的窗口,然后黑底白字的顯示一行行內(nèi)容。這個黑乎乎的窗口界面,我們一般稱為“控制臺”,英文是Console,而在控制臺界面內(nèi)運行的程序,我們一般稱為“控制臺程序”。
我們把通過控制臺運行程序的方式,稱為CUI,即Command User Interface,命令用戶界面。相對應(yīng)的,還有一種圖形化用戶界面,GUI,即Graphics User Interface。有時候,我們也把CUI模式稱為CLI模式,即Command Line Interface ,命令行界面,都可以。
我們一般通過執(zhí)行cmd.exe命令進入控制臺窗口,從而運行控制臺程序。
以上這些都是最基礎(chǔ)的概念,后面文章中用到什么就介紹什么,盡量降低復(fù)雜度。
我們先從控制臺標題入手。
控制臺程序啟動時,窗口上方會有一個title(標題)。這個title顯示的是程的絕對路徑和程序名稱。這個title字符串我們稱為“原始的窗口標題名稱”,即original title。如圖:
這個字符串顯示的真的很丑,我們一般都會改成我們喜歡的內(nèi)容,如下所示:
圍繞控制臺窗口的標題,可以實現(xiàn)三個功能。分別是“修改控制臺標題”、“獲取控制臺標題”、“獲取原始控制臺標題”。控制臺標題被我們修改以后,有時候我們可能需要知道程序所在的絕對路徑和程序名稱,通過“獲取原始控制臺標題”這個功能就很方便。下面來看一下這3個功能對應(yīng)的函數(shù)如何使用的, 并簡單的介紹下windows的win32API調(diào)用。
注釋:如果對Win32API不感興趣,可以直接復(fù)制代碼使用即可。
若修改控制臺當(dāng)前標題,需要用到的函數(shù)原型如下:
BOOL SetConsoleTitle(LPCTSTR lpConsoleTitle);
在學(xué)習(xí)如何使用這個函數(shù)時,我們先介紹一下一些windos程序設(shè)計的基本概念,以便于后面能熟練的使用各種控制臺函數(shù)。
對初學(xué)者而言,使用windows的API函數(shù),應(yīng)該會有段很痛苦的適應(yīng)過程。因為微軟把c/c++的基本數(shù)據(jù)類型和自定義數(shù)據(jù)類型,都采用了匈牙利命名法進行了封裝,我本人對這個命名法是非常厭惡的,但是還是要學(xué)習(xí)下。[狗頭]
函數(shù)名: SetConsoleTitle,設(shè)置當(dāng)前控制臺標題。參數(shù)類型:LPCTSTR,這是微軟開發(fā)團隊自定義的數(shù)據(jù)類型,我們來分析下。
L表示long類型,用來表示長整型數(shù)據(jù);P是pointer,表示指針類型;LP表示長整型指針(即32位或64位指針類型,取決于系統(tǒng)是32位或64位);C表示const,LPC表示這個指針類型指向的數(shù)據(jù)是常量數(shù)據(jù);T是type,用來表示通用類型的含義(泛型),表示有unicode版本,或者ANSI版本,暫時不用理會;STR是字符串string的含義。
下面我列個表,就看的比較清爽了。
由此可知,LPCTSTR,就是一個指向字符串常量的指針(字符串本身就是常量數(shù)據(jù)),可以近似理解成c語言中的const char*。
返回值:BOOL
BOOL,就是布爾類型,其實就是一個int類型:
typedef int BOOL;
如果真,就是TRUE,假就是FALSE。其實TRUE就是1,F(xiàn)ALSE就是0。
本函數(shù)的參數(shù)是輸入型參數(shù)(什么是輸入型參數(shù)和輸出型參數(shù),可以查看我的這篇文章:c語言解剖課:函數(shù)的輸入?yún)?shù)和輸出參數(shù))
這個參數(shù)在使用之前,其所在的外部變量是一個字符指針類型,必須被初始化,并且已經(jīng)存放了新的標題字符串,字符個數(shù)必須小于 64K(等于64*1024個字節(jié),誰能用這么多?)當(dāng)函數(shù)執(zhí)行成功,當(dāng)前標題修改成新的字符串時,就返回為TRUE,執(zhí)行失敗就返回FALSE。
我們來寫一個演示代碼:
#include <stdio.h>
#include <windows.h>
int main() {
LPCSTR newStr = "hello,title!" ;
if(SetConsoleTitle(newStr) == TRUE){
printf("Title is changed!\n");
}
system("pause");
return 0;
}
首先,文件的后綴需要是.cpp或其他C++源文件的格式,而不能是.c的后綴了。
其次,需要包含windows.h頭文件,因為對控制臺操作的函數(shù),都是這個頭文件提供的。我們需要先定義一個字符串,存放新的標題。
當(dāng)然,你也可以用 char* 來定義,但是建議最好和windows編程風(fēng)格保持一致,就用這個非常別扭的 LPCSTR類型,然后判斷是否設(shè)置成功。
隨后的System函數(shù),參數(shù)是一個字符串,這個字符串實際上就是一個DOS命令,這里是一個暫停命令。如果沒有這條語句,運行這個程序時,屏幕會一閃而過。運行效果如圖:
窗口中的“請按任意鍵繼續(xù)...”,就是system("pause");這行代碼中的pause暫停命令起的作用。當(dāng)然,為了簡潔,你也可以直接寫成如下形式:
#include <stdio.h>
#include <windows.h>
int main() {
SetConsoleTitle("hello,title!");
system("pause");
return 0;
}
但是,最好不要這么寫,因為隨著功能越來越強大(復(fù)雜),會很不利于維護。那么我們想顯示中文行不行?我們先測試一下,請看下面這段代碼:
#include <windows.h>
int main() {
SetConsoleTitle("你好世界");
system("pause");
return 0;
}
看一下執(zhí)行的效果,如圖:
控制臺標題輸出的是亂碼,當(dāng)然,有可能你的編譯器運行后,輸出的是中文。沒關(guān)系,繼續(xù)往下看就好了。為什么會這樣?不用急。我們右擊標題欄,點“屬性”進去,注意看,下面這個截圖中的紅框,顯示了系統(tǒng)當(dāng)前使用的編碼。
代碼頁,即Code Page,簡寫為CP,表明了當(dāng)前控制臺的編碼情況。如果是437,則是美國英語;如果是936,則是ANSI/OEM - 簡體中文默認的GBK編碼;如果是950 ,則是繁體中文;如果是65001,則是UTF-8編碼。當(dāng)然,代碼頁有很多種,我們只需要關(guān)心一個就可以了,就是UTF-8編碼。用列表顯示代碼頁標識符和編碼標準,會更清楚:
如果控制臺顯示亂碼,我們可以將當(dāng)前代碼頁設(shè)置為65001,即UTF-8,就可以正確顯示中文。注意,并不是因為當(dāng)前代碼頁是936,所以就顯示亂碼。
顯示亂碼是編譯器的編碼規(guī)范和系統(tǒng)的編碼規(guī)范不一致,需要手動修改編碼規(guī)范。通過下面介紹的方法,通過修改代碼頁為UTF-8,可以快速解決問題,但不是唯一的解決辦法。
函數(shù)SetConsoleCP是設(shè)置控制臺輸入字符使用的編碼標準,SetConsoleOutputCP是設(shè)置控制臺輸出字符的編碼標準,函數(shù)原型如下:
BOOL SetConsoleCP(UINT wCodePageID);
BOOL SetConsoleOutputCP(UINT wCodePageID);
參數(shù)類型:UINT
微軟將基本數(shù)據(jù)類型int封裝成INT,約定為32位的有符號整數(shù),取值范圍是 -2147483648 到 2147483647。即:
typedef int INT;
而無符號整型unsigned int 被封裝成UINT,約定為32位無符號整數(shù),取值范圍為 0 到 4294967295。即:
typedef unsigned int UINT;
參數(shù)的值就是代碼頁的標識符,比如要顯示中文,UINT變量的值就是655001即可。
返回值是BOOL類型,前已介紹過,如果執(zhí)行成功,則返回TRUE,否則返回FALSE。
舉例如下:
#include <stdio.h>
#include <windows.h>
int main() {
SetConsoleCP(65001);//控制臺標題
SetConsoleOutputCP(65001);//輸出內(nèi)容
LPCSTR newStr = "你好世界!" ;
if(SetConsoleTitle(newStr) == TRUE){
printf("標題修改成功!\n");
}
system("pause");
return 0;
}
我們看下執(zhí)行結(jié)果:
我們通過SetConsoleOutputCP,設(shè)置了輸出字符的代碼頁(Code Page,CP)為UTF-8,所以prinftf函數(shù)的輸出內(nèi)容,正常顯示中文了,同樣的輸入字符顯示中文,通過SetConsoleCP就可以了。
進一步的,我們在程序里,如果需要顯示中文,我們需要知道當(dāng)前代碼頁的數(shù)值是不是65001,如果不是,我們需要設(shè)置成6501。我們?nèi)缰獣阅兀?/p>
可以通過GetConsoleCP獲取輸入狀態(tài)的代碼頁,GetConsoleOutputCP獲取輸出狀態(tài)的代碼頁,從而進行判斷。函數(shù)原型如下:
UINT GetConsoleCP(void);
UINT WINAPI GetConsoleOutputCP(void);
這兩個函數(shù)沒有參數(shù),返回值是UINT類型,也就是當(dāng)前所用的代碼頁標識符。第一個函數(shù)返回的是輸入模式的代碼頁標識符,第二個函數(shù)返回的是輸出模式的代碼頁標識符。通過對代碼頁判斷,然后始終保持當(dāng)前代碼頁標識符為65001即可。
示例代碼如下:
#include <stdio.h>
#include <windows.h>
int main(){
UINT codePage = GetConsoleOutputCP();
if(codePage != 65001){
SetConsoleCP(65001);
SetConsoleOutputCP(65001);
}
LPCSTR newStr = "你好,世界!" ;
if(SetConsoleTitle(newStr) == TRUE){
printf("標題修改成功!\n");
}
return 0;
}
我們現(xiàn)在可以隨心所欲的修改窗口標題名了,并且也可以正確顯示中文內(nèi)容了。接下來,我們?nèi)绾潍@取當(dāng)前窗口的標題名呢?
通過GetConsoleTtile函數(shù),就可以獲取道當(dāng)前控制臺的標題。函數(shù)原型如下:
DWORD GetConsoleTitle(LPTSTR lpConsoleTitle, DWORD nSize );
函數(shù)名: GetConsoleTitle;第一個參數(shù)類型:LPTSTR
這個參數(shù)的類型和上面介紹的SetConsoleTtitle函數(shù)的LPCTSTR類型,很相似。本質(zhì)的區(qū)別在于字母C,LPCTSTR是指向常量字符串,不允許修改,屬于輸入性參數(shù)。而LPTSTR類型,指向的是可以被修改的一段區(qū)域,一般我們定義成一個數(shù)組,或者一段動態(tài)內(nèi)存分配區(qū)域。
第二個參數(shù)類型:DWORD
windows編程規(guī)范中,把8個位(bit)當(dāng)作1個字節(jié)BYTE,把2個BYTE當(dāng)作1個字,即WORD,2個WORD為雙字,即DOUBLE WORD,簡寫為DWORD。
1個DWORD類型的變量,等于4個字節(jié),表示32 位無符號整數(shù)。 范圍為 0 到十進制4294967295。其實就是c語言中的 unsigned long ,實際上也是這樣定義的:
typedef unsigned long DWORD;
返回值和第二個參數(shù)一樣,也是DWORD。
第一個參數(shù)是輸出型參數(shù),接受函數(shù)的返回數(shù)據(jù)。(什么是輸入型參數(shù)和輸出型參數(shù),可以查看我的這篇文章:c語言解剖課:函數(shù)的輸入?yún)?shù)和輸出參數(shù)),用來存放獲取的當(dāng)前窗口標題字符串。
第二個參數(shù)是要顯式的說明第一個參數(shù)所指向的內(nèi)存區(qū)域的大小,以字符為單位。
如果函數(shù)成功,則返回值為控制臺窗口標題的字符個數(shù)。如果函數(shù)失敗,則返回值為零。
下面是一段演示代碼:
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <malloc.h>
int main(){
DWORD charactor_total = MAX_PATH;
LPTSTR title = (LPTSTR)malloc(sizeof(CHAR)*MAX_PATH);
DWORD n = GetConsoleTitle(title,charactor_total);
printf("標題字符個數(shù):%d\n",n);
system("pause");
return 0;
}
程序執(zhí)行結(jié)果如圖:
大家可以數(shù)一下標題的字符數(shù),是不是91個。[憨笑]
當(dāng)控制臺窗口標題被修改后,我們有時候需要獲取窗口的原始標題,也就是程序的絕對路徑和程序全名(包括后綴),這非常有用,因為可以根據(jù)這個字符串提取出程序文件所在的目錄。
獲取原始標題的函數(shù)為GetConsoleOriginalTitle(),當(dāng)窗口標題未被修改時,GetConsoleTtile()和它的功能是一樣的,當(dāng)被修改后,就出現(xiàn)了本質(zhì)的差別,所以小心不要用錯函數(shù)。
函數(shù)原型如下:
DWORD GetConsoleOriginalTitle( LPTSTR lpConsoleTitle, DWORD nSize);
這個函數(shù)的參數(shù)和用法,就不再講解,和GetConsoleTitle函數(shù)類似。
今天因為是第一篇文章,特別是對windows程序設(shè)計比較陌生的讀者,對微軟的自定義數(shù)據(jù)結(jié)構(gòu)估計也會不適應(yīng),所以本文對微軟自定義的數(shù)據(jù)結(jié)構(gòu)講解的比較詳細,后面文章都會對出現(xiàn)的數(shù)據(jù)結(jié)構(gòu)進行詳細講解,感興趣的讀者可以持續(xù)關(guān)注我,感謝。
段譽,2024年2月9日(除夕),寫于合肥。