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