2010年4月30日 星期五

PCMan for Firefox 3.6 ,好用的逛 BBS 工具/Plugin/Extension @ Ubuntu 10.04

pcmanfx


雖然兩年前就知道這個套件,但還是很習慣用 PCMan Lite 而已,只是隨著工作環境的改變,不再只是 Windows 平台,有時是 Linux 或 Mac ,這時候若可以透過瀏覽器幫忙作跨平台,加上只是單純找找資料,也是個不錯的方式!


PCMan 已經有一段時期了,早年跟 KKMan 一樣,透過一個瀏覽介面,提供便利地逛網頁跟逛 BBS 的結合,畢竟台灣最大的地下活動就屬 BBS 了!連現在 2010 年,國外都在用 Web 論壇,台灣現在各大學仍在瘋 BBS 哩!如台灣最大的 BBS 站批踢踢,同時可上線的人數已常常破 10 萬關卡,甚至對岸也會來使用。


關於 Firefox Plugin 的下載,請到這邊:pcmanfx, PCMan Telnet/BBS extension for FIrefox - enable telnet in web browser.


直接下載:pcmanfx-0.1.8.xpi
補充資訊:


By cc 於 2010/04/30 12:59:

pcmanfx 0.1.8版還有一些嚴重的bug,
但是目前開發者似乎沒空處理,
不過有好心人士幫忙修正了,
可以從專案網頁 http://code.google.com/p/pcmanfx/
左上方的Updates進去,再進入issue 27,就可以看到了。
下面是他提供的下載點:
http://cid-2e6c8f3d9767a9d7.skydrive.live.com/browse.aspx/.Public/pcmanfx
抓pcmanfx-0.1.8_r40+_alpha_1.xpi這個回來安裝。


下載後,直接拖拉到 Firefox 瀏覽器上即可安裝,使用上就像另起新頁,用 telnet:// 即可,如 telnet://ptt.cc


2010年4月29日 星期四

Mac Mini 散熱架:鍋墊 + 防滑貼

鍋墊 + Mini


之前一直把 Mini 擺在主機上頭,但我主機上頭有接硬碟,而 Mini 底部是防滑的材質,將導致擺在主機上頭會有聚熱的現象,所以,我就跑去找一下可以拉高間距的東西,第一個想到的就是鍋墊啦!


鍋墊 防滑


並且要找間隔較高的,所以就找到這個竹子做的鍋墊,然而,它的底部是用釘子定在一起,恰好擺在主機上會有滑動的感覺,不穩,所以我又去找防滑貼,逛了 3C 賣場倒沒看到,結果是在一間書店裡,找到勉強可以用的,那個主要是用在椅子下頭的墊子,最後的成品:


鍋墊 + 防滑


只用到兩個防滑墊,把它切成兩半剛剛好四個角,只是這種防滑墊是設計給重物的,所以擺在像機殼這種材質上面,在加上 Mini 的重量,防滑的效果有限,最後我就給它反過來用,把原本鍋墊的正面當反面來用,接觸面積增大,剛好就比較不滑囉!所以,若真的要省錢,大概連防滑墊都不需要買。此次花費:鍋墊 14 元、防滑墊 39 元(8個圓墊,只用兩個)


鍋墊 + Mini


iOS 開發教學 - 使用 Property List 和 SQLite 處理資料儲存

這筆記來自於 CS 193P iPhone Application Development 2010 Winter
課程 - 9. Data in Your iPhone App (February 2, 2010)

無論寫什模樣大小的程式,都是需處理資料儲存的問題,例如依使用者的偏好、遊戲進行的進度,甚至可以增加程式的容錯性等等,這些都需要處理資料儲存的問題。在 iPhone Apps 裡,最簡單的存取資料的方式就是使用 Property List ,也就是應用程式常見的 *.plist 檔案,這非常適合用在小量資料的儲存,但倘若資料量進入了 KB 階段,還可以用用 SQLite 囉!至於什麼時候則不適用 SQLite 呢?以下是投影片提到的:


  • Multi-gigabytes databases

  • High concurrency (multiple writers)

  • Client-server applicaitons

  • “Appropriate Uses for SQLite”

所幸的 iPhone 也可以跑 C 程式,所以也還是可以維持用 C 處理檔案的部份啦,唯一要留意的是了解自己寫的程式,他們儲存資料的相對位置囉。

在進入資料的存取操作前,先簡介一下,對 APP 而言的目錄結構:

<Applocation Home>

MyApp.app

MyApp

MainWindow.nib

SomeImage.png

Documents

Library

Caches

Preferences

並且基於安全問題,限制只能在自己的家目錄進行檔案存取的行為,並且可以利用以下的方式取得對應位置:


  • Basic

    • NSString *homePath = NSHomeDirectory();

    • NSString *tmpPath = NSTemporaryDirectory();

    • NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
      NSString *fileDirPath = [filePath stringByDeletingLastPathComponent];

  • Document

    • NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    • NSString *documentsPath = [paths objectAtIndex:0];


  • 例子

    • Application Home>/Documents/my.db

      • NSArray *paths =
        NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
        NSUserDomainMask, YES);

        NSString *documentsPath = [paths objectAtIndex:0];
        NSString *dbPath = [documentsPath stringByAppendingPathComponent:@"my.db"];

  • 補充

    • 第一次執行程式時,有些情境會替程式準備好預備用的資料,這時就必須進行適當的處理,把原先以預備好的資料複製對應的位置,例如一開始在打包程式時,已經有先做好一個 default.db ,裡面已經有些 table 資料等等,當程式第一次執行時,那就把它複製到對應的位置,如 my.db 等,往後則都是對 my.db 進行操作囉!

      • BOOL check_exists = [[NSFileManager defaultManager] fileExistsAtPath:dbPath];

使用 Property Lists:

用途:處理小量資料

用法:

// Writing
- (BOOL)writeToFile:(NSString *)aPath atomically:(BOOL)flag;
- (BOOL)writeToURL:(NSURL *)aURL atomically:(BOOL)flag;

// Reading
- (id)initWithContentsOfFile:(NSString *)aPath;
- (id)initWithContentsOfURL:(NSURL *)aURL;

或是更強大的 NSPropertyListSerialization

+ (NSData *)dataFromPropertyList:(id)plist format:(NSPropertyListFormat)format errorDescription:(NSString **)errorString;
+ (id)propertyListFromData:(NSData *)data mutabilityOption:(NSPropertyListMutabilityOptions)opt format:(NSPropertyListFormat *)format errorDescription:(NSString **)errorString;

例如將 Array 或 Dictionary 內容寫到檔案,將會以 XML 格式儲存,可使用 initWithContentsOfFile 轉回來:

// write an array to disk

NSArray *my_array = ...
[my_array writeToFile:@"my_array.plist" atomically:YES];

// write a dictionary to disk
NSDictionary *my_dict = ...
[my_dict writeToFile:@"my_dict.plist" atomically:YES];

參考資料:


使用 SQLite:

由於這邊跟 C 語言一樣,細節可參考這篇完整的範例  [C] 使用 SQLite 教學筆記 - 簡單的 C 語言程式範例 ,下面則會附上與 Objective C 的完整例子,主要留意的是 CallBack function 的撰寫與資料的存取使用。

完整的範例程式:


  • [Xcode]->[Create a new Xcode project]->[iPhone
    OS]->[Application]->[Window-based Application]-> 此例以 MyDataHandle
    為例


    • [Xcode]->[File]->[Cocoa Touch Class]->[UIViewController
      subclass] (勾選 UITableViewController subclass) -> 此例以 MyTableList 為例

  • 加入 sqlite3 的函式庫

    • [MyDataHandle]->[Frameworks]->按右鍵 Add -> Existing Frameworks -> 選擇加入 libsqlite3.dylib

程式碼:

MyDataHandleAppDelegate.h

#import <UIKit/UIKit.h>
#import "MyTableList.h"

@interface MyDataHandleAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    MyTableList *table;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

MyDataHandleAppDelegate.m

#import "MyDataHandleAppDelegate.h"

@implementation MyDataHandleAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {  
    table = [[MyTableList alloc] initWithStyle:UITableViewStylePlain];
    [window addSubview:table.view];

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

- (void)dealloc {
    [table release];
    [window release];
    [super dealloc];
}

@end

MyTableList.h

#import <UIKit/UIKit.h>

@interface MyTableList : UITableViewController {
    NSMutableArray *dataSource;
}

@end

MyTableList.m

#import "MyTableList.h"
#import <sqlite3.h>

#define SQL_CREATE_TABLE    "CREATE TABLE IF NOT EXISTS my_table( filed1 char(20) );"
#define SQL_INSERT_ITEM        "INSERT INTO my_table VALUES( 'Hello World' );"
#define SQL_QUERY            "SELECT * FROM my_table;"


static int CallBackFetchRowHandling( void * context , int count , char **value, char **column ) {
    NSMutableArray *dataSource = (NSMutableArray*) context;
    for ( int i=0 ; i<count ; ++i ) {
        //NSLog( @"%s" , value[i] );
        //[dataSource addObject:[NSString stringWithUTF8String:value[i]]];
        [dataSource addObject: [NSString stringWithFormat:@"[db] %s", value[i]]];
    }
    return SQLITE_OK;
}


@implementation MyTableList

- (void)dataReload {
    // get db path
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0];
    NSString *dbPath = [documentsPath stringByAppendingPathComponent:@"my.db"];
  
    // check db exists
    BOOL firstUse = ![[NSFileManager defaultManager] fileExistsAtPath:dbPath];
    //NSLog( firstUse ? @"First-use" : @"Not" );
  
    // open & init & read data
    sqlite3 *db;
    char *err_report;
  
    if ( sqlite3_open( [dbPath UTF8String] , &db ) == SQLITE_OK ) {
        //NSLog( @"%s" , dbPath );
        if ( firstUse ) {
            [dataSource addObject:@"== init my.db =="];
            if( sqlite3_exec( db , SQL_CREATE_TABLE , NULL , NULL , &err_report ) != SQLITE_OK )
                NSLog( @"%s" , err_report );
            if( sqlite3_exec( db , SQL_INSERT_ITEM , NULL , NULL , &err_report ) != SQLITE_OK )
                NSLog( @"%s" , err_report );
        }
        if ( sqlite3_exec( db , SQL_QUERY , CallBackFetchRowHandling , dataSource , &err_report ) != SQLITE_OK ) {
            NSLog( @"%s" , err_report );
        }
    } else {
        NSLog( @"Cannot open databases: %s" , dbPath);
    }
    sqlite3_close( db );
}

- (id)initWithStyle:(UITableViewStyle)style {
    // Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
    if (self = [super initWithStyle:style]) {
        dataSource = [[NSMutableArray alloc] init];
      
        // from property list
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsPath = [paths objectAtIndex:0];
        NSString *plistPath = [documentsPath stringByAppendingPathComponent:@"my.plist"];
        if ([[NSFileManager defaultManager] fileExistsAtPath:plistPath] ) {
            NSArray *data = [[NSArray alloc] initWithContentsOfFile:plistPath];
            [dataSource addObjectsFromArray:data];
            [data release];
        } else {
            [dataSource addObject:@"== init my.plist =="];
            NSArray *data = [[NSArray alloc] initWithObjects:@"[plist] Hello World", nil];
            [data writeToFile:plistPath atomically:YES];
            [dataSource addObjectsFromArray:data];
            [data release];
        }
      
        // from databases
        [self dataReload];

    }
    return self;
}
- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
  
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

#pragma mark Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [dataSource count];
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  
    static NSString *CellIdentifier = @"Cell";
  
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
  
    // Set up the cell...
    cell.textLabel.text = [dataSource objectAtIndex:indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here. Create and push another view controller.
    // AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
    // [self.navigationController pushViewController:anotherViewController];
    // [anotherViewController release];
}

- (void)dealloc {
    [dataSource release];
    [super dealloc];
}

@end

程式第一次執行時,因為沒有 my.plist ,所以會印出 "== init my.plist ==" ,並且建一個 array 把資料存到 file ,至於對 db 而言,第一次因為沒有 db 資料,所以也會建一個,並印出 "== init my.db ==" ,還會去建 table 以及把資料存進去。

展示:

第一次執行:

init use

之後的執行:

next use



2010年4月28日 星期三

[C] 使用 SQLite 教學筆記 - 簡單的 C 語言程式範例

好久沒用 databases 囉!前一次自己使用已經是三、四年前的事了,碩班都是自己維護甚至用 C 語言開發資料的處理部分。回到正題,SQLite 是一套很簡潔的資料庫,適用於不少地方,細節請參考:Appropriate Uses For SQLite


相關學習文章:



此篇僅筆記如何使用,附上完整的範例程式:


#include <sqlite3.h>
#include <stdlib.h>
#include <stdio.h>

#define CMD_CREATE_TABLE    "CREATE TABLE IF NOT EXISTS MyTable( column1 int, column2 char(20) );"
#define CMD_INSERT_ITEM        "INSERT INTO MyTable VALUES( 1 , 'Hello World' );"
#define CMD_QUERY        "SELECT * FROM MyTable;"

int Callback_ShowList( void *context, int count, char **values, char ** columnName )
{
    int i;
    context = NULL;
    for( i=0 ; i<count ; ++i )
        printf( "\t\t%s = %s\n" , columnName[i] , values[i] ? values[i] : "NULL" );
    printf( "\n" );
    return SQLITE_OK;
}

int main( int argc , char* argv[] )
{
    char *error_report = NULL;
    sqlite3 *db = NULL;

    if( argc < 2 )
    {
        fprintf( stderr , "Usage> %s sqlite_db_file\n" , argv[0] );
        exit(1);
    }

    printf( "1> Open a database\n" );
    if( sqlite3_open( argv[1] , &db ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> Cannot open databases: %s\n" , sqlite3_errmsg(db) );
        sqlite3_close( db );
        exit(1);
    }
    else
        printf( "\t> Finish\n" );

    printf( "2> Create a table\n" );
    if( sqlite3_exec( db , CMD_CREATE_TABLE , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> CMD: %s , Error: %s\n" , CMD_CREATE_TABLE , error_report );
        sqlite3_close(db);
        exit(1);
    }
    else
        printf( "\t> Finish\n" );

    printf( "3> Insert a data\n" );
    if( sqlite3_exec( db , CMD_INSERT_ITEM , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> CMD: %s , Error: %s\n" , CMD_INSERT_ITEM , error_report );
        sqlite3_close(db);
        exit(1);
    }
    else
        printf( "\t> Finish\n" );

    printf( "4> Query\n" );
    if( sqlite3_exec( db , CMD_QUERY , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> CMD: %s , Error: %s\n" , CMD_QUERY , error_report );
        sqlite3_close(db);
        exit(1);
    }
    else
        printf( "\t> Finish\n" );


    if( argc > 2 )
    {
        printf( "N> Do argv[2] ...\n" );
        if( sqlite3_exec( db , argv[2] , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
        {
            fprintf( stderr , "\t> CMD: %s, Error: %s\n" , argv[2] , error_report );
            sqlite3_close(db);
            exit(1);
        }
        else
            printf( "\t> Finish\n" );
    }
    return 0;
}


流程:


依序是建立資料庫、資料表、新增資料、查詢和最後如果有另外傳參數的話,幫你執行一下!


編譯:


此例在 Mac OS 10.6.2
$ gcc -lsqlite3 main.c


成果:


$ ./a.out 1 "select * from sqlite_master;"
1> Open a database
        > Finish
2> Create a table
        > Finish
3> Insert a data
        > Finish
4> Query
                column1 = 1
                column2 = Hello World

        > Finish
N> Do argv[2] ...
                type = table
                name = MyTable
                tbl_name = MyTable
                rootpage = 2
                sql = CREATE TABLE MyTable( column1 int, column2 char(20) )

        > Finish


2010年4月24日 星期六

小小工作室

+ 防塵


中午買了 KVM Switch 後,就嘗試把環境架一下,當初買 UPS 是打算永不關機的,只是因為目前網路環境是 Private IP ,申請個 ADSL 一個月又要多八百,所以就暫時把 UPS 拿來當穩壓了,的確在目前的環境中,有印象有聽到 2~3 次 UPS 自行切換到自己供電的,不過,室內燈並沒有閃爍不穩,所以,我猜主機 Power 不接 UPS 穩壓也都沒啥問題啦


當初就有打算擺兩台機器的,所以我有準備"跳線"(紅色那條),再加上去年底舊主機壞掉,所以有多一張網卡,目前底下那台機器就是一張內建網卡 + 一張舊 PCI 網卡,但不曉得是不是太久沒過電,一開始裝上去還沒反應,剛剛重插了一下,終於活過來了。所以緊接著就把環境設定一下啦,在 Windows 7 的環境中,只需要對連外網卡去設定"共用網路",一下就設定好囉


此例是共有兩張網卡,對外連線稱作"區域連線",用來對內網路稱作"區域連線2"


[控制台]->[網路和網際網路]->[網路和共用中心]->點選目前的連外網卡(如:區域連線)->[內容]->[共用]->[網際網路連線共用] 勾選"允許其他網路使用者透過這台電腦的網際網路連線來來線",並對"家用網路連線"選擇你的內部網卡(如:區域連線2)


如此一來"區域連線2"就會被設定成一個 static ip 並提供 DHCP 功能,接著我就是利用跳線將兩台電腦接起來,對另一台電腦而言,那就只需用 DHCP 就能夠上網囉!印象中在 Windows XP 時可以透過家用網路設定,那個設定精靈會一步步幫你弄好。


防塵這部分參考碩班大神 Adios 在 Lab 處理機櫃的心得,好像是專門去買門簾來用的。幾個月前,已在光南買了兩支棒子,就是那種加上門上用來加門簾的工具,一支好像 20 元不到吧?接著在搭一條小時就存在的浴巾,可能是買奶粉送的?稍微再把一端用針線縫起來,可以較穩固套住,就這樣變成防塵設備啦!


不敢想像,大一別人在用 P4-1800 時,我還在用 P-200 玩 Win98 寫 VC 程式,說真的我覺得電腦好壞跟程式功力沒甚麼差,大二轉系時才託好友 Kudo 帶我組了人生中的第一台電腦 AMD 1800+ ,我記得那時還在他住了一晚並且看了看南投的建築和好喝的綠豆沙!之後開始慢慢有跟上潮流,不過幾乎都是選當時最低階來用,碩一不久後,那台 AMD 1800+ 在宿舍常常跳電的情境下永長眠,那時起我開始用大學買的二手品 AMD 800+ 的主機,因為家裡也需要電腦,所以我搬了一台最不穩的來用,畢竟出問題我還會調教嘛,直到一台台主機先後長眠後,幾個月前,我用的電腦才正式進入多核心的時代,從 1 跳到 4 ,哈。


不小心回憶起老故事了,從幾乎都用最舊的電腦,變成現在這個樣子,自己多看起眼都覺得非常奢侈啊~一定更要努力賺錢囉!應該不會有其他理由在敗電腦產品了!!