2010年2月9日 星期二

解決「命令提示字元」亂碼的問題

在公司的電腦的「命令提示字元」會出現亂碼的情況,為了解決這一個問題,在網路上找到解答啦!

Windows版本為中文,語系設定為中文語系沒有錯誤

所以請使用者用chcp指令查詢語系,結果是cmd視窗本身跑英文語系所以視窗會出現亂碼,因為中文字抓不到...

解決方式:

1.在命令提示字元視窗處的標題按右鍵選預設值,然後將預設字碼頁改成950
2.用chcp指令直接更改頁碼,但僅限該命令提示字元視窗。

方法二:
在開始運行裡輸入regedit 找到分支HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe

再雙擊右邊CodePage選項將數值改為十進制950

參考資料:
CMD環境恢復中文 dos(cmd)中文亂碼解決方案!
開啟CMD視窗之後中文會顯示亂碼
Windows下Chcp命令的用法

2010年2月2日 星期二

nmake 簡單教學

在Linux下使用的是make,而在Windows下使用的就是nmake啦!

在語法上,有一些不同,這裡留下一些記錄!

有很多語法跟在Linux下的make是相同的,可以參考Makefile - 簡單教學

!IFDEF
了解後面的變數是否有被定義,若有被定義的話,就執行後面要執行的指令!

!IFNDEF
了解後面的變數(可以是全域變數)是否有被定義,若有沒有被定義的話,就執行後面要執行的指令!

!IF
在後面的判斷式是否成立

!ENDIF
做為!IFDEF的結尾

!ELSE
與!IF, !ENDIF一起使用的語法

!ELSEIF
是跟!IF與!ELSEIF與!ENDIF一起使用的,與!ELSE不同的是,後面可以接判斷式

!ERROR
顯示出後面的錯誤訊息,並且停止nmake的執行

!MESSAGE
顯示出後面的訊息,但與!ERROR的差別在於,並不會停止nmake的執行!

!INCLUDE
把後面接的檔案的內容貼到目前的位置上

!ELSEIFDEF
!ELSE IFDEF 的同義資料表。

!ELSEIFNDEF
!ELSE IFNDEF 的同義資料表。

!UNDEF macroname
取消定義 macroname。

而要讀取出變數的內容的話,則必需使用$()把它包起來!

下面就一個編譯.cpp的例子
food.h、food.cpp、main.cpp請參考這裡
另外,要注意的一點是,這裡的[tab]指的是,一定要用tab鍵產生出來的,在linux的make,指令前一定要用[tab]鍵,但是,在nmake似乎沒有這一個限制,但是,基於習慣與相容性,我還是會用[tab]鍵。
mymake
# Program, flags, etc.
CC      = cl
CCFLAGS = /c
OBJ     = main.obj food.obj
TARGET  = main.exe

everything: $(TARGET)

clean:
[tab]del $(OBJ) $(TARGET)

all: clean everything

food.obj: food.cpp
[tab]$(CC) $(CCFLAGS) food.cpp /Fo$@

main.obj: main.cpp
[tab]$(CC)  $(CCFLAGS) main.cpp /Fo$@

main.exe: $(OBJ)
[tab]$(CC) $(OBJ) /Fe$@


而,可以透過隱含規則再簡化Makefile的內容,請參考Makefile - 簡單教學
# Program, flags, etc.
CC      = cl
CCFLAGS = /c
OBJ     = main.obj food.obj
TARGET  = main.exe
FLAGS   =

!IF "$(NAME)" == "koba"
!MESSAGE  You want to try Koba phase
FLAGS = /DKOBA
!ELSEIF "$(NAME)" == "caspar"
!MESSAGE  You want to try caspar phase
FLAGS = /DCASPAR
!ELSE
!MESSAGE  You want to try other phase
!ENDIF

everything: $(TARGET)

clean:
[tab]del $(OBJ) $(TARGET)

all: clean everything

.cpp.obj:
[tab]$(CC) $(CCFLAGS) $< /Fo$@ $(FLAGS)

$(TARGET): $(OBJ)
[tab]$(CC) $(OBJ) /Fe$@


這裡要注意的是,在Windows下的nmake與Linux下的make的語法確實有所不同!例如在隱含規則就有非常大的不同。可以參考Makefile - 簡單教學
像是$<(只能用在隱含規則中),而在make就沒有這一個限制!

可以執行以下的命令,看出輸出結果有所不同!
下面三個nmake指印選一個執行
$ nmake /f mymake all name=caspar
$ nmake /f mymake all name=koba
$ nmake /f mymake all

最後再執行編譯出來的結果

$ main.exe

[2011.09.18 補充]
!IF DEFINED(DATE) && DEFINED(TIME)
確認DATE與TIME變數同時有被定義

!IF EXIST(File.txt)
確認File.txt存在時,才做以以下被包住的程式碼

[2011.12.07 補充]
參考簡單的 nmake 使用的 Makefile 範例得知,nmake有一個替換功能
巨集替換

[2012.01.03 補充]
在nmake的指令中,如果要定義巨集有包含空格,請以引號括住這些定義。

這裡要解釋一下,

target : prerequisites
command

1. $@表示target的第一個名字,也就是代表目前的標的(target)
2. $**目前目標的所有相依性(一般linux下是用$^來表示
3. $?同一個規則的所有先決條件名,但是只有原始程式碼改過的比obj檔新才會符合,也就是比target還新的先決條件檔案。
4. $*同一個規則的第一個先決條件,但是不包含副檔名
5. $<具有比目前目標較晚之時間戳記的相依性檔案。只有在介面規則的命令中才有效。


參考資料:
nmake的用法
筆記:學習vc之cl, link, rc常用參數
命令行編譯工具NMAKE
對於.NET中的NMake和Makefile的理解
NMAKE Reference
[C++] nmake 最簡單範例
Nmake Tool
簡單的 nmake 使用的 Makefile 範例

2010年1月30日 星期六

程式Compile流程

一般寫程式的流程為:編譯-載入-執行。

編譯Compile流程

一個完整的編譯過程通常需要包含以下的4個程序:

1. Preprocess 預處理器。
2. Compiler 編譯器。會處理副檔名.c的檔案,產生副檔名.as的組譯檔。
3. Assembler 組譯器。會處理副檔名.as的組譯檔,產生副檔名.o的中間檔。
4. Linker 連結器。會處理副檔名.o或.obj的中間檔。

預處理器(Preprocess)

預處理器會在進行編譯前先處理原始碼內像#ifdef、#define相對簡單的詞句替換、和一些巨集代換的功能。

編譯器(Compiler)

編譯器是將高階語言所寫的原始程式,翻譯成機器語言組成的目的程式。在編譯過程中,會執行下列的步驟:

* 語彙分析(Lexical analysis)。分析程式中每一個字眼(word):註解(comment, 在編譯過程會被編譯器忽略掉)、關鍵字(keyword, 如int、for、while等)、常數(constant, 如1、12、”embedded”等)、運算子(operator, 如+、-、*、/等)。

* 語法分析(Syntax analysis)。主要是將程式符號,轉換成階層式的語法樹(Syntax tree)符號表示。在這個語法樹中,在正常情況下,階層最高的節點(node)為assign的符號,其餘的節點為其他的運算元符號,而葉子(leaf)就都是變數的標記(token)。

* 語意分析(Semantic Analysis)。是藉由語法樹(Syntax tree)來分析程式的邏輯與語法是否符合規定。這個階段就是用來分析程式的「文法」是否正確,已經從文字符號的階段進入了程式語意的判別。

* 中間碼的產生(Intermediate code generate)。是從語法樹(Syntax tree)中,以一個節點(node)為基本單位,從最底層的節點依序往上,拆解成一個個最基本的運算式,而每一個節點也會賦予一個暫時性的符號。

* 程式碼的最佳化(code optimize)。基本上就是減少一些不必要的暫時性節點符號。當然,另外還有一些特別的最佳化演算法也會在這個階段使用,例如針對迴圈邏輯的最佳化有三種知名的演算法: code motion、induction variable、strength reduction;因為迴圈邏輯在語法上是最沒有執行效率的語法之一,因此需要特別的最佳化。或者,有時候編譯器會調整程式的前後順序,為了在下一階段程式碼的產生過程中,暫存器的使用數目降低。

* 程式碼的產生(code generate)。以c語言為例,這裡就是將最佳化後的中間碼,搭配微處理器的暫存器,逐一轉換成組合語言。

組譯器(Assembler)

組譯器會將組合語言的原始程式,翻譯成機器語言組成的中間檔 ".obj"或".o"。

連結器(Linker)

連結器會將中間檔 obj files 連結起來,找到symbol(函式,變數名)與程式庫(shared obj)中的副程式,產生可執行的obj檔(executable obj)。

小結

「編譯-載入-執行」方式的缺點是產生的 List檔,其定址 Address 為相對位址,也就是說,每個source的各個section (Absolute section除外) 都由位置 "0" 排起,非常不方便作程式 debug用。但是產生 .obj 檔仍然有一個優點,就是會收錄 .lib 檔,以後要重新 make 時,只需要直接指定使用的 library 即可。

因為目前電腦速度已經非常快,對所有檔案做編譯及連結幾乎都是瞬間完成,所產生的 List檔已包含最終的正確位址,用來作為程式的 Debug 非常好用,如果沒有特殊需要的話,已經沒有必要產生中間檔 .obj。

參考資料:
程式Compile流程
Gcc編譯流程解析

2010年1月28日 星期四

extern與static的問題

在extern與static是不能同時形容一個變數。

而在extern基本上,一定是形容全域變數,若不是形容全域變數,就沒有意思了,原因是,他就是說在其它.c檔有宣告某一個變數,可以讓我們使用,倘若在其它的檔案,他不是全域變數,那就沒有辦法存取它,那還是會發生錯誤!

你或許對以下文章有興趣:
static
extern

參考資料:
static函數的用途?
不懂C語言的extern是做什麼的

2010年1月23日 星期六

vim 的 search 搜尋功能

若要在一個文件中,同時找到「肥仔魚」、「肥仔豬」、「肥仔貓」,有一個規則的字串,可以使用正規表示式。
/肥仔[魚豬貓」
就可以找到,[]表示的是一個字元,但是,可以被選擇的就是中間的那三個字元~

如果我們今天要找的是英文文件,想要搜尋的 lemon tree、lemon soup、lemon juice 的話,就必需使用「\|」這個「或(or)」運算
/lemon tree\|lemon soup\|lemon juice

想要搜尋的東西會是像這樣 1234- 1234-5 1234-56 1234-567 1234-789 1234-2399 1234349898。

我們大概可以分析一下,這個字串大約是以1234開頭,然後跟上或不跟上「-」之後,再接上一堆數字。先把指令給大家:

/1234[-]\{,1}[0-9]\{1,}

或是:

/1234-[0-9]\{1,}\|1234[0-9]*

我們可以先看第一個方法,在第一個方法裡面用了兩個特別的東西:\{1,} 和 \{,1}。在前面我們剛剛說中括號後面如果不跟上任何東西就表示中括號裡面的某一字元要出現一次。而現在\{n,m}就是用來表示可以對中括號裡面的字元挑選幾次。(選幾次就會出現幾個字元)

像\{1,}表示至少要出現一次(至少選一次),而\{,1}表示最多出現一次(最多選一次,不選的話就代表中括號裡面的字元都不出現)。當然我們也可以寫成\{3,9},這就表示可以在中括號裡面的字元挑選三到九次,也就是會出現三到九個中括號裡面出現的字元。

而在第二個方法裡面,則在中括號後面緊跟著一個「*」,這其實代表\{0,},也就是 0 個到任意個都可以的。而中括號裡面我們用了「0-9」這就表示 0123456789 都可以。同樣對於字母我們也可以用「a-z」表示所有小寫英文字母和「A-Z」用來表示所有的大寫字母。

儘管如此,我們可能還是不滿足。舉例來說,我可不可以只找以「傷寒」開頭的字串或是以「主之。」結尾的字串?

假如我們只想找以「傷寒」開頭的字串,可以這樣輸入搜尋命令:

/^傷寒

「^」開頭就表示要找的東西是一行的開頭。

但是有時候為了排版需要,在「傷寒」前面可能會出現一些空白或是 tab ,如果我們只是用上面的指令就不夠。所以配合我們剛剛的中括號帶次數的方式就可以做到:

/^[\t <Space>]*傷寒

<Space>是為了網頁展示可能看不到所以用<Space>來表示空白。而「\t」則是 vim 中用來表示 tab ,所以上面的命令就代表一行的開頭有任意個空白或是 tab 的混合,而其後跟上「傷寒」兩個字。

那結尾呢?還記得我們怎麼把游標移到行尾嗎?就是用「$」這個符號。所以要找以「主之」結尾的字串就可以用:

/主之$

不過你可能還是不滿意,比方說 vim 可不可以幫我找出忘了在句末補上句號的地方?可以!我們先看指令:

/.*[^。]$

或是不要看到整句:

/[^。]$

中括號裡面的字元 X 如果前面出現「 ^ 」就表示除了 X 以外的字元都算是想要比對的字元。所以上面第一個的指令就表示搜尋句末沒有句號的行(這一行至少要有一個字元,因為用了[^。]$,而不是 [^。]*$)。第二個指令比較不一樣是只搜尋句末不是以句點結束的字元。其實看哪個指令對你來說比較「順眼」就用哪個。

有關這些特殊符號還有許多更 powerful 的功能,你可以在 vim 的指令模式下打 :h pattern 看更詳細的資料。或用 :h regular 看有關 regular expression 的詳細說明。

現在,你已經可以用 vim 編輯、刪除,也可以做搜尋了,基本的 vim 操作到這裡已經可以說是差不多了。

[2020.05.29 補充]
確認是否是數字
其中包含負數與小數的情況
^-?\s*?([\d\,]+(\.\d{1,})?|\.\d{1,})\s*$


參考資料:
vim 的 search 搜尋功能
javascript - 带小数位的数字,以允许在正则表达式验证中使用逗号

直接透過vim去取代文件內容

在之前,我要取代文件的內容,都是使用外部指令grep,來完成!

雖然,這樣也可以達成,不過,其實透過vim的內建功能就可以達到這一個功能啦~

Q: 選取特定幾行,執行取代的動作
A: 在之前我就要去算由第n行到第m行要執行取代的動作,那麼我就直接使用以下的動作
: n,ms/{pattern}/{string}/gc
而這裡還有另一個方法:
在你想置換部分的開頭按下大寫「V」然後用移動游標(我們之前講的 vim 移動游標的方法在選取模式下都可以用)到你想要的位置之後按「:」就會跳到輸入指令的狀態,如下圖最下面那行看到的一樣:


或者是如果你很確定要從現在游標所在行之後的所有行都置換,可以下成
:.,$s/vim/VIM/g
冒號一開頭的那一小點「 . 」就代表游標現在所在行,「$」則用來表示最後一行。

可以找到系統的相關說明
:h :s

假如我現在有一筆人名、電話的資料,由於是隨手記的,上面自然就是沒有排序過。那沒排序過對於想要在上面找資料的人就很麻煩。萬一人名記不太清楚,電話號碼也記得七七八八,雖然說有 vim 方便的 search 功能,但總是感覺不足。(當然這只是假設情況,因為實際上可能大家都已經建立某種方便搜尋的資料庫了)

我們先假設人名、電話的對應長成這樣:

趙大明  1235478982
錢小名 1223450012
王孫李 5938123812
周渚衛 1384914191
沈以情 2345934981


那我可不可以讓它變成這樣子?

1235478982   趙大明
1223450012 錢小名
5938123812 王孫李
1384914191 周渚衛
2345934981 沈以情


我可以下這樣的指令完成這個工作:

:%s/\([0-9]\{1,\}\)\([^0-9]\{1,\}\)/\2 \1/g

前面部分\([^0-9]\{1,\}\)用\( \)括起來的,表示這是一個的單元,之後就可以依照它出現的順序而使用 \1, \2,… ,來代表它。所以我們可以看到[^0-9]就表示非數字的部分,後面的\{1,\}如果你還記得的話,就是代表出現至少一次。可以用「\+」來代表至少出現一次。你可以用 :h /multi 看到更多這些替代符號。

所以把 pattern用\( \)分開之後,我們就可以用 \1,\2 來把他們交換位置。

最短符合

之前提到的 "\{n,m}" 會盡可能的找最長符合字串。若是要找最短符合字串,要改用 "\{-n,m}" 。

譬如說:

/ab\{-1,3}

會與字串 "abbb" 中的 "ab" 符合。因為要求的是最短符合字串,所以只會比對到1個"b"。如果要強制它能比對到比下限多一些的話,還得要多加點東西。

同樣的規則也可以套用到去掉 "n" 或 "m" 的情形。甚至可以同時不寫 "m" 和 "n" ,只留下 "\{-}" 。這個指令的意思是符合零次以上,愈短愈好…所以一定會比對到符合零次。這些規則可以組合使用,譬如:

/a.\{-}b

會對 "axbxb" 中的 "axb" 符合。如果改用:

/a.*b

就會試著找最長符合字串,所以就會符合到整個 "axbxb" 。

[2010.02.27 補充]
把完整的目錄名稱,只留下檔案名稱
例如內容為
d:\work\project\main.c
d:\work\project\make.dxc
d:\work\main.h
可以使用
:%s/.*\\\(\w\+\.\w\+$\)/\1/g
尋找字串,必需滿足以下二個條件
1. 先說明\w\+\.\w\+$的部分,是以尋找符號.前,至少有一個非特殊字元且後面也是至少有一個特殊字元的字串,並且後面就是字尾啦,這樣會找到main.c、make.dxc與main.h;
而用\(...\)把它包起來,就是要把它當作pattern,等一下取代則可以使用。
2. .*\\則表示找到在\前面有任意字元的字串
當內容中,有符合以上的字串,就處理\1這一個動作,也就是把符合這一個字串的部分,整個替換成第一個pattern

現在又想到一個問題,Makefile是沒有副檔名的,所以沒有辦法使用「任意字串.任意字串」當作條件去尋找啦。
還好又想到正規表示式是貪婪的,如何在同一行符合的字串長度有兩個,則他會選則最長的那一個。例如:
d:\work\project\main.c
符合.*\\條件有d:\與d:\work\與d:\work\project\
則,它會選擇最長的部分也就是d:\work\project\
因此,在這裡使用
:%s/.*\\\(.*\)/file name is \1 end/g
就可以取出檔名,並且在前面加入一些字串和後面加一些字串

[Add-20100307-start]
全域性的指令
:[range]g/pattern/[cmd]
cmd 是 ed 可用的指令,預設是 p(print),您可查一下 man ed,就可以知道有什麼指令可用。
需注意的是,d 是行刪除指令,凡含 pattern 的整行都會被刪掉,而且 range 不指定的話,預設是全篇文章,因為 g 就是代表 globe。

Q: 刪除空白行
:g/^$/d
有些書中寫成:%s/^$//g可以刪除空白行,這是錯誤的,因為 :s 這個指令只更動一行裡的內容物,但不會做刪除一行的動作。
[End-20100307-start]

[2010.04.05 補充]
Q: Delete all lines containing which didn’t contain string
A: :v/string/d

Q: want to match all non-printable characters and delete them
A: /\p\@!.
上列後面那一個小數點的意思是說滿足這一個條件的是有幾個字元,因為,上述只有一個小數點,所以是一個字元

Q: 找到不滿足的字串(不含有"String")
A: /\(String\)\@!

Q: 重覆a這一個字元3次
A: /a\{3}

Q: 重覆abc這一個字串元5次
A: \(abc\){5}

[2010.09.22 補充]
Q: 移除每一行最後面多餘的空白
A: %s/ \+$//gc

[2012.02.20 補充]
Q: 尋找最短符合字串,如何把以下內容前面縮寫的部分移到最後,並且加括號
API Application Program Interface
BLDK Boot Loader Development Kit

A: %s/\(^[A-Z]\{-}\) \(.*$\)/\2 \(\1\)/gc

參考資料:
vim 的取代置換功能「s」
vim 的 search 搜尋功能
規則表示式的運用
100 Vim commands every programmer should know
help pattern-multi-items

2010年1月20日 星期三

如何移動無法用滑鼠點不到的視窗

昨天,同事遇到一個問題。

當他在家的時候,他用雙螢幕,而把編譯器的視窗放在延伸的桌面上,也就是第二個視窗裡面。

當他回到公司時,他只有一個螢幕,開啟編譯器時,會跳到延伸的桌面,而他此時又沒有第二個桌面,所以,也沒有辦法用滑鼠把視窗叫到目前的主要畫面!

這個時候,就要用到鍵盤啦~

=========教學開始==============

請先參考下圖,這個視窗基本上根本無法透過滑鼠來移動,唯一可以按的地方只有「關閉」


在所有 Windows 視窗的左上角如果按下滑鼠左鍵會出現一個特殊的選單 (所有版本的 Windows 都有)


而透過「鍵盤」開啟這個選單的方式是 Alt + Space (空白鍵),接著透過鍵盤選取「移動」並按下 Enter


接著按鍵盤的 上(↑)、下(↓)、左(←)、右(→) 就可以移動視窗,只要移動到你覺得差不多可以用「滑鼠」控制的時候,這時再按下 Enter 即可確定視窗位置,該視窗就可以正常移動了!


=========教學結束==============

參考資料:
如何拯救無法移動的 Windows 視窗 (用鍵盤移動視窗的密技)