[C/C++] string使用strtok的問題

string ip1 = "140.113.100.1";
string ip2 = ip1;
strtok( (char*)ip1.c_str(), "." );
cout<<ip1<<endl;
cout<<ip1<<endl;

這樣子的結果,會讓ip1與ip2的內容變成一樣的!!! 為什麼呢? 這一切都是strtok的陰謀啦!!!

首先 先了解一下strtok的處理動作:
char * strtok( char * input, const char * delimiter);
每呼叫一次,便將input裡的第一個delimiter字元,改成'\0' (內碼為0)。因為strtok會將input的字串的內容值改掉,因為當input=="140.113.100.1",呼叫strtok一次後,會變成"140\0113.100.1",呼叫三次後,會變成"140\0113\0100\01"。

所以在呼叫strtok時,自以為小聰明的把原本strtok(ip1.c_str(),".");產生的error改掉。改成strtok((char*)ip1.c_str(),".");將ip1.c_str()的型別const char *強制轉換成char *,才會產生這種結果。產生error的時候,就是在提醒你,可能會有問題囉。


再來,明明ip1與ip2是不一樣的變數,怎麼改到ip1後,ip2也變了呢!?
這邊的話,就跟string的assignment function的機制有關係了,據我測試後的猜測,應該是當ip2=ip1;時,兩個的字串的指標是指到同一塊memory的(for 效率)。而當ip1或ip2其中任一個將字串裡的值改變後,string的assignment function才會再allocate另一塊memory給改變字串值的變數使用。

因此,上面的code的結果,才會讓ip1與ip2的印出的結果是一樣。
所以,結論就是,想要對c++的string切token的話,就不要用c的strtok()!!!

Netlimit IP Switcher前進SourceForge!

前一陣子跑去申請了一個SourceForge的帳號,Creatte了Netlimit IP Switcher的Project來玩玩,最近終於通過了,剛上去玩了一下,通通是英文…好難看得懂喔=_="""

SourceForge Netlimit IP Switcher

最近會把之前的Release跟Source code都放上去,有興趣的人再去那邊抓吧。

Ubuntu更改系統時間

最近在Ubuntu上,有時間不一致的問題,找了一些資料,找半天才找到,因此記錄一下。若要更改系統時間,指令如下(date+hwclock):

sudo date -s 2007/12/03
sudo date -s 17:02:07
sudo hwclock --systohc --utc


原本Linux上是用clock指令,而Ubuntu上是用hwclock指令。hwclock是將目前的時間,寫入HW中,才不會開機後時間又變回沒改之前的時間。

還有Ubuntu系統預設是用UTC的時間格式,若同一台電腦,同時安裝了Windows & Ubuntu可能會有時間上的問題,詳情請看此篇文章

另外一個方法
透過ntp,網路校正時間,會跟國家標準實驗室的 time server 校時,這個方法更方便,不用再自己看時間調date。
sudo ntpdate time.stdtime.gov.tw

Linux iconv遞迴轉換資料夾底下的檔案編碼

在Linuxl底下有個好用的轉編碼的小工具 - iconv
不過,這個iconv只能拿來轉單一個檔案,並不能直接指定一個資料夾下去遞迴轉換。所有當有大量的檔案需要轉編碼時,就變得有點麻煩了。還好Linux強大的shell指令,可以幫忙解決這個問題,而解決的曙光就來自於find!

find -type f -exec iconv --verbose -f BIG-5 -t UTF-8 {} -o {}.result \;  \
-exec mv {} {}.bak \; \
-exec mv {}.result {} \;

以上這段指令分三個階段執行,
  1. 將目前所在的資料夾的檔案找出,丟給iconv執行轉碼,先將結果暫存至{原檔名}.result
  2. 將原本的檔案,改名為{原檔名}.bak
  3. 再將剛暫存的{原檔名}.result,改名稱{原檔名}

iconv的簡介
-l 列出所有支援的編碼格式
-f 要轉換的來源編碼
-t 要轉換的結果編碼
-o 輸出結果的檔案
--verbose 列出處理的檔案名稱


find的簡介
-type [option] 想找出的檔案類型
  • f : 正規的檔案
  • d : 資料夾
-exec [command] 執行指定的command
  • 所有在exec之後的參數,都會被當成是該command的參數,直到 '\;' 符號出現為止
  • 正在處理的檔案名稱,以'{}'取代
-print 印出正在處理的檔名


%% 奇怪的事又發生了,我是使用Ubuntu,shell是tcsh,直接執行「find -type f -exec iconv --verbose -f BIG-5 -t UTF-8 {} -o {}.result \;」,結果檔並不會產生,真的是很奇怪,一定要三部分一起寫才有用,有高手可以跟我解釋一下嗎>_<

Linux中 字串的取代指令

一般人以為對字串的處理(刪除、取代)是用tr,但是實際上tr只能針對「字元」處理,如果要對「字串」做處理的話,則必須請出sed這個指令了。

以下簡單的比較了tr與sed的用法差別

tr [option] SET1 SET2

  • option -d 刪除, Ex: tr -d 't'
  • option -s 取代, Ex: tr -s 't' 'a'
可將[STDIN]的內容,做字元的取代或刪除
ex:
現有一File:source.txt內容為:
test
tttt
eeee
ssss
tttt
% cat source.txt | tr 'test' 'ABCD'
DBCD
DDDD
BBBB
CCCC
DDDD
注意到這個結果,tr只是單純的將test <=> ABCD做個對應,並將文件內容轉換為
  • t->D
  • e->B
  • s->C


sed [regurage] [file]
可充份地表示「正規表示式」,用法很像perl裡的寫法,input的方式,可由[STDIN]或是File
Ex: 想將'test'取代為'abcd'
% cat source.txt | sed s/test/abcd/g
% sed s/test/abcd/g source.txt
abcd
tttt
eeee
ssss
tttt
說明:
s/// 是取代的意思
s/[from]/[to]/
最後加個g,表示global,就是全部取代的意思,如果不加,則只會取代找到的第一個
[form] & [to]裡頭就可以寫正規表示式

這邊只是粗淺地介紹一下字串取代的功能部分,如果想深入了解,就再去找資料吧XD

[Ubuntu] php5安裝extension xsl

原本在我的舊文章有提到Ubuntu 安裝php5+xslt,感謝L大大的寶貴意見,讓我又上到一課了。
原來在php4支援的extension xslt,在php5以後就不支援了,改成支援新的extension xsl,所以原本在php4中用到xslt的地方,要全部改寫成xsl,才能正確地跑在php5+xsl上。幸好我的project,只有一個地方用到xslt,因此只要改小部分就好了。

至於xslt與xsl差在哪邊呢,它們都是用來讀取xsl檔格式的library。但是xslt是function base,xsl是class base。而用了一下之後,果然是新版的比較好,在xsl檔有error時,會秀出error訊息,而原本的xslt要另外寫個function xslt_error_handler去取得error message。


在Ubuntu上安裝php+xsl很簡單:

% apt-get install php5
% apt-get install php5-xsl


以下寫個xsl與xslt寫法的簡單對應:
xslt (function base)
$xsltproc = xslt_create();
xslt_set_encoding( $xsltproc, "UTF-8" );
echo xslt_process( $xsltproc, "input.xml", "falldog.xsl" );

xsl (class base)
$doc = new DOMDocument();
$xsl = new XSLTProcessor();

$doc->load("falldog.xsl");
$xsl->importStyleSheet($doc);

$doc->load("input.xml");
echo $xsl->transformToXML($doc);

關於其他xsl的訊息,請參考PHP官方網站說明
http://www.php.net/manual/en/ref.xsl.php

BCB Indy的元件 IdTCPClient的OnWork有問題

BCB的Indy元件:TIdTCPClient
在使用時,出現了點問題。它的Event : OnWork,原本的意義應該要是Server端有資料傳進來時,TWorkMode的參數的值會是wmRead,當有資料要寫給Server時,wmRead的值會是wmWrite。但是,在使用時,當有資料要傳要寫時,這個Event都不會被觸發... 再拿它的sample code來看,它竟然是用Timer去判斷是否有資料要讀or寫... 這樣的話,用Event還有什麼意義,真搞不懂。

所以最後的替代方案,就是用以前的元件囉。TClientSocket,它有OnRead的Event可以用,比較方便。

[Linux] libtool: -e: command not found

最近弄嵌入式系統的作業,在compile i-boot-lite的時候,出現了這樣子的錯誤訊息
libtool: -e: command not found

解決方法:

export SED=sed

FreeBSD PHP無法啟動session

在FreeBSD的主機上,執行有session_start()的php網頁時,出現這樣的錯誤訊息...
Fatal error: Call to undefined function: session_start()
讓我很疑惑,我一直都以為安裝php時,預設會安裝session才對,檢查一下phpinfo();的結果,還真的找不到session的項目。後來google一下,找到了解決方法...

cd /usr/ports/www/phpx.x.x-session/
make
make install
make clean
apachectl restart

BCB Override WndProc的兩種方法

有時,為了攔截自訂的Message,所以必須Override WndProc,在這邊有兩種方法可以選擇
1.

class TForm1 : public TForm
{
virtual void __fastcall WndProc(TMessage&);
//....
};


2.
/// Unit1.h
class TForm1 : public TForm
{
void __fastcall MyWindowProc(TMessage&);
//...
};

/// Unit1.cpp
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
this->WindowProc = this->MyWindowProc;
//...
}
兩種方法的差別在於,法一在程式開始執行時,收到系統Message時,就會跑進WndProc了;法二卻會先跑完TForm1()的Constructor後,才會開始處理系統送來的Message。

PHP 清除session的方法

註冊session的兩種方法
1. $_SESSION['name'] = "falldog";
2. session_register( "name" );

使用第1種方法時,清除session的方法不應該用session_unregister('name');
而應該用unset(); => unset( $_SESSION['name'] );

BCB 更新Indy元件後 產生的問題

我前一陣子將BCB6的Indy更新至9.0版,結果最近寫程式時,用TClientSocket & TServerSocket的時候卻出現了compile error! 奇怪,之前更新完之後,用Indy的網路元件一點問題也沒有,怎麼現在用舊有的BCB網路元件卻給我出現錯誤訊息咧...

[C++ Error] winsock2.h(113): E2238 Multiple declaration for 'fd_set'
[C++ Error] winsock.h(55): E2344 Earlier declaration of 'fd_set'
[C++ Error] winsock2.h(116): E2146 Need an identifier to declare
[C++ Error] winsock2.h(157): E2238 Multiple declaration for 'timeval'
[C++ Error] winsock.h(98): E2344 Earlier declaration of 'timeval'
[C++ Error] winsock2.h(213): E2238 Multiple declaration for 'hostent'
....

後來google一下後,找到不少解法,但是只有一種解法能成功...
  • 將 Winsock.h 改名爲Winsock1.h
  • 將 Winsock2.h 拷貝一份並改名爲 WinSock.h
保證解决!!

不過這個方法有個後遺症...當之後再使用Indy9.0的網路元件時,當有hpp裡面#include <winsock2.h>時,就得將它改成#include <winsock.h>才行

BCB取得網路連線的名稱

因為Netlimit IP Switcher的需求,所以必須找出「網路連線」的名稱,就是控制台下的網路連線的名稱...比如"區域連線"、"ADSL"...等等的。

原本以為用MIB_IFROW裡頭的data即可,可惜並不是這麼單純...後來終於找到方法了,IP_ADAPTER_INFO裡面記錄了所有Adapter的資訊(可由GetAdaptersInfo取得),裡頭的變數AdapterName即是Adapter的GUID,還不是名稱喔!要取得名稱的話,必須進入Register中查詢後,才能得知。在Register中的路徑是:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}

以下是我的簡單測試code
(1) 首先要拉兩個元件...ListBox & Button
(2) #include <Iphlpapi.h> 為了呼叫GetAdaptersInfo
(3) #include <vcl/registry.hpp> 為了查詢Registry

#include <Iphlpapi.h>
#include <vcl/registry.hpp>
//...

void __fastcall TForm1::Button1Click(TObject *Sender)
{
/*** 取得所有網路連線的名稱 ***/
//先透過GetAdaptersInfo取得Adapter的Name ... 此為GUID
//必須去Register取得真正的名稱
ULONG ulLen=4096;
BYTE *pbBuf=new BYTE[ulLen];
IP_ADAPTER_INFO * adp_info=NULL;

TRegistry * registry = new TRegistry();
registry->RootKey = HKEY_LOCAL_MACHINE;

if( ERROR_SUCCESS==GetAdaptersInfo((IP_ADAPTER_INFO*)pbBuf, &ulLen) )
{
adp_info = (IP_ADAPTER_INFO*)pbBuf;
do{
AnsiString key_path = "\\SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\"
+ AnsiString( adp_info->AdapterName )
+ "\\Connection";
if ( registry->OpenKeyReadOnly( key_path ) ){
if( registry->ValueExists("Name") ){
this->ListBox1->Items->Add( registry->ReadString("Name") );
}
}
registry->CloseKey();

adp_info = adp_info->Next;//point to next adapter!
}while(adp_info!=NULL);
}
delete pbBuf;
delete registry;
}

BCB TrayIcon元件 最小化至系統列 的基本應用三法

在BCB中,要將視窗程式要將程式最小化至系統匣中是很簡單的事,BCB提供了一個基本的元件提供使用...TrayIcon,只要用這個元件,就可以達到最小化至系統匣的目的了。TrayIcon的元件在Samples的分類底下。以下三種最小化的方法,供大家參考。

@ 按下縮下鍵就縮到系統列 @
拉入一個TrayIcon的元件, 將屬性Visible設true即可

@ 按關閉鍵時 縮入系統列 @
拉入一個TrayIcon的元件
Override WndProc

//Override的WndProc...
void __fastcall TForm1::WndProc(TMessage &Message){
if (Message.Msg == WM_CLOSE)
{
if (TrayIcon1->Visible == false)
{
TrayIcon1->Visible = true;
TrayIcon1->Minimize();
}
return;
}
WndProc(Message);
}

//再點選TrayIcon的EVENT Restore
void __fastcall TForm1::TrayIcon1Restore(TObject *Sender)
{
TrayIcon1->Visible = false;
}

@ 啟動時 就最小化在系統匣 @
1.在設計程式時,加入一個 TrayIcon 元件於主Form中,並將該元件屬性Visible設為true
2.在主 Form 的 OnActive 事件中寫入兩行程式碼:

Application->Minimize();
ShowWindow(Application->Handle, SW_HIDE);

Netlimit IP Switcher 1.12 release 發佈

增加了一些比較人性化的功能~

2007-09-16
1.12 Release
a. 增加功能: 程式開始執行後,取得目前IP
b. 增加功能: 最小化至系統匣
c. 增加功能: 記錄上次最後的設定
d. 增加功能: 下次啟動後 自動開始執行 (可放個捷徑在「程式集」中的「啟動」資料夾,開機後就會自動執行了)

下載頁面

Netlimit IP Switcher 1.11 release 發佈

有不少人反應希望能監控上傳&下載,因此,我多加了一點選擇上去了。至於選擇網路卡換IP的話,可能還要一段時間XD,因為程式不知從何下手... 過一段時間,我會將Code整理之後,放出來開放大家下載,畢竟我是Open Source的支持者:)

下載頁面


1.11 Release Note
a. 當"ip.config"中設定超過一組gateway & mask時,出現警告
b. "ip.config"中gateway可以不用設定
c. "ip.config"中的mask,gateway,ip不限大小寫
d. 使用者可以選擇監控上傳or下載的流量
e. 格式化"觀看目前流量"顯示number

使用限制:
(1) 只限於Windows環境
(2) 只能改您的網路連線中的「區域連線」的IP

Netlimit IP Switcher 1.1 release 發佈

Netlimit IP Switcher這是一個在Windows上可以限制流量、並自動更換IP的軟體,因為目前看來看去,都找不到有類似功能的軟體,只好自己寫一個囉,寫完成後還滿有成就感的,雖然只是個小程式XD~希望分享給有需要的人吧~

目前只提供偵測上傳的流量功能,如果有人需要下載的流量監控的話,我再增加吧。

Netlimit IP Switcher 1.1 release

軟體名稱:Netlimit IP Switcher

功能說明:
(1) 輸入固定流量,自動更換IP
(2) 手動更換IP
(3) 自訂每隔多久的時間檢查流量

使用方法:
(1) 下載完1.1.zip檔後,解壓打開執行Netlimit IP Switcher
(2) 執行第一次後 會產生ip.config
(3) 透過文字編輯器修改ip.config
內容說明:gateway->閘道, mask->子網路遮罩, ip->IP 位址
(4) ip.config範例 (先後順序無關)

mask 255.255.254.0
gateway 192.168.11.254
ip 192.168.10.123
ip 192.168.10.124
ip 192.168.10.125

(5) 重新執行Netlimit IP Switcher
(6) 輸入限流容量、時間,按下「開始」鍵,即可開始

使用限制:
(1) 限Windows環境下,目前測試過XP平台沒問題
(2) 此版本只提供「上傳」流量監控
(3) 只能改您的網路連線中的「區域連線」的IP
(4) ip.config中的mask & gateway只需(也只能)輸入一組,ip可輸入多組

修正Lifetype文章中有backslash '\'的問題

我已經被Lifetype中有backslash問題快煩死了...

在文章中要輸入backslash '\'時,實際上我在打文章時,必須輸入'\\'
如果輸入'\'的話,這個backslash就會被lifetype默默地吃掉....默默地...默默地...默默地...

如果文章中我輸入'\\'
顯示出來的結果是'\'
再重新編輯文章時,輸入區的文章卻變成顯示'\'而沒有顯示'\\'...
因此如果沒有再將'\'改成'\\'的話
這個backslash就會被lifetype默默地吃掉了...

以前想說自己找看看bug在哪,結果...打開Lifetype的source code來看,看得超痛苦的...
因為Lifetype將所有control頁面的流動都包在php中,因此要了解它其中的小變數,真得滿困難的...

不過不知為何,今天開竅了!
上Lifetype的網站API Document頁面看source code,感覺還滿簡單的
看到不知道的變數,都可以直接點選連結看更詳細的說明~
這樣子看就簡單多了,終於找到bug的地方了~

我目前的lifetype版本為lifetype1.2.1

修改的地方只有一個...
lifetype-1.2/class/view/admin/admineditpostview.class.php 裡面的第53

$this->setValue( "postText", str_replace('&', '&amp;', $this->_article->getText( false )));

改成

if( get_magic_quotes_gpc() )
$this->setValue( "postText", str_replace( '\\',"\\\\", str_replace('&', '&amp;', $this->_article->getText( false ))));
else
$this->setValue( "postText", str_replace('&', '&amp;', $this->_article->getText( false )));

在Windows上 修改本機端(local)的IP Address

由於最近架了個FTP,想在固定流量時,就更改IP,因此,東找西找,找到了可以修改IP的方法,在這邊供大家參考參考。

Windows提供了一個程式Netsh可以查看、修改IP, Gateway, Mask...等等的設定。因此我們就可以透過Netsh去修改本機端的IP Address。

Microsoft Netsh相關網頁

測試的方法可以在Cmd.exe(命令提示字元)下,輸入netsh,會出現...

netsh >
netsh > interface ip
netsh interface ip > dump

這樣子就可以查看目前介面卡(interface)的IP設定是如何了...

設定IP有兩種格式:(1) DHCP (2) Static
(1) DHCP是浮動IP的設定方法
(2) static是固定IP的設定方法

假設我們今天要將IP改為固定IP:100.1.1.1,則我們可以在Cmd.exe底下輸入

C:\> netsh interface ip   netsh interface ip > set address name="區域連線" source=static addr=100.1.1.1  mask=255.255.255.0 

或是比較簡易的輸入
netsh interface ip > set address "區域連線"  static  100.1.1.1  255.255.255.0 

*記得一定要輸入mask的值 否則會出現錯誤訊息

假設我們今天要將IP改為浮動IP,則我們可以在Cmd.exe底下輸入

C:\> netsh interface ip   netsh interface ip > set address "區域連線" dhcp

----

若是想寫程式直接執行,就可以修改IP的話,就將netsh後面那一串都當參數即可
在C/C++中的程式碼:

system("netsh interface ip set address \"區域連線\"  static  100.1.1.1  255.255.255.0");

在BCB中的程式碼:

AnsiString param = "interface ip set address name=\"區域連線\" source=static addr=100.1.1.1 mask=255.255.255.0";
ShellExecute( Handle, "open", "netsh", param.c_str(), NULL, SW_HIDE );

Skype API example in BCB

因為我未來的開發環境是在BCB上,因此在這邊寫了個簡單的BCB的小程式,可以與Skype連線,並送出最簡單的"PING" button。不過從最簡單的Win32 API轉至BCB時,遇到了不少難題,不過也一一克服了,在下面會詳細地說明。

在這邊的步驟可以對照Win32 API simple example這篇文章,流程是差不多的,只是實作的方法不一樣而已。

我的code在這邊…http://falldog7.googlepages.com/Skype_BCB.rar

首先遇到的問題就是...指定處理Message的Function,不能由BCB定義的BEGIN_MESSAGE_MAP和END_MESSAGE_MAP來處理。因為BCB背後所處理的方式是#define ...,所以這些Message的Message ID必須是compile前就定義好的"常數"(Ex: WM_USR+n),而我們要處理的Skype Message是在程式開始執行後,透過RegisterWindowMessage()所獲得的動態Messag ID,所以當你用BEGIN_MESSAGE_MAP來定義處理uiGlobal_MsgID_SkypeControlAPIAttach,會出現這種錯誤:
E2313 Constant expression required
因此,要定義這種由RegisterWindowMessage()取得的MessageID的處理function,可以做WndProc()的Override(覆寫)
在class TForm1中定義:

virtual void __fastcall WndProc(TMessage&);


WndProc()的Defination...
//WndProc() Overloading
void __fastcall TForm1::WndProc(TMessage & msg){
TForm::WndProc(msg);//用基本的WndProc函数讓Windows其他程式去處理~
//....
}

不過這種作法要小心,因為你可能把其他的Message都給吃下來,記得要呼叫base WndProc()去處理其他的Message

第二個比較需要注意的問題就是當接收到WM_COPYDATA的時候,要return 1; 而WndProc()一定要定義成void,所以一開始讓我很困擾,讓我不知所措,想說把void改成UINT,結果連compile都不會過了... 後來才知道,原來void WndProc(TMessage & msg);其中的參數TMessage可以設定return的值。所以只要當我們接收到WM_COPYDATA時,將msg的return值設為1即可,這樣Skype就不會Disconnect了!

if(msg.Msg==WM_COPYDATA){
// ...
msg.Result = 1;
}

Skype API simple example

原本的Skype API附有一個很基本的windows example,只要看這個範例應該就能了解Skype API的運作方式了,不過它多了不少沒必要的code,如果想了解大致上的架構與訊息傳遞的話,其實也不用太多的code,因此我在這邊重新改寫了一個有最最最基本功能簡單視窗程式。

http://falldog7.googlepages.com/Skype_simple_example_window.cpp

這隻程式,用純Win32 API寫的,只是單純的可以開始控制Skype,然後出現視窗,在視窗內點左鍵,就會送出"PING"這個指令給Skype,正常來說,Skype會回傳"PONG"的訊息回來,這樣的結果就是正確的!!!

Skype API基本流程(請對照著我寫好的範例程式):

1. 首先必須先透過RegisterWindowMessage定義一個Message ID,並存起來

UINT uiGlobal_MsgID_SkypeControlAPIAttach = RegisterWindowMessage("SkypeControlAPIAttach");
UINT uiGlobal_MsgID_SkypeControlAPIDiscover = RegisterWindowMessage("SkypeControlAPIDiscover");

2. 透過SendMessage() 將uiGlobal_MsgID_SkypeControlAPIDiscover廣播出去

SendMessage( HWND_BROADCAST, uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM)hwnd, 0);

3. 接著就可以在WndProc()裡接收uiGlobal_MsgID_SkypeControlAPIAttach的訊息,若是LPARAM的值為程式開頭enum中定義的SKYPECONTROLAPI_ATTACH_SUCCESS,則表示有偵測到Skype。此時要將WPARAM記錄起來,因為它就是Skype Window Handle

hGlobal_SkypeAPIWindowHandle = (HWND)wParam;

4. 接著Skype就會不斷地送WM_COPYDATA的訊息過來,當我們收到後可以將訊息中的結果Show到視窗上。另外,很重要的是…在收到WM_COPYDATA後,務必要return 1;否則Skype會主動Disconnect!!!

5. 要控制Skype的話,就可以透過SendMessage() 將WM_COPYDATA的訊息,送給hGlobal_SkypeAPIWindowHandle,要傳輸的指令寫入COPYDATASTRUCT中即可。

執行結果頁面:

1.首先執行程式後,會出現一個Skype的詢問視窗,問使用者是否要開放這個試圖操控Skype的程式,當然要選Yes啦~不然怎麼Run....=_=

2. 接著可以看到一堆Skype送過來的訊息產生的訊息視窗。

3. 接著就可以看到主視窗了

4. 在視窗上面點左鍵

5. Skype回傳訊息

如果以上步驟都完成,而你也都了解其中的意義的話,那Skype API應該就算是入門完畢了:)

談談Skype API

Skype最近真是愈來愈火熱了,隨著網路電話的興起、Skype手機的出現、Wi-Max標準的制定,再再都表示,Skype日後真是無可限量啊!!! 連我的論文也跟Skype有關係了~因為Skype大方公開API的關係,讓更多人可以開發與Skype相關的軟體,我想對Skype也是有所助益啊。

Skype API雖然推出一段時間,但是我在Google上找到的中文資料卻是少之又少, 因此,我只能上去Skype API的官網看著原文的資料...(喔~殺了我吧~英文是我的罩門...) 等到略有了解後,才上到論壇去找更多人討論的資料,因為我以後的開發環境是BCB,但是論壇上的人大都是在VC上開發較為多數=_=,怎麼這麼不順利啊我...還好我集給智慧與勇氣於一身,PO了一篇文章指出我的問題,幸好有BCB的高手幫了我一個大忙~現在我已經寫出最最最簡單的程式了,哈哈哈。


學習Skype API的可去之處:


Skype API 的架構與概念:
Skype API 簡單的說,就是透過Windows Message去控制正在執行的Skype程式,也就是說不執行Skype的話,就不能使用 Skype API 了。一開始我還以為是可以直接Call它的library咧,結果並非我所想的那樣。

....如果想更深入了解 Skype API 的程式, 請繼續參考站上的其他文章~

Skype API 專欄報導

Linux下的重新命名(rename) & 新增檔案(new file)的指令

重新命名的指令mv
其實在Linux底下沒有特別處理rename的直接指令,不過可以透過mv這個指令,得到rename的效果。mv原本是用來處理「移動檔案」到不同的目錄底下,因此可以透過mv將原檔案移至原目錄底下新的檔名,就是rename的效果。
Ex: 將a.out改名稱為b.out
% mv a.out b.out

新增檔案的指令touch
Ex: 新增檔案a.txt
% touch a.txt
touch原本的意義是將檔案的建立時間更新至目前的時間,因為常常會有檔案的時間不一致,導致一些問題。比如Makefile更改時間跑到了未來,導致執行make時,會出現一些Warnning,可以透過touch Makefile來避免這個Warnning出現

MySQL++使用教學

MySQL++是一套提供C++程式可以對MySQL的Database做存取的Library。雖然MySQL原本就有提供mysql.h供人使用,但我認為MySQL++對學習C++的人而言,語法非常的簡易好用,整個用法感覺上跟PHP很像,如果學過PHP & C++的人,應該很快就能上手了~

MySQL++的相關網站:
首頁
Documentation
Simple Example

MySQL++執行環境:
‧GCC 3.x and 4.x on recent x86 Linuxes,
‧GCC 4.x from Xcode 2.x on Mac OS X 10.4
‧Visual C++ 2005 on 32-bit Windows.

而我的安裝環境是Linux的Ubuntu以下就針對Ubuntu的用者說明安裝過程
安裝MySQL++ in Ubuntu:

% sudo apt-get install libmysql++-dev  
% sudo apt-get install mysql-client-5.0

很簡單吧....不知不覺的,安裝結束了 XD,記得重開機一下,這樣等等compile程式時才抓得到library

Compile程式所需要相關MySQL++的參數:

% g++ [code-file-name] -I/usr/include/mysql++ -I/usr/include/mysql  \
-lmysqlpp -lmysqlclient

  • -I/usr/include/mysql
    • 因為MySQL++的程式都是建立在MySQL提供的mysql.h上,所以必須指名include mysql.h的路徑,這個路徑就因機器而異了,你可以locate mysql.h看看,我所寫的是Ubuntu預設的路徑
  • -I/usr/include/mysql++
    • 這個就是MySQL++的header file存放的路徑。
  • -lmysqlpp -lmysqlclient
    • 在linking time時,compiler會使用到這兩個系統註冊的library檔


MySQL++的基本類別(class)說明:
這些類別都是在mysqlpp的namespace底下,如果要使用的話,必須在程式開始就宣告using namespace mysqlpp,或是在程式中每個類別前都加上mysqlpp::Connection、mysqlpp::Query、等等。

  • Connection -- 可以與DB連線
  • Query -- 可對與DB連線的Connection下指令
  • Result -- 儲存Query執行後的結果值
  • Row -- 儲存Result中每一筆的內容


以下有個我參考官網附的Example修改的code,加上一點中文註解,希望有興趣的人可以更快學會MySQL++

#include <mysql++.h>
#include <iostream>

//這樣子之後的code裡的Connection, Query, Result, Row...
// 前面就不用加mysqlpp::
using namespace mysqlpp;
using namespace std;

int main(int argc, char *argv[])
{
/*** 連結到input輸入的argument的DB ***/
// Input argument format
// ./a.out <DB-Name> <DB-Host-IP> <Name> <Password>
Connection con(false);
if ( !con.connect( argv[1], argv[2], argv[3], argv[4] ) ){
cerr<<"connect DB error..."<<endl;
return -1;
}

/*** 從DB撈資料出來 ***/
Query query = con.query();// create一個Query
// 可以對con做讀取or寫入
query << "SELECT item FROM mydb";//輸入要query的command
Result res = query.store();//執行query,並把結果存至Result res

/*** 秀出所有的result set ***/
cout << "We have : " << endl;
if (res) {//如果res有抓到東西的話....

Row row;//row可儲存每一列的資料
Row::size_type i;
for (i = 0; row = res.at(i); ++i) {
cout << 't' << row.at(0) << endl;
}
}
else {
//秀出Query產生的Error
cerr << "Failed to get item list: "
<< query.error() << endl;
return -1;
}

return 0;
}

身為一個Programmer必須注意的事 --- i++ & ++i 效率是一樣的嗎!?

i++ & ++i 寫起來不一樣、意思不一樣,沒想到連效率也不一樣!!! 最近研讀侯捷大師翻譯的「Exception C++」讀到其中這一段,其實還滿令我驚訝的,因為我本人平時寫code的習慣,就是習慣性的將for loop最後的++寫成i++,沒想到i++比++i 還沒效率....

這個是平常寫for loop的習慣... 如果MAX_SIZE 一大,所造成的效率問題,就會比較明顯了~

for( int i=0 ; i < MAX_SIZE ; i++ )
{ .... }

為什麼說++i 比 i++還要有效率呢? 就字面上的意義而言:
‧++i => 先將i+1,再使用 i 的值~
.i++ => 先使用i的值,再將 i 的值+1

重點就是在先加或是後加的問題~先加再用,則compiler背後的運作,就是將 i 的值+1然後再使用 i。而如果是先用再加呢,compiler勢必先create出一個temp(暫時性的變數)存放著i的值,然後才將 i 的值+1,實際上使用的卻是temp的值。所以實作 i++所必須付出的代價,就是多create出一個temp的變數,以及temp變數的constructor。

在這邊我實作了一個名為INT的class,它的功能就跟一般的int型別是一樣的~而其中的operator ++很明顯地就表達出先加與後加所必須付出的代價...

#include<iostream>
#include<list>

using namespace std;

class INT{
public:
INT():_value(0){}
INT(int a):_value(a){}
INT(const INT & i):_value(i._value){}//copy constructor

//為了cout所實作出來的operator
friend ostream & operator <<(ostream & out, const INT & i){
out << i._value;
return out;
}

//i++
INT operator ++(int t){
INT temp(_value);//!!! 必須create出一個temp的變數!!!
this->_value++;
return temp;
}

//++i
INT operator ++(){
this->_value++;
return *this;
}

void operator =(int i){
_value=i;
}

private:
int _value;
};

int main(int argc, char**argv){
INT i;
cout << "i==1" <<endl;
i=1;
cout << "++i == " << ++i << endl;

i=1;
cout << "i++ == " << i++ << endl;
return 0;
}

談談Ubuntu的apt-get remove

之前因為想要自己手動compile安裝apache, php, mysql,所以想說先將原本用apt-get install安裝的apache, php, mysql先移除,然後再上網將它們的source code抓下來編譯安裝

% apt-get remove apache2
% apt-get remove php5
% apt-get remove mysql

結果手動compile安裝好後,重新開機,結果開機後自動執行的apache竟然是原本apt-get install所安裝的路徑,整個覺得很怪,不是刪除了嗎...怎麼還能跑能跳的....=__="

原來,apt-get remove <package-name>它並不會將檔案刪除,他只是將dpkg中的清單裡的套件刪了而已,並不會將它的程式、config、檔案…等刪除,於是我加上了--purge這個參數,說明是說可以將相關檔案刪除。不過實際上,跟remove是一樣的道理,只是將dpkg中的套件名稱刪除而已。

因此,如果要將套件的全部檔案刪除的話,就必須用dpkg -P <package-name> (注意...是大寫P....)才行,但是這個指令不會自動一次將所以相關套件一次刪除,只會刪除指定的套件,因此,若是想要刪除乾淨的話,就要一個一個下指令刪除才行。

比如現在要完全刪除apache2以及相關套件,先用dpkg -l 看看我們裝了哪些跟apache2相關的套件

% dpkg -l | grep apache2
ii apache2
ii apache2-mpm-prefork
ii apache2-utils
ii apache2.2-common
ii libapache2-mod-php5

因此現在必須一個個刪除這些相關套件

% dpkg -P apache2 
% dpkg -P apach2-utils
....

只是要注意有些套件是存在相依性,A depend on B,所以要先將A刪除後才能刪B。
這樣子執行完後,apache2才算是完整的被刪除了。

[Linux] 無法啟用or關閉Apache

今天看網頁怪怪的,很慢才出現,所以想說重新啟動Apache看看,可能會好一點,結果竟然出現錯誤,著實嚇了我一大跳...
% /etc/init.d/apache2 restart
(13)Permission denied: make_sock: could not bind to address [::]:80

後來才發現,原來是忘了用sudo了....Orz...要有root的權限才能啟用or關閉apache,發現原因後,覺得還滿白痴的=_="""
% sudo /etc/init.d/apache2 restart

將Lifetype Server從Windows搬移至Linux

由於本站之前在windows上執行,有時候會造成Apache莫名奇妙地失靈,所以才想將server移至Linux Ubuntu上執行。這樣子所吃的資源可能也會比較少一點。

以下紀錄了移機的過程,主要遇到的問題有...編碼以及轉簡潔網址的問題。

[事前準備工作]

  1. 將Windows的資料備份出來
    • 將apache底下的lifetype整個資料夾壓縮備份起來(用zip壓縮,傳至Linux上再用unzip解壓縮)
    • 進入phpMyAdmin後,將lifetype資料庫輸出至檔案中lifetype.sql(請選擇「使用完整新增指令」)
  2. 在Ubunte上安裝Apache、Mysql、PHP(在這邊不多加著墨)
    • sudo apt-get install apache2
    • sudo apt-get install mysql
    • sudo apt-get install php
    • sudo apt-get install php-mysql
  3. 將原本Windows的lifetype程式碼,傳至apache2預設資料夾/var/www/
    • unzip lifetype.zip
  4. 上網下載phpMyAdmin,用來管理mysql資料庫

[重建Database]

  1. 利用phpMyAdmin建立一個lifetype資料庫,校對要調成utf8_unicode_ci
  2. 在Windows匯出的lifetype.sql檔前面加上
    • SET NAMES utf8;
      SET CHARACTER_SET_CLIENT=utf8;
      SET CHARACTER_SET_RESULTS=utf8;
  3. 在cmd line下指令,匯入lifetype.sql檔
    • mysql [lifetype DB name] -u[Name] -p[Password] < lifetype.sql \
      --default-character-set=utf8
    • # 因為mysql的預設編碼為latin1,因為需設定正確的character-set才能匯入
    • # 或是直接更改mysql預設的編碼方式為utf8就更乾脆了,可修改mysql的設定檔my.cnf
    • # [client]
      # default-character-set=utf8
      # [mysqld]
      # default-character-set=utf8
      # default-collation=utf8_general_ci
  4. ---- Error Message ----
    都按照上面的步驟做的話,應該就不會有所謂的亂碼出現才對。一開始我沒就直接匯入,也不疑有他,結果就犯了全天下男人都會犯的錯…ERROR 1064 (42000) at line 330: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xxxxxx' 主要的原因就是因為編碼問題,因為lifetype.sql中挾雜著鼎鼎大名的「許功蓋…」含有跳脫字元「」,所以就視為字串已結束,就出現錯了。
    不過我後來都照上面的做了,卻還是不斷出現同樣的錯誤訊息纏著我,讓我陷入人生中的低潮,後來才發現,原來是因為在Windows匯出lifetype.sql檔案,沒有選擇「使用完整新增指令」,所以才會不斷地出錯,當我又重新試了一次之後,終於成功了!!!讓我流下了感動的眼淚,並且大喝了一聲X,響徹雲霄!!!

[建置Lifetype網站]

  1. 把原本在Windows上的lifetype資料夾複製到/var/www後,建議是把wizard.php抓回來,重新建置一遍,這樣子會比較簡單一點,否則要自己動手修改一些東西。
    • 修改/lifetype/config/config.properties.php內的db_host為
      $config['db_host'] = localhost
    • tmpgallery這兩個資料夾權限改為777
      % cd /var/www/lifetype
      % chmod 777 tmp
      % chmod 777 gallery
    • 為了安全性起見,不將gallery底下的資料夾權限全開,因此必須將它的Owner改為apache的名字,我的主機上apache的名稱為www-data
      % chown -R www-data gallery
    • 看gallery底下有幾個資料夾就改幾個資料夾的權限,因為這些都是blog user上傳檔案的資料夾,因為本站只有一個user,blog_user_id為1,所以就只有一個資料夾「1」
      % cd gallery
      % chmod 755 1
      再將1資料夾底下的previews&previews-med的權限改成755
      % cd 1
      % chmod 755 previews
      % chmod 755 previews-med
  2. 輸入網站的網址,試試看是否正常運作。照理來說,到目前為止應該都是正常的。
    % /etc/init.d/apache2 start
    如果出現Error Message: apache2: Could not determine the server's fully qualified domain name, using 127.0.1.1 for ServerName
    在/etc/apache2/apache2.conf中,加入ServerName localhost即可
  3. 但是,要進入admin.php的後台管理系統卻會失敗,因為原本網站的IP跟新網站的IP不一樣,而原本的DB中紀錄的base_url導致網頁中的連結都不正常了。
    @解決方法:利用phpMyAdmin進入lifetype的DB,進入lt_config這個Table。修改其中的兩項參數:
    base_url
    blog_does_not_exist_url
    將這兩個url參數中的網址改成新的IP就行了!
  4. 網站中有使用authImage外掛,可能會發現在產生驗證圖檔時,出現錯誤訊息「You don't have GD support compiled in, we cannot create an authimage. Please activate GD Support.」。這是因為你的php5沒有使用GD module,因為只要將GD module安裝起來即可,以下為Ubuntu的做法
    % apt-get install php5-gd
    % /etc/init.d/apache2 restart

[使用簡潔網址]

  1. 有使用簡潔網址的人,因為Ubuntu預設沒有掛載mod_rewrite,所以要先將module rewrite掛進來,因為apache在安裝時,其實已經裝載好,只是尚未load而已。而load的語法已寫好放在/etc/apache2/mods-available中,只要做個link放至mods-enabled,apache就會自動去load了
    % sudo ln -s /etc/apache2/mods-available/rewrite.load \
    /etc/apache2/mods-enabled/rewrite.load
    或是
    %
    sudo a2enmod rewrite
  2. 修改/etc/apache2/sites-enabled/000-default
    先找到
    <Directory /var/www>
    ...
    </Directory>
    將其中的AllowOverride後的None改為All
    Options後面要有FollowSymLinks

    然後重新啟動Apache
    /etc/init.d/apache2 restart
  3. 順利的話,應該就一切正常了:)
    可惜的是,我一點也不順利....Orz,我開網頁時,一直遇到error 500,打開apache的error.log檔來看時,顯示的錯誤是:「/var/www/lifetype/.htaccess: </Files> without matching <Files> section 」我一直不知道是哪裡的問題,千萬百試之後,才發現,我的.htaccess檔的編碼是utf8,後來把它改成ansi編碼就OK了~我猜可能是從windows複製過來linux後的才變這樣。

[Linux] 截取、忽略系統發出的segmentation fault signal

因為有個程式有需求,要暫時把segmentation fault的signal SIGSEGV這個訊號忽略掉,比較利於之後程式的debug。原本想說用single();這個function指定發生SIGSEGV時交給指定的handler function去處理,就可以忽略掉。結果…「代誌並不是憨人所想得呷呢簡單」,執行之後的結果,卻是系統不斷地發出SIGSEGV這個signal…停都停不下來,擋都擋不住...連ctrl+c都不行Orz...

原來在fault發生後,必須處理造成fault的原因,再返回程式執行,返回程式後,會重新執行faulting instruction。所以若是想忽略segmentation fault的話,就必須透過longjmp()跳過可能發生錯誤的code,然後再繼續執行。

 

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/ipc.h>
#include <setjmp.h>

jmp_buf setjmp_buffer;

void segmentFault(){
std::err<<"get a segment Fault!"<<std::endl;
longjmp( setjmp_buffer, 1 );//用0的話, setjmp()就會收到0,那麼可能會無窮回圈
}

int main(){

//當發出SIGSEGV這個signal時,交給segmentFault這個function處理
signal( SIGSEGV,(sig_t)segmentFault );

if( setjmp( setjmp_buffer ) == 0 )
{
// do something
std::cout<<"longjmp!!n";
char *a= NULL;
std::cout<<"ok"<<std::endl;
a[0] = '1';//cause a Segmentation Fault
}
else
{
// do something
std::cout<<"after longjmp.n";
}
return 0;
}

 

[Linux] php5安裝extension xslt

## 此篇文章有更新 ##
## 以下為手動compile安裝xslt+php的步驟 ##
## 至於Ubuntu+php5+xsl 請參考我的新文章 ##

由於在php4.1以後的版本對extension xslt預設不再是support,因此如果想要為php安裝xslt的話,就變得有點麻煩了。最近正好要在Fedora5上安裝Apache+PHP+XSLT+Mysql,原本一切很順利得裝起來了,但是最後才發現PHP5不support XSLT了,原本全都用yum指令安裝的,或是用rpm安裝的,全都要移除重新安裝一次…

在Linux上,由於要讓PHP5裝上XSLT,就必須要自行下載source code經過compile後才能安裝,而要compile安裝PHP的話,也必須compile安裝Apache才行。

安裝XSLT所需要的套件是Sablot,而Sablot又需要先安裝Expat,所以安裝的順序為(以下版本為參考用):

  1. Expat-2.0.1
  2. Sablot-1.03 (Sablotron)
  3. Apache (httpd-2.0.59)
  4. PHP-5.2.3

以下為我的安裝指令,--prefix的路徑是可以自己設的,以自己的主機設定為主。

  • Expat
    • ./configure
    • make
    • make install
  • Sablot
    • 要-enable-shared才能讓PHP找到XSLT的library
    • ./configure --enable-shared
    • make
    • make install
  • Apache
    • 要--enable-module才能讓PHP順利安裝
    • ./configure --prefix=/usr/local/apache2 --enable-module=so
    • make
    • make install
  • PHP
    • ./configure --prefix=/usr/local/php \
    • --with-apxs2=/usr/local/apache2/bin/apxs \
    • --with-mysql=/usr/ \
    • --enable-xslt \
    • --with-xslt-sablot
    • make
    • make install

這只是在Linux上,安裝Apache+PHP再加上PHP的extension的XSLT,如果要加上其他的extension也是差不多依樣畫胡蘆就行了。若還是不太清楚,可以下./configure --help查一查,或是看看INSTALL檔案的內容,應該就滿清楚的了。


#ps. 最近改用Ubuntu照上面的步驟安裝,卻是屢裝屢敗,硬是不給我裝起來...明明phpinfo()的結果都看得到xslt了,執行時,就是給我出現Fatal error: Call to undefined function xslt_create() 的訊息...真的很幹!
後來改用php 4.4.7安裝後,才成功。真是奇怪...

[C/C++] 用C++的Vector產生動態二維陣列

vector是c++很好用的standard library,以往自己在產生動態二維陣列時,都習慣用int ** array,然後再配合for迴圈去new出想要的array size

    //2維的array[n][n]
    int ** array_2D = new int*[n];
    for(int i=0;i<n;i++){
        array_2D[i]=new int[n];
    }


但是當量一大時,用for loop與new配置出來的效果不是很好,因此,後來用了vector,發覺此方似乎比原本上面的方法好多了

 vector<int> row;
 row.assign(n,0);//配置一個row的大小
 vector< vector<int> > array_2D;
 array_2D.assign(n,row);//配置2維

[C/C++] 千萬別把function definition & 變數definition寫入.h裡

程式碼一多,為了撰寫與維護的方便,最好的方法就是將一個.cpp檔切割成多個檔案( .cpp & .h )
但是最近撰寫了一支程式,把它切成多個file,卻莫名其妙地出現了"multiple definition of [function name] ... "這種錯誤訊息...
我仔細地觀察了又觀察,不管是正著看、反著看、倒著看…都看不出錯誤來…正當對分檔的寫法失望之餘,想回復到原本一個檔案的寫法後,才驚覺原本寫法的漏洞與缺點…
原本的一個file寫到底的code....

#include<iostream>

void a_function(){
    int a;
}

void a_problem_function(){
    int problem;
}

int main(int argc, char**argv){
    return 0;
}
把它切成多個file... 包括 .h && .cpp
a.h的內容
#ifndef A_H
#define A_H

void a_function();

void a_problem_function(){
    int problem;
}

#endif
a.cpp的內容
#include "a.h"

void a_function(){
    int a;
}
main.cpp的內容
#include<iostream>
#include "a.h"

int main(int argc, char**argv){
    return 0;
}
compile過程:
% g++ -c a.cpp                                              
% g++ a.o main.cpp                                        
...... multiple definition of `a_problem_function()'

照上面的寫法實作後 會產生這樣的問題...
奇怪!? 明明a.h不是寫了「#ifndef ... #define .... 」怎麼還會有問題呢!?
沒錯! 這樣的寫法的確是會有問題的... 並不是說寫了「#ifndef ... #define .... 」,在compile過程中,就應該只有一份a_problem_function() 而不應該出現multiple definition的問題才對呀...
其實問題是在於g++ -c a.cpp後,產生了a.o檔,這個a.o檔裡面,已經包含了一個a_problem_function()的definition,然而接下來g++ a.o main.cpp時,main.cpp又去include a.h,因此,它又想產生了一份a_problem_function(),問題就此產生了!
所以把變數definition寫入.h檔也會產生一樣的問題喲!
所以問題的徵結就在於.h檔裡面,寫入了function的definition或是變數definition,這樣子容易在多個檔案互相include時,產生multiple definition的問題,所以說囉,千萬別偷懶…直接把function definition寫在.h檔裡,是很不明智的決定!!!

[Ubuntu] 安裝phpMyAdmin

我之前已經安裝好了PHP5, Apache2, MySQL了,現在就缺phpMyAdmin來管理我的DB~

在Ubuntu上安裝上面那三個套件都很簡單
都用apt-get install就可以輕鬆安裝成功了~

原本以為用apt-get install phpmyadmin就可以了
結果Ubuntu竟然不能這樣直接安裝...Q_Q
所以只能自己做苦力囉~

首先,先至phpMyAdmindownload最新的phpMyAdmin的版本,我是下載all-languages-utf-8-only.tar.gz這個版本

% wget [URL]                                          
% tar -xvzf [file name]
% mv [director name] /var/www/
% sudo cp config.sample.inc.php config.inc.php

然後再修改config.inc.php裡面的內容~
/* Authentication type */
$cfg['Servers'][$i]['auth_type'] = 'cookie';
將cookie改成http,可以增加安全性

[Ubuntu] vim讓程式碼顯示彩色

由於Ubuntu在安裝時內建的vim是簡易版(tiny)
所以無法讓程式碼變色

因此,必需要重新安裝vim才行
 > apt-get install vim       
安裝完成後,將預設的vimrc copy至user的目錄底下
 > cp /etc/vim/vimrc  ~/.vimrc   

[2007-9-9修改]
若想增加vim的功能的話
 > vim ~/.vimrc
再將想要的功能前面的「"」 去掉即可

Ex:
要讓程式碼變色
syntax on前面的"去掉即可

 

vim還有其他很多功能
可以仔細看看vimrc中的註解,再把想要的功能的"刪掉,即可~

[Ubuntu] 用apt-get安裝tcsh

因為覺得原本的bash太乏味了,所以就想說換換口味看看,順便熟悉一下Ubuntu~
所以就選擇安裝tcsh來玩玩看

在Ubuntu中,大部分常用的軟體都可以透過apt-get install來安裝
不過由於tcsh並非原本官方support的software
所以必需利用一些小手段才能夠安裝~

方法一:
「下載source code回來make install」
失敗了....在make install時,出現了一些錯誤訊息... 所以,就試試別的方法看看囉~

方法二:
「修改sources.list,再用apt-get」
原因~因為原本apt-get update只會下載官方support的software information,因此必須再下載其他的information才行,而sources.list中就是記載要從哪下載software information的文件。

修改/etc/apt/sources.list 將後面有universe的選項去掉#

% apt-get update       
% apt-get install tcsh

就成功了 ^_^

如果要設定一些基礎的tcsh設定文件,可以到.tcshrc download.tcshrc安裝檔,就可以安裝到你的user的設定了。

如果要將user的預設的shell從bash改成tcsh
可以到/etc/passwd這個file去修改
把要改的user後面的/bin/bash改成/bin/tcsh就行了

Lifetype 1.2.x版 結合BBClone

BBClone是統計網頁瀏覽人數的好幫手
感謝之前已經有Mark大大將Lifetype與BBClone結合在一起了
詳細的文章在這邊
http://blog.markplace.net/marks_development_blog/2/2005/05/06/4

但是上面這個只適用於1.0.x版的吧
1.1.x我沒試過
筆者目前所使用的版本是1.2.1版
按照Mark大大的方法掛上去後 會產生一些問題
原因是因為Lifetype的核心有修改過
所以有部分的code會產生錯誤...

按照Mark大大的方法做完之後
index.php中原本插入在
SessionManager::init();
這一行之後的code,
只要再加上成後面有// 1.2.x 的句子
就可以了

 ///BBClone
include_once( PLOG_CLASS_PATH."class/dao/articles.class.php" );//for 1.2.x
define("_BBCLONE_DIR", PLOG_CLASS_PATH."bbclone/");
define("COUNTER", _BBCLONE_DIR."mark_page.php");
if(!isset($_COOKIE["stats_cookie_nolog"])) {
$articles =& new Articles();
if(!empty($_REQUEST['articleId'])) {
//$article = $articles->getUserArticle($_REQUEST['articleId']); //1.0.x
$article = $articles->getArticle($_REQUEST['articleId']);// 1.2.x
$pageName = $article->getTopic();
} elseif(!empty($_REQUEST['articleName'])) {
if(!empty($_REQUEST['blogId'])) {
$blogId = $_REQUEST['blogId'];
} else {
$blogs =& new Blogs();
$blogInfo = $blogs->getBlogInfoByName($_REQUEST['blogName']);
$blogId = $blogInfo->getId();
}

$article = $articles->getBlogArticleByTitle($_REQUEST['articleName'], $blogId);
$pageName = $article->getTopic();
} else {
$pageName = 'index';
}

define("_BBC_PAGE_NAME", $pageName);
if (is_readable(COUNTER)) include_once(COUNTER);
}

或是直接下載已經改好的index.php

[Linux] 在Linux底下的getch() & getche()

因為C/C++語言中的getch() & getche()都只能在windows底下執行
但是在Unix/Linux底下 就必須自己寫個function囉~

#include<termios.h>
char getch(){
    int i;//判斷是否有read到值
    char ch;
    struct termios _old, _new;

    tcgetattr (0, &_old);
    memcpy (&_new, &_old, sizeof (struct termios));
    _new.c_lflag &= ~(ICANON | ECHO);
    tcsetattr (0, TCSANOW, &_new);
    i = read (0, &ch, 1);
    tcsetattr (0, TCSANOW, &_old);
    if (i == 1)/* ch is the character to use */ 
        return ch;
    else/* there was some problem; complain, return error, whatever */ 
        printf("error!n");;
    return 0;
}

用了dp.SyntaxHighlighter後所產生的編輯問題

因為dp.SyntaxHighLighter的程式碼上色方法
是將code寫在<textarea>中
不過因為Lifetype的線上編輯器也是使用<textarea>
所以在文章新增完成後,再去編輯修改文章時就會產生問題了

因為文章裡有</textarea>這個tag
所以編輯頁面看到這個就會以為tinyMCE的編輯畫面到此為止了
結果會使得</textarea>後面的文字,全都跑去外面鬼混了…

解決方法:
templates/admin/editpost.template 75行

{$postText}
改成
{$postText|replace:'</textarea>':'&lt;/textarea>'}
這樣子的話 編輯畫面就會顯示正常了...

 

以上解法也是在網路上看到同好高手解決的
http://www.jiayun.org/plog/1_jiayun/archive/188__dpsyntaxhighlighter_ee.html

解決tinyMCE會吃掉textarea中的換行符號\n

由於lifetype1.1版以上的線上編輯器就開始使用tinyMCE
因此有個問題就存在了,tinyMCE會將<textarea></textarea>中間的換行符號n吃掉
所以在<textarea></textarea>中的文字全部都會擠成同一行

解決之道:
到lifetype的資料夾底下
lifetype / js / tinymce tiny_mce-plog.js 這個檔案裡
加入這一行
remove_linebreaks : false ,
儲存之後就ok了。

[Lex] 簡易語法教學

Lex是個古老的工具
雖然是個老東西,但是還是挺好用的!
Lex的功用主要是對一個文件寫下rule
然後產生一個compiler去paser這種文件

安裝:
  目前Lex / Flex在linux下皆可以安裝執行
  以Ubuntu為例,只要下指令
  % apt-get install flex
  即自動幫你安裝完成
  接著只要輸入指令flex即可執行Lex程式了

執行Lex的順序:
  Lex的input file,必須是*.l 的檔案 ( 副檔名為l ... 小寫的L )
  接著只要輸入指令
  % flex test.l
  然後Lex就會自動產生一個output file:lex.yy.c
  接著只要compile這個lex.yy.c 就可以執行這個token parser了
  % gcc lex.yy.c -ll
  而-ll是為了include lex的library

Lex的Input File架構:
  *.l 主要分三個部分:definition & rules & user code
  這三個部分以「%%」為分界

  definition
  %%
  rules
  %%
  user code


  definition:使用者自己定義的變數,都放在這個地方
  rulesparser對token match的規則
  user code:最後產生的lex.yy.c最底下會有一模一樣的code

Definition:
  在Definition的區間裡,可以宣告一些在rule中的code要使用的變數(寫法跟c一模一樣)
  而這些code必須用%{%} 將跨行的code包起來
  因為在這個區間的code都會被完完整整、一字不漏地output至lex.yy.c檔中
  所以在compile lex.yy.c檔時,才不會產生error!
  Ex:
  %{//要記錄parser的input file的總字數與行數
     int num_char = 0;
     int num_line = 0;
  %}
  %%
  \n { num_line++; }
  . { num_char++; }

  也可以宣告一些「rule的變數」,讓rule的寫法更簡潔
  寫法為:
  name definition
  
  Ex :
  number [0-9]+
  identifier [a-zA-Z_][a-zA-Z_0-9]*
  %%
  {number} printf("%s this token is a number\n", yytext);
  {identifier} printf("%s this token is a identifier\n", yytext);

  上面的意思其實就是...
  {[0-9]+} printf("%s this token is a number\n", yytext);
  {[a-zA-Z_][a-zA-Z_0-9]*} printf("%s this token is a identifier\n", yytext);

Rule:
  要對input file切token的規則,全寫在這裡。
  寫法的規則是:
  pattern action
  
  pattern可以輸入一些正規表示法,或是一些word,而正規表示法在此不再贅述
  想了解的人,自己想辦法吧,筆者累了....Or2...
  action則是當pattern match後,執行相對應的code(跟c一模一樣),因此這些code會原封不動地寫入output file中
  若action的code太多,則可以用"{" "}"跨行將code包起來
  Ex:
  [0-9]+ ECHO;printf("this is a number!\n");
  等同於...
  [0-9]+ {
        ECHO;
        printf("this is a number!\n");
       }
  
  在這邊有一個特別的word可以用在action中
  ECHO 可以印出yytext(match pattern的字串)中的內容至output中

Global Variable:
  這個是lex的預設變數,在寫*.l檔的definition & rule時,可以直接使用這些變數
  yyin 是lex的input來源,型態為FILE * ,初始預設為stdin
  yytext 當rule中match一個pattern時,match的string就會存在yytext中,型態為char *
  yyleng 記錄yytext的長度
  yylineno 記錄目前的yyin讀到第幾行了
  

Example:
  這是一個計算input file的總字數&行數的lex檔  

%{
int num_lines = 0, num_chars = 0;
%}

%%
\n   { ++num_lines; ++num_chars; }
.    { ++num_chars; }

%%
main()
{
yylex();
printf( "# of lines = %d, # of chars = %d\n",
num_lines, num_chars );
}

[BCB] 無法new出新的TXMLDocument

BCB中有提供一個內建的物件TXMLDocument
它可以讀進一個xml檔,並建成一個完整的tree
讓user透過它對這個xml檔做操控
而這個物件可透過拖拉Tool bar 「Internet」中的XML圖檔至編輯器中

但是如果想新建的xml的文件一多該怎麼辦?
要read進100個xml檔 就該拉100個TXMLDocument至Form中嗎?!
那這樣就太可怕了....

 

一開始我以為可以自己寫一個TXMLDocument的pointer
再去new出一個TXMLDocument的物件出來就好了....
結果卻一直有問題...
一直出現不能create TXMLDocument的error!....
TXMLDocument * obj = new TXMLDocument("");
obj->LoadFromFile("user.xml");

後來找到這篇文章才知道
http://www.tinydust.net/prog/diary/2004/02/bcbtxmldocument.html
原來不能直接使用TXMLDocument
而要透過Delphi Interface的物件去create它才行
_di_IXMLDocument

_di_IXMLDocument doc = LoadXMLDocument("user.xml");

其他操作都跟TXMLDocument都一樣了

[PHP] Linux底下 透過php呼叫外部程式執行開檔 發生錯誤

我撰寫了一個php的網頁
透過exec呼叫一個外部程式a.out
a.out裡頭執行了open file的動作
不管是用fopen() 還是ifstream.open()
原本不存在這個檔案 還是原本存在這個檔案
最後的結果都是open file fail!

讓我覺得很奇怪
明明直接由console執行這個a.out的程式
一切正常....open file success!!!


但是一透過php去執行它,開檔就會失敗....不知為何...

以下為php的code..
<?php
echo exec("/home/falldog/a.out");
?>

a.out的source code

#include <iostream>
int main(int argc, char** argv){
FILE * file = fopen("/home/falldog/aaa.txt","w");
if( file==NULL )
printf("open file error!!!n");
else
printf("open file success!!!n");
return 0;
}


試了很多次
最後才發現只要將路徑改為/tmp/就ok了
原來…透過php去呼叫a.out
可能牽涉到檔案目錄權限問題...因為/home/falldog/底下不為php網頁的權限目錄
因此,開檔時,會有錯誤發生
所以只要將這個暫存檔開啟在/tmp/就不會發生這樣的問題了

修改Lifetype的中文翻譯

如果對Lifetype中文版的中文翻譯有點不滿意的話
可以自己到 locale/locale_zh_TW.php這個檔案修改裡面的內容

因為我本人是不太喜歡將「一月」稱為「元月」,感覺很怪Tongue out
譯者不要打我...我沒有說你翻得不好....

增加SyntaxHighlighter到自己的Lifetype上

SyntaxHighlighter
是一個能為程式碼加上顏色的好物!
方法是透過javascript & css為<textarea>中的code加上顏色


主要是參考
中文翻譯者的網站:
http://www.dcaid.com/article/article.asp?tid=52

原作者網站:
http://www.dreamprojections.com/syntaxhighlighter/

首先 到這個地方下載我自行改好的程式
下載

將這個檔案解壓縮後放在lifetype的目錄下

然後到你所使用的template的目錄下
ex: templatesstandard
找到header.template與footer.template

首先在header.template裡頭的<head>...</head>之間插入

<link type="text/css" rel="stylesheet" href="SyntaxHighlighter/Styles/SyntaxHighlighter.css"></link>
<link href="SyntaxHighlighter/Styles/TestPages.css" rel="stylesheet" type="text/css">

然後再到footer.template裡頭的</body>之前插入

<script class="javascript" src="SyntaxHighlighter/Scripts/shCore.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushCSharp.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushPhp.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushJScript.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushJava.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushVb.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushSql.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushXml.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushDelphi.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushPython.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushRuby.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushCss.js"></script>
<script class="javascript" src="SyntaxHighlighter/Scripts/shBrushCpp.js"></script>
<script class="javascript">
dp.SyntaxHighlighter.HighlightAll('code');
</script>

[Perl] 用正規表示法做出trim的功能

trim的功能就是去掉前後多餘的空白

$var = " abc_ def_ ";
$var =~ s/^(s)+//; # 去前面空白
$var =~ s/(s)+$//; # 去後面空白
#$var結果="abc_ def_"

[Perl] 做出2維陣列

Perl有@var的1維Array
這是大家所熟悉的
至於要如何做出2維的Array呢?

在Perl裡面
如果@var是1維
那麼@@var就是2維!? 錯了!大錯特錯.... 我一開始就是這麼想的...Embarassed


其實Perl的2維陣列 是透過reference(參照)所產生出來的
ex:
my @one_dim = split(/ /, "a b c d e");
my @two_dim = (); # 此時的@two_dim還是1維的
$two_dim[0] = @one_dim; # @即是Reference, 此時的@two_dim就變2維的了!
for(my $i=0 ; $i<5 ; $i++ ){
print $two_dim[0][$i];
}

不過,這種方法有幾個缺點

缺點一:
就是,這種方法是利用「Reference」!
所以$two_dim[0]是參考了@one_dim的東西
而不是整個複製過來
因此,如果@one_dim的東西改變了
$two_dim[0]的東西(實際上也就是@one_dim的東西)也就跟著改變了!!!

缺點二:無法用foreach取得$two_dim[0]的個數
foreach( $two_dim[0] ){...}
這樣就變得很麻煩了...
如果當@two_dim有很多個element時
每一個element都需要一個1維的變數在儲存才行
而且這每一個element的名稱都要不一樣...這樣就會有砂鍋大的問題了!!!

[Perl] swap()

sub swap{
( $_[0], $_[1] ) = ( $_[1], $_[0] );
}


my $a = 100;
my $b = 200;

swap($a, $b);

print $a . ", " . $b . "n";

#結果會是200, 100

[Perl] Sub function傳入兩個以上的Array的問題...

sub FUNC{
foreach(@_){
print "$_n";
}
}
my @zzz1 = split(/ /,"ab1 cd1 ef1");
my @zzz2 = split(/ /,"ab2 cd2 ef2");

FUNC(@zzz1, @zzz2);

原本Perl的sub function傳入多個參數是允許的
但是它會自動將傳入的參數存成@_的Array
所以若傳入兩個Array當參數
則在Sub FUNC中會將這兩個Array視為一個Array...
這樣就會產生問題了...

上面的程式碼就是將@zzz1,@zzz2兩個Array傳入sub FUNC
而sub FUNC會將這兩個Array存至@_中, 視為同一個Array

[Perl] local的用法

原本local的用法
是為了怕變數被sub function更改到 才有這個用法的
因為perl原本都不用宣告就能使用參數了
所以若是
sub Test
{
$abc = 0;
}
$abc = 100;
Test();
print $abc;
#印出來的值會是0
#因為 sub function中,使用了$abc在之前就宣告了, 所以程式會拿之前宣告的$abc的memory來使用
# 所以呼叫Test()後 值就變了

因此,若是不希望sub function會改到全域(呼叫它的也可能是sub function)原有的變數
就必須在sub function裡的變數 加上local這個關鍵字
sub Test
{
local $abc = 0;
}
$abc = 100;
Test();
print $abc;
#印出來的值會是100


另外一點就是...若程式使用了
use strict
就不能使用local了
因為上面這種情形就不會出現了...凡事要用的變數都要宣告
就不會分不清楚是「區域變數」還是「全域變數」了

[PHP] -error- UTF-8檔案中存有BOM

Warning: Cannot modify header information - headers already sent by進入網頁後若出現上面的警告文字

就是當你的檔案想用UTF-8編碼時
若利用UltraEdit-32時 將ASCII轉成UTF-8時
它會自動幫你在檔案前面多加了BOM的資料
但PHP不認識,所以開啟時會出現這樣的訊息

所以若想將原本Big5編碼的檔案轉成UTF-8
可以用Notepad++選擇"格式" => "編譯成UTF-8碼(無BOM)"即可


Notepad++真是個好物啊!
大部分的程式我都用它來撰寫
Notepad++網站連結

[Perl] 精要整理

正規表示式m// 比對
s/// 取代(只取代一次)

加在字尾部分 ex: m/falldog/ig
/s 跨行比對
/g 全域取代
/i 忽略大小寫

字符集...
d 數字(digital),代表[0-9]
w 單字(word),代表[A-Za-z0-9_]
s 空白(space)
f 跳頁(form-feed)
t 跳格(tab)
n 換行(new-line)
r 回行首(carriage return)
....w* => 0個以上的單字
....w+ => 1個以上的單字

 

[Tomcat] 如何防止目錄被瀏覽?

有時候 安全性也是挺重要的
因此,若是怕目錄被別人看透的話,是可以避免的

在 tomcat安裝目錄/conf/底下,找到web.xml檔案,尋找

<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>

把true改成false就好囉 

[JSP] 取得Client的IP

String ip = (String)request.getRemoteAddr(); 

[JSP] 將openDB、closeDB抽開來寫

通常在開發一個與資料庫連結的JSP專案時
常常每一個檔案開頭、結果都會有著同樣的code...
就是openDB, clodeDB的部分...

///一般jsp頁面要讀取資料庫寫法~
<%@ include file="../Connection/openDB.jsp" %>
...
...
<%@ include file="../Connection/closeDB.jsp" %>



///////////////////////////////////////////////////////////////////////////

///將openDB、closeDB抽出來的寫法~
//openDB.jsp的寫法
//...不能用try catch包起來...否則con這個變數只能生存在該try區塊中~
<%
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/SchoolList" );
%>


//closeDB.jsp的寫法
<%
try{
con.close();
}catch(Exception e){
out.println( e.toString() );
}
%>

[JSP] request.getParameter 中文的問題

request.getParameter 出來的結果常常會出現亂碼...這就是編碼的問題...

法1:針對單一個request
String name = new String( request.getParameter("name").getBytes("ISO-8859-1"), "Big5");


法2:針對此頁中的所有request
<%request.setCharacterEncoding("Big5");%>

[Java] 讀檔 對字串切Token

讀檔 對字串切Token

    String fileName = "xxx.txt";

FileReader fr = new FileReader( fileName );//讀檔

BufferedReader stdin = new BufferedReader( fr );
//將fr置入BufferedReader, 只有BufferedReader能readLine()



StringTokenizer stoken = null;

while( stdin.ready() ){

stoken = new StringTokenizer( stdin.readLine() );

while( stoken.hasMoreTokens() ){
System.out.println( stoken.nextToken() );
}
}
 

[Notepad++] 快速鍵改不回來

原本用Notepad++3.5版
為了修改檔案方便
DEL的快速鍵改掉成F3
結果後來就改不回來了...



後來不管是重新安裝or安裝更新版3.7版
還是沒有用

直到找到一個檔案才發現
快速鍵的設定在檔案中


1. 開啟執行對話窗(開始->執行)。注意:Notepad++ 應該關閉。
2. %APPDATA%Notepad++ 複製到執行對話窗,選取 ok,這將會開啟 Notepad++ 目錄視窗。
3. 在開啟的目錄視窗中,開啟shortcuts.xml
4. 找到

<ScintillaKeys>
....
<ScintillaKeys>

 在這個區間裡,應該就會有你所設的快速鍵的設定,把那一行刪除,儲存,就ok了

[Ubuntu] apt-get install出現錯誤

出現cdrom://Ubuntu....的錯誤訊息

必須至/etc/apt/sources.list 把那一行註解掉

[Ubuntu] 網路設定

在/etc/network/中


1. 所以我們先假設網路是由dhcp派送
我們該如何設定 eth0這張網路卡
% vim /etc/network/interfaces
auto eth0
iface eth0 inet dhcp 這樣即可


2. 若我們的IP 是固定的有該如何設
% vim /etc/network/interfaces

# The primary network interface
auto eth0
iface eth0 inet static
address 140.113.208.120
netmask 255.255.255.0
gateway 140.113.208.254
dns-nameserver 140.113.250.135


3. 另外還有loopback記得要設定(照理來說應該預設會有)
% vim /etc/network/interfaces
# The loopback network interface
auto lo
iface lo inet loopback


存檔離開 即可
別忘了 最後一步當然是重新啟動網路服務
讓新的值生效
% /etc/init.d/networking restart

DNS name servers 還有一個地方要改:
% vi /etc/resolv.conf
nameserver 140.113.250.135

從Lifetype移機至Blogger

我想這是個重要的決定,畢業自架主機不是長久的辦法,以後流量愈來愈大(可能...),所以現在用學校的網路還撐得過去,但是以後一定要想辦法移機至虛擬主機上,可能花得錢多多、限製也多多。所以倒不如移至Google的Blogger,畢竟我也是個Google的愛好者(還幻想能到Google上班呢XD)~

而原本Lifetype上使用的dp.SyntaxHighlighter也改用Google Code Pretty,這部分暫時用鴻哲提供的css吧,以後有機會再自己改一下:p