2016年12月5日 星期一

Linux vi 將內容從一個檔複製到另一個檔

假設要將 testA 檔案部分內容,複製到 testB 檔
$ vi testA
先在 testA 檔內複製需要的部分,
例如,用「yy」指令複製目前這一行(此時複製的資料會存在暫存區)
在不退出 vi 的情況下,直接去編輯 testB 檔,
如此在暫存區的複製資料才會留著。
:vi testB

:e testB

此動作會關閉 testA,然後開啟 testB,
所以假設 testA 檔有修改到,但沒儲存,會過不去 testB
若要放棄testA修改,直接編輯testB,指令可改成
:vi! testB

:e! testB

到 testB 檔後,即可直接用「p」指令貼上

若 testA 檔,有好幾個地方,要分別複製到 testB 檔
則在 testA 檔複製時,可指定要放的暫存區(a~z)
先按「"」「a」,表示要使用 a 暫存區
再按「yy」指令複製,則會將資料放在 a 暫存區
同理,
先按「"」「b」,表示要使用 b 暫存區
再按「yy」指令複製,則會將資料放在 b 暫存區
如此便可將不同部分,放在不同暫存區,一次帶到 testB 檔,
然後在 testB 檔時,切換目前使用的暫存區,再使用「p」指令貼上。


[相關:選取特定區域後複製、貼上]
先按「v」後,移動游標,即可反白選取所要的區域字段。
再來可選擇按「d」,剪下選取區域的內容,
或是按「y」,複製選取區域的內容。
最後再用「p」貼上。


參考:
Vi- 編輯與修改
vim - Copy and paste content from one file to another file in VI - Stack Overflow
Checko's Blog: VI 學習 : copy and paste


2016年11月24日 星期四

PHP new self 、new static 比較

class A {
    public static function get_self() {
        return new self();
    }

    public static function get_static() {
        return new static();
    }
}

class B extends A {
    public function testB(){
        return 'testB';
    }
}

echo get_class(A::get_self()); // A
echo get_class(A::get_static()); // A
echo get_class(B::get_self());  // A
echo get_class(B::get_static()); // B

#echo B::get_self()->testB();  // Fatal error:  Uncaught Error: Call to undefined method A::testB()
echo B::get_static()->testB(); // testB
new self() => 生成的物件為實際寫有這句 code 的 class
new static() =>生成的物件為呼叫使用這句 code 的 class


參考:
php - New self vs. new static - Stack Overflow
class - What does new self(); mean in PHP? - Stack Overflow


MySQL InnoDB AUTO_INCREMENT 自動縮小?

MySQL InnoDB 的 auto-increment 計數器數值只儲存在記憶體中,所以重新啟動 MySQL 後,原本的 auto-increment 值就消失了。在需要取得 auto-increment 時,才重新取目前最大值來初始化 auto-increment
SELECT MAX(ai_col) FROM table_name FOR UPDATE;

因為這個特性,
所以若原本 InnoDB 資料表有1~5筆資料,auto-increment 為 6,
刪除了第4筆、第5筆,資料只剩 1~3 筆,
重新啟動 MySQL 後,auto-increment 會變成 3
(註:若為MyISAM,重啟後仍為 6)


其他:
取得 AUTO_INCREMENT 值
//方法1
SHOW TABLE STATUS LIKE "資料表";
//方法2
SELECT AUTO_INCREMENT FROM information_schema.tables WHERE table_name = "資料表" AND table_schema = "資料庫";
//方法3 DATABASE()目前資料庫
SELECT AUTO_INCREMENT FROM information_schema.tables WHERE table_name = "資料表" AND table_schema = DATABASE();



參考:
MySQL :: MySQL 5.6 Reference Manual :: 14.8.6 AUTO_INCREMENT Handling in InnoDB
sql - How to get the next auto-increment id in mysql - Stack Overflow


2016年10月26日 星期三

PHP foreach 使用 reference 修改值

PHP array 使用 foreach 時,可以直接將代表值的變數($v),指定為 reference(&$v)
如此,可方便直接對值修改:
foreach ($arr as &$v){
    $v = $v*10;
}
.....

但這樣使用時須注意,最後須記得將 reference 變數 unset,
避免 reference 變數仍指到陣列的元素,不注意時,可能造成非預期的修改。
$arr = array("A", "B", "C");
foreach ($arr as &$v){
}
var_dump($arr);
foreach ($arr as $key => $v) {
    echo "{$key} => {$v}\n";
    var_dump($arr);
}
輸出結果,可發現第2次的freach,最後一個元素一直改變,
因為 reference 變數 $v 還一直代表著陣列最後一個元素,所以一直被重新賦值。
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  &string(1) "C"
}

0 => A
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  &string(1) "A"
}
1 => B
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  &string(1) "B"
}
2 => B
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  &string(1) "B"
}



使用完馬上unset(&$v),可避免非預期的最後一個元素值一直改變
$arr = array("A", "B", "C");
foreach ($arr as &$v){
}
unset($v);
var_dump($arr);
foreach ($arr as $key => $v) {
    echo "{$key} => {$v}\n";
    var_dump($arr);
}
輸出結果
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}

0 => A
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}
1 => B
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}
2 => C
array(3) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
}




參考:
PHP: foreach - Manual

PHP、Java 的 Des 加密(ECB mode)

PHP 和 Java 間用 Des 加密(ECB mode) 來傳輸資料,要確保兩邊用同樣的key加解密的結果一樣。

PHP code:
/**
 * DES 加解密(ECB mode)
 */
class DesCrypt {

    private $key;

    /**
     * DES 加解密(ECB mode)
     * @param string $key 加密的key
     */
    public function __construct($key) {
        $this->key = $key;
    }

    /**
     * 加密(ECB mode)
     * @param string $str 要加密的資料
     * @return string
     * @throws \Exception
     */
    public function encrypt($str) {
        //獲得加密算法的分組大小
        $size = mcrypt_get_block_size('des', 'ecb');
        //填充不足字節數,傳入數據長度必須是 n * 分組大小
        $str = $this->pkcs5Pad($str, $size);
        $key = $this->key;
        //打開算法和模組對應的模塊
        $td = mcrypt_module_open('des', '', 'ecb', '');
        $key_max_len = mcrypt_enc_get_key_size($td);
        if (strlen($key) > $key_max_len) {
            throw new \Exception("key超過最大長度:{$key_max_len}");
        }
        //建立初始向量
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        //初始化加密所需的緩衝區
        //在 ECB 模式下,初始向量會被忽略, 在 CFB,CBC,STREAM,nOFB 和 OFB 模式下,必須提供初始向量
        mcrypt_generic_init($td, $key, $iv);
        //加密數據
        $data = mcrypt_generic($td, $str);
        //清理指定加密模組緩衝區
        mcrypt_generic_deinit($td);
        //關閉加密模組
        mcrypt_module_close($td);
        //將加密結果用base64編碼
        $data = base64_encode($data);
        return $data;
    }

    /**
     * 解密(ECB mode)
     * @param string $str 要解密的密文
     * @return string
     * @throws \Exception
     */
    public function decrypt($str) {
        //將base64編碼結果解開
        $str = base64_decode($str);
        $key = $this->key;
        //獲得加密算法的分組大小
        $td = mcrypt_module_open('des', '', 'ecb', '');
        $key_max_len = mcrypt_enc_get_key_size($td);
        if (strlen($key) > $key_max_len) {
            throw new \Exception("key超過最大長度:{$key_max_len}");
        }
        //建立初始向量
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        //$ks = mcrypt_enc_get_key_size($td);
        //初始化加密所需的緩衝區
        mcrypt_generic_init($td, $key, $iv);
        //解密數據
        $decrypted = mdecrypt_generic($td, $str);
        //清理指定加密模組緩衝區
        mcrypt_generic_deinit($td);
        //關閉加密模組
        mcrypt_module_close($td);

        //去除 pkcs5Pad 的填充值
        $result = $this->pkcs5Unpad($decrypted);
        return $result;
    }

    /**
     * 填充不足字節數
     * 1.將填充量取chr當填充值
     * 2.剛好滿也再填充一組
     * @param string $text
     * @param int $blocksize
     * @return string
     */
    private function pkcs5Pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    /**
     * 去除 pkcs5Pad 的填充值
     * @param string $text
     * @return boolean
     * @return string
     */
    private function pkcs5Unpad($text) {
        $pad = ord($text{strlen($text) - 1});
        if ($pad > strlen($text)) {
            return false;
        }
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
            return false;
        }
        return substr($text, 0, -1 * $pad);
    }

}
$Des = new DesCrypt("12345678");
$encrypt_res = $Des->encrypt("test data"); //l9TN9Ln/71IvFRjlhD8PaQ==
$Des->decrypt($encrypt_res); //test data
註:PHP要確定有裝 php-mcrypt
$ yum install php-mcrypt



Java Code:
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.*;

/**
 * DES算法
 *
 * @author user
 */
public class DES {

    /**
     * 加密函數
     *
     * @param data 加密數據
     * @param key 密鑰
     * @return 返回加密後的數據
     */
    public static byte[] encrypt(byte[] data, byte[] key) {

        try {

            // DES算法要求有一個可信任的隨機數源
            SecureRandom sr = new SecureRandom();

            // 從原始密鑰數據創建DESKeySpec對象
            DESKeySpec dks = new DESKeySpec(key);

            // 創建一個密匙工廠,然後用它把DESKeySpec轉換成
            // 一個SecretKey對象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(dks);

            // using DES in ECB mode
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");

            // 用密匙初始化Cipher對象
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, sr);

            // 執行加密操作
            byte encryptedData[] = cipher.doFinal(data);

            return encryptedData;
        } catch (Exception e) {
            System.err.println("DES算法,加密數據出錯!");
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 解密函數
     *
     * @param data 解密數據
     * @param key 密鑰
     * @return 返回解密後的數據
     */
    public static byte[] decrypt(byte[] data, byte[] key) {
        try {
            // DES算法要求有一個可信任的隨機數源
            SecureRandom sr = new SecureRandom();

            // byte rawKeyData[] = /* 用某種方法獲取原始密匙數據 */;
            // 從原始密匙數據創建一個DESKeySpec對象
            DESKeySpec dks = new DESKeySpec(key);

            // 創建一個密匙工廠,然後用它把DESKeySpec對象轉換成
            // 一個SecretKey對象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(dks);

            // using DES in ECB mode
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");

            // 用密匙初始化Cipher對象
            cipher.init(Cipher.DECRYPT_MODE, secretKey, sr);

            // 正式執行解密操作
            byte decryptedData[] = cipher.doFinal(data);

            return decryptedData;
        } catch (Exception e) {
            System.err.println("DES算法,解密出錯。");
            e.printStackTrace();
        }

        return null;
    }
}
import javax.xml.bind.*;

public class desTest {

public static void main(String[] args) {
    String key = "12345678"; //設定加密 key
    String plain = "test data";//加密內容明文

    byte[] key_byte = key.getBytes();
    byte[] plain_byte = plain.getBytes();
    //ECB mode 加密
    byte[] data_ecb = DES.encrypt(plain_byte, key_byte);
    //加密結果經BASE64編碼
    String base64String = DatatypeConverter.printBase64Binary(data_ecb);
    System.out.println("密文經過BASE64編碼:" + base64String);

    try {
        byte[] b = DatatypeConverter.parseBase64Binary(base64String);
        System.out.println("解密後明文:" + new String(DES.decrypt(b, key_byte)));
    } catch (Exception ex) {
        System.out.println(ex.getMessage());
        System.out.println(ex.toString());
    }
}

}
//輸出
密文經過BASE64編碼:l9TN9Ln/71IvFRjlhD8PaQ==
解密後明文:test data



參考、節錄:
在JAVA中使用DES算法 - 张兴业 - 博客园
java - how to avoid warning for the Base 64? - Stack Overflow
[ Java 文章收集 ] Java如何進行Base64的編碼(Encode)與解碼(Decode)?
DES加密模式详解(zz) - Wayne - BlogJava
java php DES 加密解密 - bask之家 -- 思则远 - ITeye技术网站
php des 加密解密实例 - clh604的专栏 - 博客频道 - CSDN.NET (AES加密範例)
使用php mcrypt加密解密
Encryption operating modes: ECB vs CBC
PHP通用DES加密算法
javaDESEncrypt.rar DESEncrypt.java