2015年11月18日 星期三

【分享】收集個人覺得不錯的英文教學影片

先來看看賴世雄老師談如何學英文,我大概在高二開始(二十年前)每天聽半時他的常春藤解析英語,一直到大學聯考,幫助是不管期中考試,或大學聯考,英文這科拜歐其實都不太需要準備,聯考也考了個還可以的89分。

現在還會推薦賴世雄老師演講的原因在於,拜歐自己聽了一遍這影片一次,有點感慨,發現自己真能在前面的二十年前利用自己零星,虛渡過的那些時間,照影片中的方式去學習,現在英語無論在聽、說、讀、寫,跟現在比較,一定是雲泥之別。

PS.若時間不多,你可以跳過前一小時,XD


【IDIOM 100】昰介紹一百個英語中的常用語,雖然字幕是日文和英文,但片中全部都是用英語講說,加上這個老師發音很標準,所以講的速度對拜歐來講,是適中且易懂的。

她的講解都會搭配個情境小卡來幫助理解的,是個來學英語中慣用語很棒的教學影片。



TED對拜歐來講,有些演講者講得速度太快,而且有些主題是比較專業,可能會有很多不熟的單字出現,聽起來會很吃力,很難跟上。

拜歐的方法是挑一些熟悉的主題,而且講者的速度是比較慢一點的,多看幾次,應該對聽力有所幫助。

像下面這個講者的主題跟教育有關,而且速度比較慢且發音很標準,是拜歐比較聽得懂的。


【秧秧教英語】是拜歐無意間看到的影片,一系列用大家所熟悉的美國熱門影集來教大家美國日常會用到的慣用詞等,趣味性是一百分,也讓人覺得學英語是很有意思的,更一進步貼近生活。

應該因為是滿久前的影片了,大部份影片的畫質也不是很理想,還有拜歐喜歡的六人行,也只有這幾部,令人只覺得可惜,但也不失為一個學習英文的好地方。

2015年10月16日 星期五

【筆記】使用Java練習觀察者模式(Observer pattern)

Subject.java
package idv.jk.study.designpattern.observer;

import java.util.ArrayList;
import java.util.List;

public abstract class Subject
{
    private String mMessage;
    List mObserverList = new ArrayList();

    public void add(Observer observer)
    {
        mObserverList.add(observer);
    }

    public void remove(Observer observer)
    {
        mObserverList.remove(observer);
    }

    public void broadcast()
    {
        for(Observer o : mObserverList)
        {
            o.update();
        }
    }

    public String getMessage()
    {
        return mMessage;
    }

    public void setMessage(String message)
    {
        this.mMessage = message;
    }
}
Observer.java
package idv.jk.study.designpattern.observer;

public abstract class Observer
{
    protected String mName;
    protected Subject mSubject;

    public abstract void update();
}
Sheep.java
package idv.jk.study.designpattern.observer;

public class Sheep extends Observer
{
    public Sheep(String name, Subject subject)
    {
        this.mName = name;
        this.mSubject = subject;
        subject.add(this);
    }

    @Override
    public void update()
    {
        System.out.printf("通知 %s:%s%n", mName, mSubject.getMessage());
    }
}
SheepDog.java
package idv.jk.study.designpattern.observer;

public class SheepDog extends Subject
{
}
Main.java
package idv.jk.study.designpattern.observer;

public class Main
{
    public static void main(String[] args)
    {
        //Shaun the sheep中的那隻牧羊犬
        SheepDog bitzer = new SheepDog();

        //chief actor
        Sheep shaun = new Sheep("Shaun", bitzer);

        //Shaun's cousin, is the flock's only lamb,
        Sheep timmy = new Sheep("Timmy", bitzer);

        //Shirley is the largest member of the flock
        Sheep shirley = new Sheep("Shirley", bitzer);

        //Nuts, is quite an eccentric, but useful sheep and usually like the rest of the flock, accompanies and helps Shaun.
        Sheep nuts = new Sheep("Nuts", bitzer);

        bitzer.setMessage("主人回來了,快點恢復正常羊的樣子");
        bitzer.broadcast();

        System.out.println("晚上了...");
        //晚上了,Timmy比較早睡,Bitzer就不通知他
        bitzer.remove(timmy);
        bitzer.setMessage("星星出來了,大家快來看啊");
        bitzer.broadcast();
    }
}

2015年10月13日 星期二

【筆記】使用Java來做快速排序(quick sort)的簡單範例

上次真正去接觸、實作快速排序,想想是當年上職訓課,老實講,當時也是一知半解,XD
package idv.jk.study.algorithm.sort;

/**
 * Created by bioyang on 2015/10/10.
 */
public class QuickSort
{
    public static void main(String[] args)
    {
        int[] numberArray = new int[]{6, 1, 2, 7, 9, 3, 4, 5, 10, 8};

        new QuickSort().quickSort(0, numberArray.length - 1, numberArray);

        for(int n : numberArray)
        {
            System.out.print(n + "\t");
        }
    }

     /**
     *
     * @param left 最左邊那個數字的index
     * @param right 最右邊那個數字的index
     * @param numberArray 要排序的整數陣列
     */
    public void quickSort(int left, int right, int[] numberArray)
    {
        if(left > right)
        {
            //代表排序已結束
            return;
        }
        int startIndex = left; //代表最左邊那個數字的起始index
        int endIndex = right;   //代表最右邊那個數字的起始index
        int baseValue = numberArray[left]; //用來儲存要排序的數字陣列最左邊的數字
        int temp;   //用來暫存交換時的值

        while (startIndex != endIndex)
        {
            //要先從右往左找
            while (numberArray[endIndex] >= baseValue && startIndex < endIndex)
            {
                endIndex--;
            }

            while (numberArray[startIndex] <= baseValue && startIndex < endIndex)
            {
                startIndex++;
            }

            if (startIndex < endIndex)
            {
                temp = numberArray[startIndex];
                numberArray[startIndex] = numberArray[endIndex];
                numberArray[endIndex] = temp;
            }
        }

        numberArray[left] = numberArray[startIndex];
        numberArray[startIndex] = baseValue;

        //這裡會叫用遞迴,想起有次聽到良葛格說;「遞迴只應天上有,人間只能用迴圈」,XD
        //將原本最左邊的數字歸位後,開始排序以比這個數小的那群數字
        quickSort(left, startIndex - 1, numberArray);
        //將原本最左邊的數字歸位後,開始排序以比這個數大的那群數字
        quickSort(startIndex + 1, right, numberArray);

    }
}

2015年10月12日 星期一

【筆記】用Java實作備忘錄(memoto)模式的簡單範例

嘗試用說故事的方弍來寫看看,XD Main.java
package idv.jk.study.designpattern.memoto;

/**
 * Created by bioyang on 2015/10/12.
 */
public class Main
{
    public static void main(String[] args)
    {
        //有一個叫小明的工程師
        Programmer smallMing = Programmer.beforeWork();
        //他在上班前的身心狀態
        System.out.printf("開始工作前的HP為 %d, 憤怒條為 %d\r\n", 
                            smallMing.getHitPoint(), smallMing.getAngryPoint());

        //為了確認明天是全新的一天,他的大腦要把上班前的狀態給記下來
        ProgrammerBrain brainOfSmallMing = new ProgrammerBrain();
        brainOfSmallMing.setBodyStateMemoto(smallMing.getBodyStateMemoto());

        //小明修了一個Bug
        smallMing.fixBug();
        //這對他的身心狀態沒有太大的影響
        System.out.printf("修了一個Bug的HP為 %d, 憤怒條為 %d\r\n", 
                            smallMing.getHitPoint(), smallMing.getAngryPoint());

        //客戶改了需求
        smallMing.requirementChanged();
        //要殺死一個工程師,只要改一個需求
        System.out.printf("知道客戶改了需求後的HP為 %d, 憤怒條為 %d\r\n", 
                            smallMing.getHitPoint(), smallMing.getAngryPoint());

        //但一天過去了,又是美好、新的一天
        smallMing.recoverBodyStateMemoto(brainOfSmallMing.getBodyStateMemoto());
        System.out.printf("隔天起床工作前的HP為 %d, 憤怒條為 %d\r\n", 
                            smallMing.getHitPoint(), smallMing.getAngryPoint());
    }
}
Programmer.java
package idv.jk.study.designpattern.memoto;

/**
 * Created by bioyang on 2015/10/12.
 */
public class Programmer
{
    /**
     * 生命值
     */
    private int hitPoint;
    /**
     * 憤怒條
     */
    private int angryPoint;

    private BodyStateMemoto mBodyStateMemoto;

    public BodyStateMemoto saveState()
    {
        return new BodyStateMemoto(this.hitPoint, this.angryPoint);
    }

    public static Programmer beforeWork()
    {
        Programmer programmer = new Programmer();
        programmer.setHitPoint(100);
        programmer.setAngryPoint(0);
        return programmer;
    }

    public void fixBug()
    {
        this.hitPoint -= 5;
        this.angryPoint += 10;
    }

    public void requirementChanged()
    {
        this.hitPoint = 0;
        this.angryPoint = 200;
    }

    public BodyStateMemoto getBodyStateMemoto()
    {
        return new BodyStateMemoto(getHitPoint(), getAngryPoint());
    }

    public void recoverBodyStateMemoto(BodyStateMemoto bodyStateMemoto)
    {
        setHitPoint(bodyStateMemoto.getHitPoint());
        setAngryPoint(bodyStateMemoto.getAngryLevel());
    }

    public int getHitPoint()
    {
        return hitPoint;
    }

    public void setHitPoint(int hitPoint)
    {
        this.hitPoint = hitPoint;
    }

    public int getAngryPoint()
    {
        return angryPoint;
    }

    public void setAngryPoint(int angryPoint)
    {
        this.angryPoint = angryPoint;
    }
}
BodyStateMemoto.java
package idv.jk.study.designpattern.memoto;

/**
 * Created by bioyang on 2015/10/12.
 */
public class BodyStateMemoto
{
    /**
     * 生命力
     */
    private int hitPoint;
    /**
     * 憤怒值
     */
    private int angryLevel;

    public BodyStateMemoto(int hitPoint, int angryLevel)
    {
        this.hitPoint = hitPoint;
        this.angryLevel = angryLevel;
    }

    public int getHitPoint()
    {
        return hitPoint;
    }

    public void setHitPoint(int hitPoint)
    {
        this.hitPoint = hitPoint;
    }

    public int getAngryLevel()
    {
        return angryLevel;
    }

    public void setAngryLevel(int angryLevel)
    {
        this.angryLevel = angryLevel;
    }
}
ProgrammerBrain.java
package idv.jk.study.designpattern.memoto;

/**
 * Created by bioyang on 2015/10/12.
 */
public class ProgrammerBrain
{
    private BodyStateMemoto mBodyStateMemoto;

    public void setBodyStateMemoto(BodyStateMemoto bodyStateMemoto)
    {
        this.mBodyStateMemoto = bodyStateMemoto;
    }

    public BodyStateMemoto getBodyStateMemoto()
    {
        return mBodyStateMemoto;
    }
}

2015年10月10日 星期六

【筆記】ReactJS呼叫外部API練習-顯示YouBike站點列表

最近有機會碰到React除了做完它的教學增加點自信外,再來嘗試做一些套用API的練習,這裡使用台北市政府提供的YouBike臺北市公共自行車即時資訊來取得YouBike站點資訊。

這裡拜歐先用 Node JS開一個網頁伺服器,主要是因為若用JavaScript去對上述的API送要求的話,會有Cross Domain呼叫的問題,會比較麻煩,所以這裡就用Node在伺服器送出要求,取回所需要的資料。

先寫一支名為server.js的程式:
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var request = require('request');

app.set('port', (process.env.PORT || 9999));

app.use('/', express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.get('/api/comments', function(req, res) {
 res.end('abc');
});

    app.get('/youbike/:pageSize/:offset', function(req, res){
    var pageSize = req.params.pageSize,
      offset = req.params.offset;
    request('http://data.taipei/opendata/datalist/apiAccess?scope=resourceAquire&rid=ddb80380-f1b3-4f8e-8016-7ed9cba571d5&limit=' + pageSize + '&offset=' + offset, 
        function(err, response, body){
          if(err)
          {
            console.log(err);
          }
          else if(response.statusCode == 200)
          {
            res.end(body);
          }
    })
    });

app.listen(app.get('port'), function() {
  console.log('Server started: http://localhost:' + app.get('port') + '/');
});
上列程式中,會用到Express這個Framwork,以及request這個package,它主要的功能就是在受到用戶端的要求時,再組成查詢的URLAPI送出要求,取回JSON格式的資料。

再來會有一支名為index.htmlHTML程式來顯示資料:
<!DOCTYPE html>
<html>
<head>
        
 
</head>
<body>
    
</body> </html>
那我們主要的 React是寫在youbiks.jsx中,其中主要定義了YouBikeSiteYouBikeSiteRowYouBikeTable等三個元件:
var YouBikeSite = React.createClass({
 getInitialState : function(){
  return {
   site : [],
  };
 },
   render : function(){
  var site = this.props.site;
    return (
     
{site.sno}
{site.sarea}
{site.sna}
{site.sbi}
{site.mday}
); } }); var YouBikeSiteRow = React.createClass({ render : function(){ console.log('YouBikeSiteList'); return (
{this.props.siteList.map(function(site) { //console.log(site); return (<YouBikeSite site={site} />); })}
); } }); var YouBikeTable = React.createClass({ getInitialState : function(){ return { siteList : [], page : 1 }; }, componentDidMount : function(){ this.loadYouBikeSiteInfo(); }, loadYouBikeSiteInfo : function(){ console.log('this.state.page: ' + this.state.page); var pageSize = 10, offset = (this.state.page >= 1 ? this.state.page - 1 : 0) * pageSize; console.log(offset); var youBikeSiteUrl = '/youbike/' + pageSize + '/' + offset; $.ajax({ url : youBikeSiteUrl, dataType : 'json', cache : false, success : function(data){ if(data.result && data.result.results) { this.setState({ siteList : data.result.results }); } //console.log(this.state.siteList); }.bind(this), error : function(xhr, status, err){ console.error(youBikeSiteUrl, status, err.toString()); }.bind(this) }); }, nextPage : function() { this.state.page += 1; this.loadYouBikeSiteInfo(); }, previousPage : function() { this.state.page -= 1; if(this.state.page < 0) { this.state.page = 0; } this.loadYouBikeSiteInfo(); }, render : function(){ return (

YouBike Site Information

場站代號
場站區域
場站名稱
目前車輛數
資料更新時間
<YouBikeSiteRow siteList={this.state.siteList}/>
) } }); ReactDOM.render( <YouBikeTable />, document.getElementById('container') );
完整的範例可以到 GitHub下載。

2015年9月23日 星期三

【筆記】用Java來做氣泡排序法的簡單範例

一直以來,想給自己補一下在演算法等基礎的不足,所以就從最基本的排序法開始,希望可以持續下去,XD

下列程式是拜歐依書中的範例改成Java版本的。
package idv.jk.study.algorithm.sort;

import java.util.Scanner;

/**
 * Created by javakid on 15/9/22.
 */
public class BubbleSort
{
    public static void main(String[] args)
    {
        int scores[] = new int[100];
        int size;
        System.out.println("輸入分數的總數...");
        Scanner scanner = new Scanner(System.in);

        size = scanner.nextInt();

        System.out.println("輸入分數(用,隔開)...");
        String strNumbers = scanner.next();

        String numbers[] = strNumbers.split(",");

        //把輸入的數字存入到陣列中
        for(int i = 0; i < numbers.length; i++)
        {
            scores[i] = Integer.parseInt(numbers[i]);
        }

        int temp;   //於大小比對時暫存數字的變數

        //開始對所有數字做排列
        for(int i = 0; i < size; i++)
        {
            //size - i表第2次只要比對size-1個數字,
            // 第3次只要比對size-2個數字,以此類推
            for (int j = 0; j < size - i; j++)
            {
                //由小至大,兩兩比對做數字交換
                if (scores[j] < scores[j+1])
                {
                    temp = scores[j];
                    scores[j] = scores[j+1];
                    scores[j+1] = temp;
                }
            }
        }

        //印出結果
        for(int i = 0; i < size; i++)
        {
            System.out.printf("%d ", scores[i]);
        }
    }
}
重點
  • 若有 n 個數字要進行排多,只需將 n-1 個數字歸位
  • 相鄰的數字比較後,依需要做大小交換
  • 時間複雜度為O(N2)

若真想打好演算法的底子,建議您拜歐正在念的這本:



2015年8月28日 星期五

【心得】初老宣言XD

圖片來源:https://images.unsplash.com/photo-1429198739803-7db875882052?q=80&fm=jpg&s=73fc6976c560896a0561a6d4119803a0

  • 趁年輕好好聽歌,好好流淚。

2015年8月27日 星期四

【筆記】Android Studio的快捷鍵

交互地用MacWindows,有些快捷鍵真的會忘,來記一下吧!

Mac快捷鍵 Windows快捷鍵 動作
Alt + ↑(方向鍵上)、Alt + ↓(方向鍵上) 在相鄰的方法間上下來回
command+ delete Ctrl + Y 刪除整行
command + fn + F12 Ctrl + F12 在類別中搜尋方法
command + N Alt + insert Generate,可以產生建構子、Getter、Setter、覆寫方法等
command + shift + U 選取的字串轉換大小寫
Ctrl + I Ctrl + I 實現(implement)介面或抽像類別的方法(method)

參考資料

2015年8月21日 星期五

【心得】工作上的一些想法

  • 某產品還沒受精時,就在想老闆怎麼會打分數,可以直接跟該產品說:「你已經死了」。
  • 愛抱怨的小孩,很難長大。
  • 做事不要只擔心著會被老闆打搶,偶爾也想想怎樣能給老闆打一槍。
  • 適時的溝通很重要,不要忍著讓原本可以討論的事變成抱怨;最後,把工作環境染成互相消費的氣圍。
  • 不要選擇太安逸的環境,這個時代,安逸代表很低的機會可以長進。

2015年8月20日 星期四

【筆記】Android模擬器(emulator)的快捷鍵

Mac快捷鍵 Windows快捷鍵 效用 備註
Ctrl + F12 垂直、水平翻轉
Esc 返回 對應到裝置的「Back」按鈕
F7 鎖定螢幕,解除螢幕鎖定
Home 回首頁畫面 對應到裝置的「Home」按鈕

參考資料

2015年6月24日 星期三

【筆記】用Java做蛇與樓梯(snakes and ladders)遊戲的簡單範例

今天在書上看到一個蛇與樓梯的程式,其規則大致如下:

  1. 擲骰子來決定要走的步數。
  2. 從數字1開始走到最大數字(如下圖中的25)。
  3. 若走到梯子下,就可以延著梯子爬上去(前進)。
  4. 若走到蛇頭,就要延著蛇身滑下去(倒退)。


其程式碼如下:
package idv.jk.fun;

public class SnakesAndLadders
{
    private static final int finalSquare = 25;

    public static void main(String[] args)
    {
        int[] board = new int[finalSquare + 1];

        for(int i = 0; i <= finalSquare; i++)
        {
            board[i] = 0;
        }

        //定義走到的格子要上樓梯或滑下蛇後,要前進或後退幾格
        board[3] = +8;
        board[6] = +11;
        board[9] = +9;
        board[10] = +2;

        board[14] = -10;
        board[19] = -11;
        board[22] = -2;
        board[24] = -8;


        int diceNumber = 0;//擲出的點數

        int step = 0; //走到第幾格

        while(step < finalSquare)
        {
            //擲骰子
            diceNumber = (int)(Math.random() * 6 + 1);
            System.out.print("擲出的點數: " + diceNumber + ", ");
            step += diceNumber;

            if(step < board.length)
            {
                if(board[step] > 0)
                {
                    System.out.print("上樓梯, 爬上 " + board[step] + " 格, ");
                }
                else if(board[step] < 0)
                {
                    System.out.print("下蛇身, 滑下 " + board[step] + " 格, ");
                }
                step += board[step];
            }
            System.out.println("前進到第 " + step + " 格");
        }
        System.out.println("結束");
    }
}
執行結果為:
擲出的點數: 3, 上樓梯, 爬上 8 格, 前進到第 11 格
擲出的點數: 3, 下蛇身, 滑下 -10 格, 前進到第 4 格
擲出的點數: 3, 前進到第 7 格
擲出的點數: 5, 前進到第 12 格
擲出的點數: 1, 前進到第 13 格
擲出的點數: 1, 下蛇身, 滑下 -10 格, 前進到第 4 格
擲出的點數: 4, 前進到第 8 格
擲出的點數: 4, 前進到第 12 格
擲出的點數: 6, 前進到第 18 格
擲出的點數: 6, 下蛇身, 滑下 -8 格, 前進到第 16 格
擲出的點數: 4, 前進到第 20 格
擲出的點數: 1, 前進到第 21 格
擲出的點數: 4, 前進到第 25 格
結束

2015年6月9日 星期二

【筆記】在Ubuntu 14.04中,用python來連線並查詢MySQL中的資料

先到這裡下載mysql-connector-python_2.0.4-1debian7.6_all.deb
wget http://dev.mysql.com/get/Downloads/Connector-Python/mysql-connector-python_2.0.4-1debian7.6_all.deb
並將其安裝:
sudo dpkg -i mysql-connector-python_2.0.4-1debian7.6_all.deb
安裝完成後,就開始來寫程式吧!這裡將程式存成mysql_query.py
#!/usr/bin/python
import mysql.connector
cnx = mysql.connector.connect(user='cbuser', password='cbpass',
                              host='127.0.0.1',
                              database='cookbook')

cursor = cnx.cursor();
query = "select a, b from test"
cursor.execute(query)

for(a, b) in cursor:
        print("column a is {}, column b is {}".
                format(a, b))

cursor.close()
cnx.close()
test這個資料表中的資料如下:
+---+------+
| a | b    |
+---+------+
| 1 | abc  |
| 2 | xyz  |
+---+------+
最後執行程式:
python mysql_query.py 
執行結果為:
column a is 1, column b is abc
column a is 2, column b is xyz

2015年6月3日 星期三

【筆記】使用MapReduce來替換字串(replace string)的簡單程式

拜歐先把要替換的檔案上傳到HDFS中。

上傳完成後,使用下列指令檢視其內容:
hdfs dfs -cat /user/javakid/replace/replace.txt
輸入的檔案為replace.txt,其內容如下:
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
foo1234foo567foo890foo123foo
再來開始寫程式,拜歐是用Intellij IDEA來撰寫程式,在專案設定中,將編譯的輸出目錄(compile output path)設定為/home/javakid/study/hadoop/classpath

第一個要寫的是 mapper
package idv.jk.study.hadoop.myself;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * Created by javakid on 2015/6/3.
 */
public class StringReplaceMapper
        extends Mapper<LongWritable, Text, NullWritable, Text>
{
    private static final String TARGET = "foo";
    private static final String REPLACEMENT = "bar";

    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException
    {
        String line = value.toString();

        if(line.indexOf(TARGET) >= 0)
        {
            context.write(NullWritable.get(),
                            new Text(line.replaceAll(TARGET, REPLACEMENT)));
        }
    }
}
mapper的輸出,拜歐要的只是替換後的每一行字串,這些值的key是什麼,並不重要,所以在上列程式中的第27行,用NullWritable來做為輸出的key。

再來要寫的是 reducer
package idv.jk.study.hadoop.myself;

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * Created by javakid on 2015/6/3.
 */
public class StringReplaceReducer
        extends Reducer<NullWritable, Text, NullWritable, Text>
{
    @Override
    protected void reduce(NullWritable key, Iterable<Text> values, Context context)
            throws IOException, InterruptedException
    {
        for(Text text : values)
        {
            context.write(NullWritable.get(), text);
        }
    }
}
reducer中,拜歐要的也只是將已經在mapper替換好的字串輸出,在第21行一樣使用NullWritable來做為輸出的key。

最後是主程式:
package idv.jk.study.hadoop.myself;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

/**
 * Created by javakid on 2015/6/3.
 */
public class StringReplace
{
    public static void main(String[] argv)
            throws IOException, ClassNotFoundException, InterruptedException
    {
        if(argv.length != 2)
        {
            System.err.println("Usage: StringReplace <input path> <output path>");
            System.exit(-1);
        }

        Job job = new Job();
        job.setJarByClass(StringReplace.class);
        job.setJobName("String replacement");

        FileInputFormat.addInputPath(job, new Path(argv[0]));
        FileOutputFormat.setOutputPath(job, new Path(argv[1]));

        job.setMapperClass(StringReplaceMapper.class);
        job.setReducerClass(StringReplaceReducer.class);

        job.setOutputKeyClass(NullWritable.class);
        job.setOutputValueClass(Text.class);

        System.out.println(job.waitForCompletion(true) ? 0 : 1);
    }
}
上列setOutputKeyClasssetOutputValueClass這兩個方法控制reduce方法中輸出的型別,而且與reducer中產出的輸出中設定,兩者必須是相同的。

先將HADOOP_CLASSPATH設定好後,用hadoop指令來執行主程式:
export HADOOP_CLASSPATH=/home/javakid/study/hadoop/classpath
hadoop idv.jk.study.hadoop.myself.StringReplace \
/user/javakid/replace/replace.txt /user/javakid/replace/output
執行完成後,使用下列指令來確認結果:
hdfs dfs -cat /user/javakid/replace/output/part-r-00000
預期結果如下:
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
bar1234bar567bar890bar123bar
你可以在這裡找到原始碼。

2015年5月30日 星期六

【筆記】在Ubuntu 14.04建立Hadoop多節點叢集-分散式架構(Multi-node cluster)

這裡使用的版本是2.6.0,之前有些設定如設定JAVA_HOME
或是ssh等,在Setting up a Single Node Cluster己經完成,若有撞牆的地方,可以回去參考一下。

在拜歐的cluster中,會有四台機器,分別為:
  • javakid01:做為master,會用它來跑NameNodeSecondaryNameNode
  • jkserver01:做為slaves,會用它來跑DataNode
  • jkserver02:做為slaves,會用它來跑DataNode
  • jkserver03:做為slaves,會用它來跑DataNode

若要改hostname,你可以這樣做:
sudo vi /etc/hostname
上列四台機器中,先確認已經安裝好JavaHadoop

先對javakid01(master),進行設定,在/etc/hosts中加入:
10.211.55.10    javakid01
10.211.55.7     jkserver01
10.211.55.13    jkserver02
10.211.55.14    jkserver03
再來編輯$HADOOP_INSTALL/etc/hadoop/core-site.xml加入下列設定:

        
                fs.defaultFS
                hdfs://javakid01:9000
        

接著編輯$HADOOP_INSTALL/etc/hadoop/hdfs-site.xml加入下列設定:

        
                dfs.replication
                3
        
        
                dfs.namenode.name.dir
                file:/opt/data/hadoop/hadoop_data/hdfs/namenode
        

上面設定好後,使用下列指令建立上列設定的目錄,建立前請確定有足夠的權限:
mkdir -p opt/data/hadoop/hadoop_data/hdfs/namenode
接著編輯$HADOOP_INSTALL/etc/hadoop/mapred-site.xml加入下列設定:

        
                mapred.job.tracker
                javakid01:54311
        

接著編輯$HADOOP_INSTALL/etc/hadoop/yarn-site.xml加入下列設定:


        
                yarn.nodemanager.aux-services
                mapreduce_shuffle
        
        
                yarn.nodemanager.aux-services.mapreduce.shuffle.class
                org.apache.hadoop.mapred.ShuffleHandler
        
        
                yarn.resourcemanager.resource-tracker.address
                javakid01:8025
        
        
                yarn.resourcemanager.scheduler.address
                javakid01:8030
        
        
                yarn.resourcemanager.address
                javakid01:8050
        

最後,在$HADOOP_INSTALL/etc/hadoop/slaves中加入做為DataNodehostname
jkserver01
jkserver02
jkserver03
下面要開始對做為slaves的機器進行設定,其中jkserver01jkserver02jkserver03的設定幾乎是相同的。

一樣先在/etc/hosts中加入:
10.211.55.10    javakid01
10.211.55.7     jkserver01
10.211.55.13    jkserver02
10.211.55.14    jkserver03
請注意!上列的IP會因機器不同而有差異,這裡設定是以我的機器為例。

再來編輯$HADOOP_INSTALL/etc/hadoop/core-site.xml加入下列設定:

        
                fs.defaultFS
                hdfs://javakid01:9000
        

接著編輯$HADOOP_INSTALL/etc/hadoop/hdfs-site.xml加入下列設定:

        
                dfs.replication
                3
        
        
                dfs.datanode.data.dir
                file:/opt/data/hadoop/hadoop_data/hdfs/datanode
        

上面設定好後,使用下列指令建立上列設定的目錄,建立前請確定有足夠的權限:
mkdir -p opt/data/hadoop/hadoop_data/hdfs/datanode
接著編輯$HADOOP_INSTALL/etc/hadoop/mapred-site.xml加入下列設定:

        
                mapred.job.tracker
                javakid01:54311
        

接著編輯$HADOOP_INSTALL/etc/hadoop/yarn-site.xml加入下列設定:


        
                yarn.nodemanager.aux-services
                mapreduce_shuffle
        
        
                yarn.nodemanager.aux-services.mapreduce.shuffle.class
                org.apache.hadoop.mapred.ShuffleHandler
        
        
                yarn.resourcemanager.resource-tracker.address
                javakid01:8025
        
        
                yarn.resourcemanager.scheduler.address
                javakid01:8030
        
        
                yarn.resourcemanager.address
                javakid01:8050
        

最後,也要將讓DataNode知道slaves的設定。在$HADOOP_INSTALL/etc/hadoop/slaves中加入做為DataNodehostname
jkserver01
jkserver02
jkserver03
接下來,為了讓master不用密碼就可登入slaves的話,需要進行SSH連線的設定。

回到javakid01這台機器,在終端機下執行:
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
然後執行:
ssh-copy-id -i ~/.ssh/id_rsa.pub javakid@jkserver01
完成後,由javakid01登入jkserver01,第一次登入會需要在jkserver01上的使用者密碼:
ssh jkserver01
jkserver02jkserver03進行相同設定,在javakid01執行:
ssh-copy-id -i ~/.ssh/id_rsa.pub javakid@jkserver02
登入jkserver02
ssh jkserver02
接著執行:
ssh-copy-id -i ~/.ssh/id_rsa.pub javakid@jkserver03
登入jkserver03
ssh jkserver03
全部設定完成後,在javakid01執行下列指令對HDFS進行格式化:
hadoop namenode -format
接著啟動 HDFS Daemons
$HADOOP_INSTALL/sbin/start-dfs.sh
成功啟動後,在javakid01執行jps指令:
jps
應該可以看到下列的結果:
27964 NameNode
28220 SecondaryNameNode
29695 Jps
再來若是到jkserver01jkserver02jkserver03任一台機器執行jps,應該可以看到下列結果:
2204 DataNode
2378 Jps
若在瀏覽器輸入http://javakid01:50070這個網址,應該在Datanodes這個頁籤看到三台機器的資訊:


2015年5月26日 星期二

【筆記】Hadoop名詞解釋

名詞意思備註
metricsHDFS與MapReduce daemon會收集一些與事件以及量測相關的資訊,這些總稱為metrics例如,datanode會收集已寫入的byte數、已複製的block總數等metrics
data locality optimization Hadoop在執行map task時,會盡全力將該task放到存放輸入該task資料的那台node去執行,以免使用到寶貴的頻寬進行資料的傳送
shuffle A proccess by which the system performs the sort and transfers the map outputs to the reducers as inputs.
speculative execution Hadoop doesn't try to diagnose and fix slow-running tasks; instead, it tries to detect when a task is running slower than expected and launches another equivalent task as a backup.

2015年5月5日 星期二

【分享】解決Java連Mongo DB時,出現no such cmd: saslStart錯誤

今天在使用JavaMongo DB時發生了一個錯誤,訊息如下:
Exception in thread "main" com.mongodb.CommandFailureException: { "serverUsed" : "192.168.55.12:22004" , "ok" : 0.0 , "errmsg" : "no such cmd: saslStart"}
我使用的 Java MongoDB Driver2.12.3而連線的Mongo DB版本是2.4.3

使用的程式碼如下:
MongoCredential credential = MongoCredential.createPlainCredential("foo", "testDB", "bar".toCharArray());
MongoClient mongoClient = new MongoClient(new ServerAddress("192.168.55.12", 22004), Arrays.asList(credential));
DB db = mongoClient.getDB("testDB");
後來發現在使用MongoCredential.createPlainCredential這個方法時,在Mongo DB2.6版本是才支援的,詳細可以參考文件內說明。

2015年4月28日 星期二

【分享】解決MacBook Pro 開機、關機變慢的問題

開機變慢


好像在上周更新過OS X Yosemite之後,開機就明顯變得很慢:


這裡無法確認更新是否為確切造成開機變慢的原因,只是剛好在此時間點後,狀況變得明顯。

一開始在網路上找到的解法,不外乎是「修復磁碟權限」、把不必要的「登入項目」關閉,或是清除桌面等,該試的都試過了,拜歐一開機等白蘋果出來前,還是要望著非常久的黑螢幕,想著為何會變這麼慢。

後來找到這串討論串:請問各位大哥 為什麼MBP 開機會變慢,六樓朋友的回應,解決了拜歐開機速度慢的問題。

拜歐是這樣做的,先到【系統徧好設定】,在下方倒收第二排有個【啟動磁碟】:


點進去後,在「請選擇要用來開機的系統」中,選擇要開機的硬碟後,再重新開機:


上面的做法解決了拜歐的問題,不禁猜想,會不會是兒子三不五時,找到機會就偷敲鍵盤造成設定跑掉的,XD

關機變慢


Google上打了「macbook pro 關機變慢」的關鍵字,第一筆出現這一篇:解決升上Yosemite-OS X10.10關機速度慢的問題,這位樓主分享了一篇在Apple Support Communities上討論到Slow shutdown on Yosemite的問題。

這個提到MacBook Pro會變慢的原因,可能是有裝舊版本的Parallels Desktop,受其影響的關係,拜歐的MacBook Pro也有裝,心想,有可能是這個原因。

其解決方法提到,要到~/Library/LaunchAgents~/Library/LaunchDaemons把有「parallels」字眼的檔案都清掉,在拜歐的電腦中只找到~/Library/LaunchAgents這個資料夾,而就索性把~/Library/LaunchAgents裡面的檔案全刪了,果然,關機就感覺有恢復跟原來一樣的速度,而Parallels Desktop也能正常運作。

請注意,這解決了拜歐的問題,不一定適用看倌您的情境,請在刪不知用途的檔案前,請三思而後行XDD

討論串中還有一位仁兄提到~/Library/Preferences這個資料夾中和「parallels」有關的檔案也要刪,這裡面的拜歐沒動,就不知道有沒有助益了。


2015年4月25日 星期六

【筆記】在PHP中太長的數字以科學符號(scientific notation)顯示的解決方案

寫過PHP的人一定遇到,過長的數字echo出來後,非所預期地以科學符號顯示的問題,如:
$f1 = 201504242111234; 
$f2 = 201504191213211;

echo $f1 . ',' . $f2;
一般預期的結果應該為:
201504242111234,201504191213211
但實際上,結果可能為:
2.0150424211123E+14,2.0150419121321E+14
這時若只是要在網頁上顯示所要的格式,可以使用printf(文件)這個方法,它的用法是:
int printf ( string $format [, mixed $args [, mixed $... ]] )
其中,第一個參數為要印出的格式設定,詳細說明可以參考這裡

據此,可以把上面echo的這一行替換成下面程式:
printf('%.0f,%.0f', $f1, $f2);
這裡%.0f的意思是說,以浮點數的格式去顯示後面傳入的參數,在其中的0是指小數點後要顯示的位數為「0」。

另外,因為%.0f有兩個,後面要傳入對應的兩個浮點數。

有時,在一些情況下需要在程式中將兩個浮數數串在一起,好讓後面的程式使,這時就要使用到sprintf(文件)這個方法:
string sprintf ( string $format [, mixed $args [, mixed $... ]] )
從上面可以看到,它回傳的是一個字串,所以可以這樣使用:
$s1 = sprintf('%.0f', $f1);
$s2 = sprintf('%.0f', $f2);

$s3 = $s1 . ',' . $s2;

//$s3有可能有其他如做為SQL的運用
//這裡僅把它印出來
echo $s3;

【分享】學習正規表示式(Regular Expression)的資源

Regular Expression翻做中文為正規表示式,又稱正則表達式、正規表示法、正規運算式、規則運算式、常規表示法(wiki),在此拜歐習慣使用正規表示式這個翻譯,因為它唸起來最順,XD。

之前斷斷續續地涉略或鑽研正規表示式的用法,期間也找到一些在網路上覺得不錯的資源,因此在此記錄與分享。

OCPSoft


OCPSoft是一群喜好開放原始碼開發者的組織,在它的網站上找到兩篇講述Java和正規表示式的教學資源。

Guide to Regular Expressions in Java (Part 1)


Guide to Regular Expressions in Java (Part 2)

2015年4月24日 星期五

【筆記】在JavaScript中使用正規表示式擷取目標字串

(function(){

 var input = '1990Mar02Taipei35';

 var num = /(\d+)\D+(\d+)(\D+)(\d+)/g;
 
 var result = num.exec(input);

  console.log(RegExp.$1 + ' 年在 ' + RegExp.$3 + ' 的數值是: ' 
              + RegExp.$4);

})();

【筆記】在Java中使用正規表示式(Regular Expression)切割字串

在工作上若碰到要在一行字串去擷取資料時,如下面記錄某年某月某日某地區的某個數值:
1990Mar02Taipei28
常是以substring算好位置去截取各個資料,若有時可能應程式更新造成了一些例外,如上面的月份是英文簡寫,但程式更新後,記錄月份的方式變成:
1991March03Tainan35
這時若再用substring來切字串,就可能要做一些判斷來算位置。

當每一行資料的模式都是一樣時,如上面兩筆資料,都是數字、英文字、數字、英文字、數字時,就可以使用Pattern(文件)搭配Matcher(文件),以正規表示式來切字串:
package idv.jk.util.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MatcherTest
{
    public static void main(String[] arv)
    {
        String input = "1990Mar02Taipei28\r\n1991March03Tainan35";
        Pattern pattern = Pattern.compile("(\\d+)(\\D+)(\\d+)(\\D+)(\\d+)",
                                            Pattern.MULTILINE);

        Matcher matcher = pattern.matcher(input);

        while(matcher.find())
        {
            System.out.println("The value at " + matcher.group(4) +
                                    " in " + matcher.group(2) +
                                    " " + matcher.group(1)+
                                    " is " + matcher.group(5));
        }
    }
}
結果為:
The value at Taipei in Mar 1990 is 28
The value at Tainan in March 1991 is 35
上面範例用簡單的資料,來說明正規表示式的功用,在現實的工作上,資料模式通常都複雜許多,但若能熟悉正規表示式並應用得當,在檔案處理上必能得心應手。

若想進一步鑽研,可以考慮這一本:


它也有中文版,XD

2015年4月22日 星期三

【筆記】在Ubuntu 14.04.2 Server安裝Redis(版本3.0.0)

Redis目前官方有支援的版本就是Linux-based的作業系統,在這裡拜歐是在Ubuntu 14.04.2 Server安裝Redis,版本為3.0,先到這裡下載Redis

在終端機中,輸入下列指令下載:
wget http://download.redis.io/releases/redis-3.0.0.tar.gz
下載完成後,將其解壓縮:
  
tar xzv -f redis-3.0.0.tar.gz
進入redis-3.0.0目錄中執行make指令:
 
cd redis-3.0.0
make 
若顯示找不到make指令的訊息,請執行下列指令進行安裝:
 
sudo apt-get install make
再來若遇到下列錯誤訊息:
/bin/sh: 1: cc: not found
make[1]: *** [adlist.o] Error 127
make[1]: Leaving directory `/home/javakid/tools/nosql/redis-3.0.0/src'
make: *** [all] Error 2
這裡要注意的是第一行的訊息,若是出現這個訊息,請執行下列指令進行安裝:
 sudo apt-get install build-essential
若有什麼檔案找不到的,如下列訊息:
In file included from adlist.c:34:0:
zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory
 #include <jemalloc/jemalloc.h>
                               ^
解決辦法只要去把缺的檔案make出來:
cd deps 
make jemalloc
然後再回到redis-3.0.0目錄,再執行make

若是有再遇到下列訊息:
cc: error: ../deps/hiredis/libhiredis.a: No such file or directory
cc: error: ../deps/lua/src/liblua.a: No such file or directory
make[1]: *** [redis-server] Error 1
make[1]: Leaving directory `/home/javakid/tools/nosql/redis-3.0.0/src'
make: *** [all] Error 2
一樣先到deps執行,把缺的檔案make出來:
cd deps
make hiredis lua jemalloc linenoise
完成後,再回到上一層再執行make
cd ../
make
make執行完畢後,執行下列指令,開始執行指令啟動Redis
 src/redis-server

2015年4月15日 星期三

【筆記】在Java中爬取網頁內容-使用jsoup來做html parser

日前因工作上的需要,寫Java程式去爬網頁,爬回來的網頁要去解析出要用的資料,拜歐發現jsoup這個函式庫非常好用,在這裡簡單介紹一下。

在這裡,拜歐要把Google新聞首頁的標題都爬回來,先找到包含標題的HTML標籤裡,可以辨別的屬性,如:


從上面可以看到要包著標題的是span標籤,而且它有一個名為titletextCSS類別,所以使用jsoup函式,可以這樣做:
package idv.jk.web.parser;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.Iterator;

/**
 * Created by Javakid on 2015/4/14.
 */
public class HtmlParserMain
{
    public static void main(String[] argv)
    {
        String url = "https://news.google.com.tw/news";
        try {
            Document doc = Jsoup.connect(url).get();
            System.out.println(doc.title());
            Elements h1s = doc.select("span.titletext");

            Element thisOne = null;
            for(Iterator it = h1s.iterator(); it.hasNext();)
            {
                thisOne = (Element)it.next();
                System.out.println(thisOne.html());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面的程式碼中,在第20行先把該網址的網頁內容取回,並且將其封裝成一個org.jsoup.nodes.Document類別的物件。

在第21行呼叫title方法,將該頁的抬頭(title)印出來。

在第22行使用選擇器查詢(selector query)來挑出所要的元素,這裡span.titletext挑選的出來的是class屬性為titletextspan標籤。

撰擇器的語法可以參考這裡,若是熟悉jQuery,一定會覺得一模一樣XD。

然後在第28行把其內含的HTML內容印出來。

結果如下:
Google 新聞
毛揆:尊嚴、公平是加入亞投行最高原則| 聯合影音
入亞投行金額朱立倫:非台灣說了算
加入亞投行毛揆:決定權仍在我方
毛治國:亞投行創始章程無法容納台灣
肺癌躍居10大癌症第二女性患者激增
大腸癌7度居冠子宮頸癌首脫離十大排行
每5分26秒就有1人罹癌大腸癌連7年居首位
十大癌症排行!腸癌7度奪冠最兇猛女性肺癌變多
博科聖地猖狂綁架逾2000女性
被博科聖地擄走50少女據報仍生存
(以下省略)

2015年4月9日 星期四

【筆記】在Java中去除字串的空白字元(字串去空白)

Java若只是要去除字串前後的空白,最簡單的方式就是呼叫String類別中trim這個方法:
String input = " Hello World ";
input = input.trim();
System.out.println(input);
但若是字串中的空白字元呢?

有用Java處理過字串的人,都應該有使用String類別中replaceAll這個方法,來替換字串中的某些字元,其實去仔細看它的API文件後,可以看到傳入這個方法的第一個參數是一個正規表示式,所以可以用正規表示式的來尋找要替換的部份。

所以若在要在 Java程式中把所有的空白字元,包含\t\n\x0B\f\r或空格等空白字元去除,比如現在有個字串如下:
String oldString = " Hello\t\tHello\r\nWorld";
System.out.println(oldString);
則輸出結果為:
  Hello      Hello
World
可以使用如下的正規表示式把上列字串中的空白字元去掉:
String newString = oldString.replaceAll("\\s+", "");
System.out.println(newString);
則輸出結果為:
HelloHelloWorld
而再次看到文件中的說明,其實上列replaceAll這個方法的結果,就跟下列方式相同:
String oldString = " Hello\t\tHello\r\nWorld";
String newString = Pattern.compile("\\s+").matcher(oldString)
                       .replaceAll("");
System.out.println(newString);

【筆記】PHP去除字串的空白字元(字串去空白)

PHP中,是有內建一個trim的方法來去除頭尾的空白,但若要去除字串中的空白呢?這時通常會用字串替換的方式把空白替換成空字串,如下:
//H之前空一格,o與W之間有兩個空格,在d後面又有一個空格
$str = ' Hello  World ';
echo 'head' . str_replace(' ', '', $str) . 'tail';
結果為:
headHelloWorldtail
由上面的結果可知,若在PHP中只是要去掉空格,使用str_replace就夠了,但若在字串中除了空格,還有其他如\t\v\n等的空白字元呢?如下:
//H前面一個空格,但o與W之間還有d後面是Tab
$str = ' Hello World ';
echo 'head' . str_replace(' ', '', $str) . 'tail';
若依str_replace的方法,這裡只能把空白去掉,而其他字元仍會留著,如下面結果:
headHello World tail
這時,要使用preg_replace來搭配正規表示式去除:
echo 'head' . preg_replace('/\s+/', '', $str) . 'tail';
結果為:
headHelloWorldtail
上列的\s+表示只要是有一個以上的空白字元就符合被替換的條件,其意義和符合的字元,可以參考這裡

參考資料

2015年4月8日 星期三

【雜志】人的格局


一個人的格局,決定一個人的世界有多高、多廣。

放大格局,別在意小事。

也別去批判別人的小格局。

也許,別人的格局是不同形狀,

而我只是站在比較窄的這邊看過去。

2015年4月7日 星期二

【筆記】解決Android Studio中Gradle DSL method not found: 'runProguard()'的錯誤

今天開啟好久沒用的Android Studio再更新後,引入一個之前練習用的專案,突然出現下列的錯誤訊息:


於是用下面的片段當關鍵字去搜尋解法:
Gradle DSL method not found: 'runProguard()'
發現原來是Gradle版本的關係,因為runProguard這個方法從Gradle版本0.14.0後,就改名為minifyEnabled

解決方法就是先開啟build.gradle這個檔案:


開啟後,找到下列程式碼:
runProguard false
把他改成
minifyEnabled false

參考資料

【筆記】使用Commons lang中的StringUtils來處理字串

Apache Commons是一個專注於提供可重覆利用的Java元件,其中,好幾個元件在拜歐在工作中開始用Java開發專案,就常用到。

其中 Commons Lang這一元件中,針對了字串、數值,還有物件建立與序列化等等,提供了更多的方法,以補足標準java.lang API的不足之處。

好像不管使用什麼語言,在程式要處理字串的地方,是常常看到的,在 JavaString類別本身就提供了很多來處理字串的方法,但在使用這些方法時,也常都要先判斷String物件是否為null再做呼叫,有時會程式失去的易讀性,如要判斷一個String物件是否為空白字串(""),一般會這樣寫:
if(input != null && input.equals(""))
        
//OR
if("".equals(input))
但若使用StringUtils類別的話,可以呼叫isEmpty這個方法來判斷:
if(StringUtils.isEmpty(input))
//StringUtils.isEmpty(null) 回傳true
//StringUtils.isEmpty("")   回傳true
//StringUtils.isEmpty(" ")  回傳false
這樣不只省了一些程式碼,而且也大大地提升了程式的易讀性。

StringUtils類別中實作了很多方便用來處理字串的方法,這裡再介紹一個拜歐覺得用過來切分字串是非常方便的方法:splitByWholeSeparatorPreserveAllTokens,詳細的文件,可以參考這裡

1,2,,4,5,,若用,去切,在有些情況下,會希望結果是1、2、空白、4、5和空白,先用String的內建的split方法去切:
String input = "1,2,,4,5,";
String[] peices = input.split(",");
System.out.println("字串陣列的長度為: " + peices.length);
System.out.print("字串陣列內的元素為: ");
System.out.print(Arrays.asList(peices));
結果為
字串陣列的長度為: 5
字串陣列內的元素為: [1, 2, , 4, 5]
從上面來看,最後一個空白不見了,這不符合原先預期的結果。這時若使用splitByWholeSeparatorPreserveAllTokens這個方法去切:
String input = "1,2,,4,5,";
String[] peices = StringUtils.splitByWholeSeparatorPreserveAllTokens(input, ",");
System.out.println("字串陣列的長度為: " + peices.length);
System.out.print("字串陣列內的元素為: ");
System.out.print(Arrays.asList(peices));
結果就會如希望地呈現:
字串陣列的長度為: 6
字串陣列內的元素為: [1, 2, , 4, 5, ]
若對其實作有興趣,建議可以下載它的原始檔來瞧瞧。

在使用Commons Lang元件時,有一個小地方要注意的是,它到版本 3.0以後,在套件名稱上有點差異,在3.0以前,套件名稱為org.apache.commons.lang,而之後的套件名稱為org.apache.commons.lang3,這有時是為什麼明明有把函式庫引入,但卻編譯錯誤的原因!

2015年4月4日 星期六

【筆記】Java把長整數拆成一個一個數字

今天同事跑來問拜歐,如何把一個long型態的長整數,比如說,把5678拆成5678等四個數字要怎麼做,其實先把長整數使用String.valueOf轉成字串,再使用String類別中的charAt就可以輕鬆辦到。
package idv.jk.util.lang;

public class LongUtils
{
    public static int[] spitLongToIntArray(long inNumber)
    {
        String strNumber = String.valueOf(inNumber);

        int length = strNumber.length();
        int[] results = new int[length];

        for(int i = 0; i < length; i++)
        {
            results[i] = Character.getNumericValue( strNumber.charAt(i) );
        }

        return results;
    }

    //test
    public static void main(String[] argv)
    {
        for(int i : spitLongToIntArray(5678))
        {
            System.out.print(i + ",");
        }
    }
}
執行結果如下:
5,6,7,8,
這裡要注意的是,若傳入的是負數,字元-經由Character.getNumericValue,關於該方法更詳細的說明,可以參考Java文件,如傳入-5678,執行結果則為:
-1,5,6,7,8,
若有處理負數的需要,則需多加一些處理。

2015年4月1日 星期三

【筆記】在Linux中使用指令來查看資料夾與子資料夾中的檔案數量

Linux中,若要下指令去查看在某個目錄下的檔案,可以用findwc這個兩個指令搭配來達成。

比如說,現在有一個/home/javakid/temp/的資料夾,其中有一個名為6.txt的檔案,以及abc三個資料夾,分別各有2、2、1個檔案,如下圖所示:


開啟終端機後,輸入下列指令:
find /home/javakid/temp -type f
結果顯示如下:


檔案很少時,很容易看出來有幾個檔案,但其中包含的檔案很多時,在終端機一行一行地數,好像就不是很聰明的做法,還有很大的機會數錯。

那如果像下列指令一樣,先把結果輸出到檔案,再看檔案內的行數,好像又有一點點「搞剛」:
find /home/javakid/temp -type f > ../count.txt
拜歐查到最好的做法是,再搭配wc指令:
find /home/javakid/temp -type f | wc -l
上列的意思可以想像為,將find且用-type f找出目錄與子目錄下的檔案,再把輸出的結果,用|傳給wc這個指令,指定-l參數,表示僅列出行數。

若想進一步了解findwc的用法,可以輸入
find --help

wc --help
你也會發現鳥哥的教學網站是座寶山。

最後插一句,寫程式的要進一流的軟體公司,Linux shell是必學的,若真想學好,推薦這本:


參考資料

2015年3月31日 星期二

【雜志】自強不息


日有東昇西熄,
月有夜掛晨閉;
星有高閃低隕,
人應自強不息。

2015年3月26日 星期四

【雜志】日廢十年功


欲跑如驚風,
實速綁重鐘;
習如逆水游,
日廢十年功。

【筆記】開發PHP時,更改Xdebug的port

這一天,如往常一樣,在Eclipse中啟動Xdebug要來除錯,結果轉出兩下就沒動靜了,本來以為是Eclipse又再鬧脾氣了,接著才想起以前有碰到類似的情況,咦!好像是Xdebug要使用的port被佔用了。

netstat -a -o -b(在Windows中)查了一下,原來是預設的9000javaw.exe佔走了,看來改 port是一勞永逸的方法,以前改過,這次要改的時間有漏了一些地方,因此把它記起來。

先在 Eclipse開啟徧好設定視窗,然後前往【PHP】→【Debug】→【Installed Debuggers】→【XDebug】,再點按【Configure】視窗。


進去後,在【Debug Port】欄位中輸入要設定的port,這裡設定為9168


改好 Eclipse的設定後,其實事情只完成一半,接著要前往php.ini去同步調整 Xdebug的設定,找到xdebug.remote_port這行設定,把它改成和 Eclipse上列設定一樣的值:


完成後,請記得重開Apache Server,就能正常地在Eclipse中啟動Xdebug來除錯了。

參考資料

2015年3月24日 星期二

【筆記】MySQL storage engine

MyISAM Engine

  1. MySQL 5.1版本以前的預設引擎
  2. 不支援交易(transaction)或row-level lock
  3. 非remotely crash-safe,此乃最大的弱點
  4. 在儲存資料表的方式是將其儲存為一個data file(.MYD)和一個index file(.MYI)
  5. MyISAM工作時鎖定(lock)整個資料表,而非單一列資料;但仍可以在有select執行時,做寫入的動作
  6. MyISAM的資料表中,可以對BLOBTEXT欄位的前500個字元建立indexMyISAM支援full-text indexes

2015年3月12日 星期四

2015年3月4日 星期三

【分享】解決在Mac使用HP LaserJet P1102w無法雙面列印的問題

在Mac使用HP LaserJet P1102w無法雙面列印的問題困擾我很久了,試了很多次重新安裝都無法解決,後來也就用Windows系統的電腦來列印,今天又找了一下,在官方討論區上發現一個蠢方法。

1. 先第一面的印完。

2. 把印完的所以紙張放回送紙匣。

3. 把印表機上方的蓋子打開,如下圖手指之處。



4. 再把蓋子蓋回去後,印表機就會自動開始列印第二面。

2015年2月25日 星期三

【筆記】解決Hadoop在主機重開機後,namenode無法啟動的問題

在使用pseudo-distributed mode設定好Hadoop以後,執行hadoop namenode -format,再執行start-dfs.sh後,即可正常啟動Namenode等,順利把檔案放到Hadoop上去,或是瀏覽Hadoop上的檔案。

但有時只要每次重開機後,再執行start-dfs.sh啟動Hadoop服務,下jps去檢查時,會看不到Namenode,這時可能的原因是沒在core-site.xml中設定hadoop.tmp.dir

在沒有設定hadoop.tmp.dir這個參數的情況下,Hadoop的暫存資料夾預設為/tmp/hadoop-你的使用名稱,如:


而這個資料夾在每次重開機後,即會被清除,因此會造成Namenode在重開機無法正常啟動的情況,解決方式就是在core-site.xml中設定hadoop.tmp.dir來指定Hadoop的暫存資料夾:

        hadoop.tmp.dir
 /home/javakid/study/hadoop/temp_data

設定完成後,再執行hadoop namenode -format,下次重新啟動後,再啟動DFS後,Namenode就會正常啟動。

2015年1月31日 星期六

【筆記】使用Node.js讀取MySQL資料

今天想到嘗試使用Node.js來讀取MySQL的資料,上網找了一下教學,發現其實使用模組來實現的話,特別簡單。

首先在MySQL中建立一個test的資料表,再來新增兩筆資料如下:
+---+------+
| a | b    |
+---+------+
| 1 | abc  |
| 2 | xyz  |
+---+------+
首先使用npm指令來安裝mysql模組:
npm install mysql
然後新增一個新增JavaScript檔案,其名為mysql_test.js
var mysql = require('mysql');
var connection = mysql.createConnection({
        host : 'localhost',
        user : 'cbuser',
        password : 'cbpass',
        database : 'cookbook'
});
connection.query('select a, b from test', 
        function(error, rows, fields){
                if(error)
                {
                        throw error;
                }
                for(key in rows)
                {
                        console.log(rows[key].a + ',' 
                                        + rows[key].b);
                }
        });
再來使用node指令執行上列的mysql_test.js
node mysql_test.js
結果如下:
1,abc
2,xyz
參考資料

2015年1月29日 星期四

【雜志】為人父的驕傲


在排隊買水煎包時,看到劉備的兒子放寒假了,走進店裡幫忙。

店裡的阿姨問:「有沒有跟小妹妹聊天?」

兒子:「不知道要講什麼!」

阿姨:「她一直來找你耶!」

講完,劉備兒子一臉靦腆,兩個腮幫子微紅了起來,「小姐,要幾個?」,立刻轉身幫忙夾水煎包。

我看到劉煎一邊忙著把熱呼呼的水煎包起鍋,一邊強忍著嘴角,避免過度上揚,臉上滿滿一拿掐不好就會內傷的喜悅。

那是一種身為父親才知道的驕傲。

2015年1月14日 星期三

【筆記】常用IT或其他領域相關英文名詞意義

有些技術名詞,
即使使用最強大的詞典去找,
也不一定找得到,
所以在這裡整理了很多IT相關的名詞解釋,
對我來說,
防變笨時候用的,XD


C
cardinality在一個集合 (如 list,set ) 中所有元素的數量
checksum總和檢查碼傳輸單位元中的位計數及其單位,接收器可以根據此值檢查是否收到相同的位數。如果計數符合,即認為已接收整個傳輸。又稱為雜湊更多
compromise弱化
F
failover容錯移轉指叢集中的某個節點無法使用時將叢集資源移轉至可用節點。 服務從叢集中的主動節點移到被動節點的程序。更多
L
latency延遲對於檔案系統,這通常是指特定檔案系統作業返回到使用者所花費的時間長度。更多
P
polling輪詢一個主單位輪流去詢問各個從屬單元的狀態。更多
P
RPC輪詢Remote Procedure Call,遠端程序呼叫。更多
T
throughput傳輸量對於檔案系統,這通常指的是給定時間單位內的 I/O 作業數。更多

2015年1月11日 星期日

【筆記】使用SSH連線到VirtualBox的Ubuntu VM

若要在實體機器使用如PieTTY等連線軟體使用SSH通訊協定連線到VirtualBox中的Ubuntu時,需要先在VirtualBox中設定「連接埠轉送」:


首先,查出VirtualBox虛擬網卡的資訊,在命令提示字元下輸入:
ipconfig
接著找到 「乙太網路卡 VirtualBox Host-Only Network」這一塊的資訊中的「IPv4 位址」,如下:


若找不到「乙太網路卡 VirtualBox Host-Only Network」這一塊的資訊,則先到Oracle VM VirtualBox管理員視窗中,依下列步驟進行設定。

先點選【檔案】→【喜好設定】:


進入「VirtualBox - 設定值」視窗後,點選【網路】→【「僅限主機」網路】,然後在最右的加號圖示上點按一下:


加入成功後,就會在下方的區塊內出現「VirtualBox Host-Only Ethernet Adapter」,回到命令提示字元再輸入一次ipconfig,應該就可以找到「乙太網路卡 VirtualBox Host-Only Network」了。

在這裡「IPv4 位址」的設定值為「192.168.123.1」。

再來啟動Ubuntu後,使用下列指令來查網路設定:
ifconfig
再來找到eth0這段的設定,這裡是「10.0.2.15」:


回到Oracle VM VirtualBox管理員視窗中,選擇要設定的虛擬機器後,點按上面的【設定值】:


進入設定值視窗後,在左邊區塊內找到【網路】後,把右邊區塊中的【進階】展開,再點按【連接埠轉送】開啟「連接埠轉送規則」設定視窗後,再點按右邊的加號圖示新增一組規則:


在上面畫面的各欄位設定如下:
  • 「名稱」用輸入自訂的名稱,這裡用的是「SSH」
  • 「協定」就使用預設的「TCP」
  • 「主機IP」輸入在「乙太網路卡 VirtualBox Host-Only Network」這一塊的資訊中的「IPv4 位址」,亦即「192.168.123.1」
  • 「主機連接埠」輸入「22」
  • 「客體IP」設定為eth0這段的設定,這裡是「10.0.2.15」
  • 「客體連接埠」設為「22」
設定好點按【確定】鈕離開即可:


若設定完仍無法連線,可能尚未安裝SSH Server,可以在Ubuntu使用下列指令安裝OpenSSH Server
sudo apt-get install openssh-server

2015年1月7日 星期三

【筆記】Java取得檔案的建立時間、修改日期,以及存取日期

在使用Java處理檔案時,有些時候會需要去取得檔案的一些如建立時間、修改日期或存取日期等資訊,如:


1.6以前,可以使用File類別來取得最近的修改日期:
String strPath = "D:/_javakid/test.txt";
		
File file = new File(strPath);
		
Calendar c = Calendar.getInstance();
c.setTimeInMillis(file.lastModified());
	
System.out.println(strPath + " 上次修改時間為:" 
			+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
但使用File類別只能得到檔案的最近修改時間,若要取得像上列的檔案內容中的修改日期以及存取日期的話,就要用到NIOjava.nio.file套件下的FilesPathPaths這三個類別:
System.out.println("使用nio...");
Path path = Paths.get(strPath);
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);

c.setTimeInMillis(attrs.creationTime().toMillis());
System.out.println(strPath + " 建立時間為:" 
		+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));

c.setTimeInMillis(attrs.lastModifiedTime().toMillis());
System.out.println(strPath + " 上次修改時間為:" 
		+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));

c.setTimeInMillis(attrs.lastAccessTime().toMillis());
System.out.println(strPath + " 上次存取時間為:" 
		+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
使用上要注意的是,上列三個類別都是在JDK 1.7之後才存在的,要使用這些類別,要先把電腦的JDK升級到1.7以上。

完整程式碼如下:
package idv.jk.io;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class FileModificationReader 
{
	public static void main(String[] args) throws IOException
	{
		String strPath = "D:/_javakid/test.txt";
		
		File file = new File(strPath);
		
		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(file.lastModified());
	
		System.out.println(strPath + " 上次修改時間為:" 
							+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
		
		System.out.println("使用nio...");
		Path path = Paths.get(strPath);
		BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
		
		c.setTimeInMillis(attrs.creationTime().toMillis());
		System.out.println(strPath + " 建立時間為:" 
				+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
		
		c.setTimeInMillis(attrs.lastModifiedTime().toMillis());
		System.out.println(strPath + " 上次修改時間為:" 
				+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
		
		c.setTimeInMillis(attrs.lastAccessTime().toMillis());
		System.out.println(strPath + " 上次存取時間為:" 
				+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
		
	}
}
執行結果為:
D:/_javakid/test.txt 上次修改時間為:2015-01-07 18:27:39
使用nio...
D:/_javakid/test.txt 建立時間為:2015-01-05 15:12:59
D:/_javakid/test.txt 上次修改時間為:2015-01-07 18:27:39
D:/_javakid/test.txt 上次存取時間為:2015-01-07 18:27:12