27 12月 2007

[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()!!!

26 12月 2007

Netlimit IP Switcher前進SourceForge!

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

SourceForge Netlimit IP Switcher

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

03 12月 2007

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

16 11月 2007

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 \;」,結果檔並不會產生,真的是很奇怪,一定要三部分一起寫才有用,有高手可以跟我解釋一下嗎>_<

15 11月 2007

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

14 11月 2007

[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

30 10月 2007

BCB Indy的元件 IdTCPClient的OnWork有問題

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

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

15 10月 2007

[Linux] libtool: -e: command not found

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

解決方法:
export SED=sed

09 10月 2007

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

06 10月 2007

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。

05 10月 2007

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>才行

04 10月 2007

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;
}

03 10月 2007

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. 增加功能: 下次啟動後 自動開始執行 (可放個捷徑在「程式集」中的「啟動」資料夾,開機後就會自動執行了)

下載頁面