2020年11月11日 星期三

重新踏入網頁開發 (5) - Blocking & Non-Blocking

Blocking & Non-Blocking

    Nodejs 為事件觸發的單一執行緒,不像 Apache + PHP 會自動開一個 Thread 去接每一個 Request。
    Blocking Code 示範 (程式碼另有 index.js 請參照重新踏入網頁開發 (4) - Dependency injection)
  • route.js
        function start_requset_handle () {
            console.log("Start request.");
            
            // Sleep for 10 seconds
            function sleep(milliSeconds) {
                var startTime = new Date().getTime();
                while (new Date().getTime() < startTime + milliSeconds);
            }
            sleep(10000);
        }
    
        function upload_requset_handle () {
            console.log("Upload request.");
        }
    
        function requset_handler (url) {
            var handle = {};
            handle['/'] = start_requset_handle;
            handle['/start'] = start_requset_handle;
            handle['/upload'] = upload_requset_handle;
    
            if (typeof handle[url] === 'function') {
                handle[url]();
            } else {
                console.log("No request handler found for " + url);
            }
        }
    
        export function route(url) {
            requset_handler(url);
        }
  • server.js
        import http from 'http'
        import url from 'url'
    
        export function serverStart(route) {
            function onRequest(request, response) {
                var path_name = url.parse(request.url).pathname;
                console.log("Request for " + path_name + " received");
                
                // Routing function
                route(path_name);
    
                response.writeHead(200, {"Content-Type": "text/plain"});
                response.write("Hello World.\n");
                response.end();
            }
            http.createServer(onRequest).listen(8888);
            console.log("Server has started...");
        }
    這裡先用 requset_handler() 這 function 去應對 2 種 url 的 request。client 端 request http://localhost:8888/start 會等 10 秒才回覆 Hello World.,request http://localhost:8888/upload 則立即回覆。因為 nodejs 為單一執行緒,如果有人先 request start, 後面即使是 request upload 也要跟著等 10 秒。
    Non-Blocking Code 示範 (程式碼另有 index.js 請參照重新踏入網頁開發 (4) - Dependency injection)
  • route.js
        import { exec } from 'child_process';
    
        function start_requset_handle (response) {
            console.log("Start request.");
            var content = "empty\n";
            exec("ls -alh", function (error, stdout, stderr) {
                content = stdout;
                response.writeHead(200, {"Content-Type": "text/plain"});
                response.write(content);
                response.end();
            });
            // 放在外面會讓 server 直接回 empty
            // response.writeHead(200, {"Content-Type": "text/plain"});
            // response.write(content);
            // response.end();
        }
    
        function upload_requset_handle (response) {
            console.log("Upload request.");
            response.writeHead(200, {"Content-Type": "text/plain"});
            response.write("Uploading...\n");
            response.end();
        }
    
        function requset_handler (url, response) {
            var handle = {};
            handle['/'] = start_requset_handle;
            handle['/start'] = start_requset_handle;
            handle['/upload'] = upload_requset_handle;
    
            if (typeof handle[url] === 'function') {
                handle[url](response);
            } else {
                console.log("No request handler found for " + url);
            }
        }
    
        export function route(url, response) {
            requset_handler(url, response);
        }
  • server.js
        import http from 'http'
        import url from 'url'
    
        export function serverStart(route) {
            function onRequest(request, response) {
                var path_name = url.parse(request.url).pathname;
                console.log("Request for " + path_name + " received");
                
                // Routing function
                route(path_name, response);
            }
            http.createServer(onRequest).listen(8888);
            console.log("Server has started...");
        }
    import nodejs 內建的 child_process,解決了問題。傳 response 進去是為了讓 request 處理完再做回覆。
上一篇 :
下一篇 :
參考資料 :

2020年10月30日 星期五

重新踏入網頁開發 (4) - Dependency injection

耦合性 (Coupling,dependency)

    耦合性是指一程式中,模組及模組之間資訊或參數依賴的程度。低耦合性是結構良好程式的特性,低耦合性程式的可讀性及可維護性會比較好。舉例來說 Module A 使用了 Module B 功能, 今天 Module B 更改了功能而造成 Module A 必須也更改使用其功能的邏輯, 則會說 Module A 對 Module B 依賴程度高, 具有高耦合性。所以你要維護 B 就要一起維護 A, 邏輯參在一起想必 module 的可重複使用率也低。

解耦 > Dependency injection

    高耦合 > 解耦 > 低耦合。解耦方法很多這裡使用的是 Dependency injection (依賴注入>///<)。拿前幾些章節裡的 Server 跟 Route 舉例。
    沒有依賴注入的 Server + Route
  • route.js
        export function regular_route(pathname) {
            console.log("Start routing path name \"" + pathname + "\"");
        }
  • Server.js
        import http from 'http'
        import url from 'url'
        import { regular_route } from './route.js';
    
        export function serverStart() {
            function onRequest(request, response) {
                var path_name = url.parse(request.url).pathname;
                console.log("Request for " + path_name + " received");
                
                // my Route function
                regular_route(path_name);
    
                response.writeHead(200, {"Content-Type": "text/plain"});
                response.write("Hello World.\n");
                response.end();
            }
            http.createServer(onRequest).listen(8888);
            console.log("Server has started...");
        }
  • index.js
        import {serverStart} from './Server.js'
        serverStart();
    老實說看起來像最佳解, 但當你要換另一種 routing function, 你正常來說要更改 2 個地方。import 的部份可能要改, 因為是新的檔案或其他大 module 裡的 route function。function name 可能要改, 因為新的 routing function 不叫 regular_route。驗證了上面所說的你改 B 結果 A 也要跟著改,量子糾纏...阿不對是"高耦合"。才 2 個看起來還好,但隨著專案擴的越大,你改 routing function 的時間成本也越大,甚至改天不用 route 用其它東西去處理 URL,那就更慘了。
    依賴注入的 Server + Route
    這邊切入的點是 Server 這 module 主要不變工作應該只有接收與回覆,中間的 routing function 是會依照時空背景不同去更動(沒錯,就是這麼彈性且有價值,誰說整天看政治新聞沒用的),可以被抽象化成一個動作,而這個動作可以當作參數傳進 Server 這 module。(說穿了就是做 callback function)
  • route.js (沒變)
        export function regular_route(pathname) {
            console.log("Start routing path name \"" + pathname + "\"");
        }
  • Server.js
        import http from 'http'
        import url from 'url'
    
        export function serverStart(route) {
            function onRequest(request, response) {
                var path_name = url.parse(request.url).pathname;
                console.log("Request for " + path_name + " received");
                
                // my Route function
                route(path_name);
    
                response.writeHead(200, {"Content-Type": "text/plain"});
                response.write("Hello World.\n");
                response.end();
            }
            http.createServer(onRequest).listen(8888);
            console.log("Server has started...");
        }
  • index.js
        import { serverStart } from './Server.js'
        import { regular_route } from './route.js';
        serverStart(regular_route);
    這樣你改 routing function 就不會需要動到 Server.js (改成動index.js...),甚至改傳吃豬肉這 function,Server 也照執行沒什麼問題。
上一篇 :
下一篇 :
參考資料 :

2020年10月29日 星期四

重新踏入網頁開發 (3) - Route

Route ( URL )

    通過不同的 URL 去區別不同請求
  • Server.js
        import http from 'http'
        import url from 'url'
    
        function onRequest(request, response) {
            // 會紀錄 request url
            var path_name = url.parse(request.url).pathname;
            console.log("Request for " + path_name + " received");
            response.writeHead(200, {"Content-Type": "text/plain"});
            response.write("Hello World.\n");
            response.end();
        }
    
        http.createServer(onRequest).listen(8888);
        console.log("Server has started...");
  • 用 curl 去 request
        $ curl http://localhost:8888/
        Hello World.
        $ curl http://localhost:8888/students/1
        Hello World.
  • 伺服器端顯示
        $ node Server.js 
        Server has started...
        Request for / received
        Request for /students/1 received
上一篇 :
下一篇 :
參考資料 :

2020年10月28日 星期三

Node.js - 支援 ES6 module

Nodejs 支援 ES6 module

    要讓 nodejs 去支援 ES6 module,必須去改寫 package.json。
    開啟一個新的 nodejs project
  • 首先是 node package manager
        npm init
        # 應該會有一堆要填的,若沒任何想法可以改下 npm init -y 自動幫你填完
  • 這時候多一個 package.json 的檔案,裡面有你剛填的資料
        {
          "name": "react",
          "version": "1.0.0",
          "description": "",
          "main": "index.js",
          "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1"
          },
          "repository": {
            "type": "git",
            "url": "git+https://github.com/JunYe1993/React.git"
          },
          "author": "JunYe1993",
          "license": "ISC",
          "bugs": {
            "url": "https://github.com/JunYe1993/React/issues"
          },
          "homepage": "https://github.com/JunYe1993/React#readme"
        }
  • package.json 裡多加一個設定
        ...
          "type": "module"
        }
    若 node 版本低於 v13, 會噴 Warning。
  • node 版本低於 v13
        $ node HelloWorld.js 
        (node:29009) ExperimentalWarning: The ESM module loader is 
        experimental.
        Hello World
參考資料 :

2020年10月27日 星期二

重新踏入網頁開發 (2) - ES6

模組化

    Ryan Dahl 創造的 node.js 專案之所以如此重要, 是因為其理念為 模組(module), 讓 javascript 的大規模專案得以實現, 也讓 javascript 可以做伺服器端程式設計。

node.js 的模組

    這裡示範 node.js 原生的模組,
  • moduleA.js
        // 想像這裡有隱藏程式碼
        // # var module = new Module(...);
        // # var exports = module.exports;
        // index.js 在 call "require("./ModuleA");"
        // 等於回傳 module.exports
        var name = "JunYe";
        exports.name = name;
  • moduleB.js
        // 想像這裡有隱藏程式碼
        // # var module = new Module(...);
        // # var exports = module.exports;
        // index.js 在 call "require("./ModuleB");"
        // 等於回傳 module.exports
        var name = "Daniel";
        exports.name = name;
  • index.js
        var a = require("./moduleA");
        var b = require("./moduleB");
        console.log(a.name + " and " + b.name + " both are the best.");
  • 執行 node index.js
        $>:node index.js 
        JunYe and Daniel both are the best.

ES6 的模組

    ES6 是較新的 javascript 規範, 沒錯就是規範, javascript 並非正規的程式語言, 而是一種語言規範, 而其 runtime 的執行則讓各個瀏覽器引擎決定(nodejs也是其中之一)。雖然 ES6 是較新的規範, 但大部分的瀏覽器都有支援了。以下為 nodejs 支援 ES6 的示範
  • studentA.js
        export var _name = "JunYe";
        export var _class = "SSS";
        export var _number = "3064";
  • function.js
        export function intro (name, number) {
            console.log("My name is " + name + ", and my number is " + number);
        };
  • index.js
        // 要在 package.json 加 type: module
        // 才能達到 <script src="./function.js" type="module"></script> 的效果
        // 這邊不知道為什麼要加 .js 才給過
        import { intro } from "./function.js";
        import { _name, _number } from "./studentA.js";
        intro(_name, _number);
  • 執行 node index.js
        $>:node index.js 
        My name is JunYe, and my number is 3064
上一篇 :
下一篇 :
參考資料 :

2020年10月10日 星期六

重新踏入網頁開發 (1) - Nodejs

前言

    網頁開發是一個對我而言還蠻陌生的東西, 因為我都是自學所以總是得過且過。自學通常就會拿手邊資源也就是公司內部系統的 Code。公司的 Code 較為古早, 不外乎 3 個基本的元素, http server(apache), PHP, 前端(html, css, javascript)。 此時的我靠著 w3schools 就能完成所有事情。既然重新踏入開發網頁, 就去學最新的東西 (目標是 react), 即使路途遙遠, 但相對的有很多東西可以紀錄。

Node.js

    離開了較為古老的開發方法後, 進入到所謂較為現代網站開發, 查了查似乎都繞不開 node.js。所以我想在這個下點功夫, 學學這個可以在後端執行的 javascript。

    學程式學語言的第一步通常是 Hello World,所以那就來吧。(當然要先裝 Node.js)
  • 創一個檔案 HelloWorld.js
        console.log("Hello World");
  • 在 CMD 執行 HelloWorld.js
        $>node HelloWorld.js
        Hello World
    再一個貨真價實的後端 Hello World
  • 創一個檔案 Server.js
        var http = require("http");
    
        http.createServer(function (request, response) {
            response.writeHead(200, { "Content-Type": "text/plain" });
            response.write("Hello World");
            response.end();
        }).listen(8888);
  • 在 CMD 執行 Server.js (如果你是 windows 請按允許存取)
        $>node Server.js
    
    
  • 開啟瀏覽器, 讀取 http://localhost:8888/ (chrome 書籤太多...科)

結語

    老實說上面的程式碼對我來說還蠻震驚的, 因為它取代了 apache (http server), 一個我從來懶得去研究的東西。看來路途比想像的遙遠
下一篇 :
參考資料 :

2020年9月3日 星期四

Linux - Shell Scripts - ln 指令

ln 指令

    ln 指令是用來建立連結檔, 這裡用安裝 nodejs 示範。安裝法為在官網下載編譯好的 nodejs, 然後用 ln 建立連結檔。
    # 從 nodejs.org 下載並解壓縮至 /home/user/software
    $ ln -s /home/daniel/software/node-v12.18.3-linux-x64/bin/node /usr/local/bin/
    $ ln -s /home/daniel/software/node-v12.18.3-linux-x64/bin/npm  /usr/local/bin/
    # 之後利用 node -v 跟 npm -v 來確定安裝是否完成...
    
    daniel@daniel-pc:~/daniel/node-v12.18.3-linux-x64$ ls -al /usr/local/bin/
    總計 8
    drwxr-xr-x  2 root root 4096  9月  3 15:46 .
    drwxr-xr-x 10 root root 4096  2月  4  2020 ..
    lrwxrwxrwx  1 root root   52  9月  3 15:02 node -> /home/daniel/daniel/node-v12.18.3-linux-x64/bin/node
    lrwxrwxrwx  1 root root   51  9月  3 15:46 npm -> /home/daniel/daniel/node-v12.18.3-linux-x64/bin/npm

軟連結(soft/symbolic link) 跟 硬連結(hard link)

    上面範例我用的是軟連結, 指令有加 -s, 硬連結則是不加。下面是兩種連結的比較。
    連結 : 與連結的檔案相同的 inode, 相對路徑不受連結之檔案影響
    連結 : 與連結的檔案不同的 inode, 相對路徑連結之檔案影響
    如果用相對路徑去軟連結, 結果就是 2 個連結檔壞掉, 因為 inode 不同, 軟連結找不到 ./bin/node 跟 ./bin/npm。
    $ sudo ln -s ./bin/node /usr/local/bin/
    $ sudo ln -s ./bin/npm  /usr/local/bin/
    $ ls -al /usr/local/bin/
    總計 8
    drwxr-xr-x  2 root root 4096  9月  3 18:16 .
    drwxr-xr-x 10 root root 4096  2月  4  2020 ..
    lrwxrwxrwx  1 root root   10  9月  3 18:16 node -> ./bin/node
    lrwxrwxrwx  1 root root    9  9月  3 18:16 npm -> ./bin/npm
    如果用相對路徑去硬連結, 因為 inode 相同, 硬連結找得到 ./bin/node。
    $ sudo ln ./bin/node /usr/local/bin/
    $ sudo ln ./bin/npm  /usr/local/bin/
    $ ls -al /usr/local/bin/
    總計 47520
    drwxr-xr-x  2 root   root       4096  9月  3 18:24 .
    drwxr-xr-x 10 root   root       4096  2月  4  2020 ..
    -rwxr-xr-x  2 daniel daniel 48646656  7月 22 23:00 node
    lrwxrwxrwx  2 daniel daniel       38  9月  3 13:34 npm -> ../lib/node_modules/npm/bin/npm-cli.js
    但上面的 npm 是壞的, 因為原本的 npm 也是連結檔, 而且連的是相對位置。雖然透過 inode 找到該連結檔, 但因為是在 /usr/local/bin 下找相對位置所以找不到。所以就如同文章最一開始的安裝示範, 軟連結在相對路徑的處理上會受所連結檔案影響, 反而能成功找到 ../lib/node_modules/npm/bin/npm-cli.js。
參考資料 :

Makefile 筆記 (2)

Makefile 範本 (OS: Linux)

    這個 Makefile 有結合 .c 跟 .cpp 檔案, 並且使用一些字串處理 function。
    # gcc compiler 參數
    CC      := gcc
    CFLAGS  := -g -Wall -Werror -std=c99
    # g++ compiler 參數
    CXX     := g++
    CXXFLAG := -Wall -Werror -std=c++17


    # 宣告 source 資料夾
    SRC_DIRS = ./source
    # 把 source 資料夾裡的 cpp 跟 c 全部找出來
    SRCS := $(shell find $(SRC_DIRS) -name "*.cpp" -or -name "*.c")
    # 取檔名並把副檔名加上 .o
    OBJS := $(addsuffix .o,$(notdir $(SRCS))) main.o

    # include 參數 (適用於多個 include 資料夾, 成果會是 gcc -I/dir1 I/dir2)
    INC_DIRS  := ./include 
    INC_FLAGS := $(addprefix -I,$(INC_DIRS))

    all: program

    program: $(OBJS)
        $(CXX) $(CXXFLAG) $(OBJS) -o $@ 

    main.o: main.cpp
        $(CXX) $(INC_FLAGS) $(CXXFLAG) -c $< -o $@

    # .cpp.o 找 .cpp 編譯
    %.cpp.o: $(SRC_DIRS)/%.cpp
        $(CXX) $(INC_FLAGS) $(CXXFLAG) -c $< -o $@

    # .c.o 找 .c 編譯
    %.c.o: $(SRC_DIRS)/%.c
        $(CC)  $(INC_FLAGS) $(CFLAGS) -c $< -o $@

    .PHONY: clean

    clean:
        rm -rf ${OBJS} program
相關文章 :

2020年8月28日 星期五

C 語言 - 正規表示法實作 ( regex.h )

regex.h (Linux 原生, Windows 再說)

    Regex.h 實作主要分 3 階段,regcom, regexec, regfree。
    // 要被批配的 buffer 跟一些參數
    int status, len, i;
    char buf[1024], data[1024];
    getdata(data);

    // 正規表示式的會要先 compile (regcomp())並儲存在 regex_t 此資料結構
    regex_t preg;

    // 設定批配模式的 flag. 
    // REG_EXTENDED ERE (Extended Regular Expression, 不用就是 BRE)
    // REG_ICASE    忽略大小寫
    // REG_NOSUB    不儲存批配後的結果
    // REG_NEWLINE  識別換行符(^$ 這類符號會成每一行的開頭跟結尾), REG_NEWLINE 效力 > eflags
    int cflags = REG_EXTENDED | REG_NEWLINE;

    // 正規表示式的批配樣板
    const char * regex = "Name: ([A-Z a-z]+)\nYear of Birth: ([^\n]*)\n([^:]*: [^\n]*)";

    // pmatch 為 struct 陣列去儲存批配後的結果
    // pmatch.rm_so 批配到的子字串在 target string 的起始 index
    // pmatch.rm_eo 批配到的子字串在 target string 的終止 index
    // nmatch 為宣告 pmatch 的陣列大小
    const size_t nmatch = 10;
    regmatch_t pmatch[nmatch];
    
    // eflags 也會對批配做改動
    // REG_NOTBOL   開頭批配符號(^)永遠批配不到
    // REG_NOTEOL   結尾批配符號($)永遠批配不到
    // REG_STARTEND 是直接使用 pmatch[0] 的 rm_so 跟 rm_eo
    //   作為字串頭尾的 index 然後批配,是為了避免 target string 
    //   中間有終止符(\0) 或 字串太長 strlen() 會 fail.
    int eflags = 0;

    // compile regex 
    if( regcomp(&preg, regex, cflags) != 0 ) {
        puts("regex compile error!\n");
        return;
    }

    // 進行批配 status = 0 代表成功
    status = regexec(&preg, data, nmatch, pmatch, eflags);
    if (status == REG_NOMATCH) {
        printf("No Match\n");
    } else if (status == 0) {
        // pmatch[0] 所指的子字串會是整串
        // pmatch[1], pmatch[2]... 會是括弧裡 sub regex,通常拿來取真正想要的值
        for (i = 0; i < nmatch && pmatch[i].rm_so >= 0; ++i) {
            len = pmatch[i].rm_eo - pmatch[i].rm_so;
            strncpy(buf, data + pmatch[i].rm_so, len);
            buf[len] = '\0';
            printf("match %d :\n%s\n\n", i+1, buf);
        }
    }

    regfree(&preg);
    return;

測試資料 跟 結果

    測試資料
Name: JunYe
Year of Birth: 1993
Gender: Male
    結果
match 1 :
Name: JunYe
Year of Birth: 1993
Gender: Male

match 2 :
JunYe

match 3 :
1993

match 4 :
Gender: Male

Basic (BRE) and extended (ERE) regular expression

    在一些特殊字元處理不一樣, 請參考 GNU的說明
參考資料 :

2020年8月8日 星期六

英文歌詞翻譯 Jonathan Roy - Keeping Me Alive


You try to hold me down so I became a soldier
你想要控制所以我成為了戰士
Built up all theses walls and now I'm climbing over
監牢般的城牆如今我已越過
Those nasty bees are tempting me
那些令人惱人的念頭還在勸誘著我
Oh lord! But I ain't going back
主阿!我是不會回頭的
You take me for a fool (fool), that doesn't make me foolish
試圖讓我認知自己的愚笨,並不會使我變成真的笨蛋
Told me I was wrong (wrong), passion made you ruthless
試圖讓我認知自己的錯誤,這份情感只會讓你更無情。
Manipulate, it's just too late
試圖操弄一切,但已太晚
Oh lord! 'Cause I ain't going back no more
主阿!我再也不會回頭的
Your fueling of the flames gonna show you what I'm made of
你全心助長的這份火焰只會讓我更堅信自己
Breakin' every chain that you put on me
斷開所有你在我身上施加的枷鎖
You thought I wouldn't change but I grew on you
你以為我不會改變但我已然超越你
'Cause I will never be what you wanted
因為我永遠不會成為你的洋娃娃
This fire (this fire), this fire
這份火焰,這份情感
Is keeping me alive
只會讓我更深切地感受自我
Making me believe I couldn't do without you
想讓我相信沒有你我將會一事無成
Make it hard to leave you think it's all about you
想讓我相信我的世界只會繞著你旋轉而難以逃脫
You know I'll never be what you wanted
你知道我永遠不會成為你的洋娃娃
This fire (this fire), this fire
因為這份火焰,這份情感

I tried to get this weight off of my shoulders
試著甩開肩上所有的包袱
Built up all my strength I'm finally taking over
所以累積著力量,最後終於克服
Complicate, I don't appreciate
試著複雜一切,但我不苟同
Oh lord, 'cause I ain't going back no more
主阿!我再也不會回頭的

Your fueling of the flames gonna show you what I'm made of
你全心助長的這份火焰只會讓我更堅信自己
Breakin' every chain that you put on me
斷開所有你在我身上施加的枷鎖
You thought I wouldn't change but I grew on you
你以為我不會改變但我已然超越你
'Cause I will never be what you wanted
因為我永遠不會成為你的洋娃娃
This fire (this fire), this fire
這份火焰,這份情感
Is keeping me alive
只會讓我更深切地感受自我
Making me believe I couldn't do without you
想讓我相信沒有你我將會一事無成
Make it hard to leave you think it's all about you
想讓我相信我的世界只會繞著你旋轉而難以逃脫
You know I'll never be what you wanted
你知道我永遠不會成為你的洋娃娃
This fire (this fire), this fire
因為這份火焰,這份情感
Is keeping me alive
只會讓我更深切地感受自我
Keeping me alive, keeping me alive
讓我感受自己活著,自己的存在
This fire (this fire), this fire (is keeping me alive)
這份火焰,這份情感

Breakin' me, shakin' me, shapin' me
打擊我,動搖我,塑造我
Into what I never wanted, oh
試圖想讓我變成我所不願的
Breakin' me, shakin' me
打擊我,動搖我
Makin' my beatin' heart a little stronger
只會讓我更強烈活著

2020年7月20日 星期一

Linux - Shell Scripts (3) (pipe : grep)

管線命令 ( Pipe )

    管線命令是用 "|" 去連接,只要前一資料能變成 standard input。管線命令僅會處理 standard output,而不會處理 standard error output。下面用時下最夯的 MBTI 作的假資料。
index    name    MBTI
1        JunYe   ISTJ
2        JunYe   ISTP
3        JunYe   ESTP
4        JunYe   ESTJ
5        Mario   ISFJ
6        Mario   ISFP
7        Mario   ESFP
8        Mario   ESFJ
9        Joel    INFJ
10       Joel    INFP
11       Joel    ENFP
12       Joel    ENFJ
13       Joel    INTJ
14       Joel    INTP
15       Joel    ENTP
16       John    ENTJ
17       John    ESFP
18       Peter   ESFJ
19       Peter   INFJ
20       Peter   INFP
21       Peter   ENFP
22       Eva     ESTJ

基本管線命令 grep

    grep會在每一行找尋配對的字串,然後列出來。
$ bash pipeData.sh | grep "JunYe"
1        JunYe   ISTJ
2        JunYe   ISTP
3        JunYe   ESTP
4        JunYe   ESTJ
    加 -v 找每一行沒配對的字串,然後列出來。
$ bash pipeData.sh | grep -v "JunYe"
index    name    MBTI
5        Mario   ISFJ
6        Mario   ISFP
7        Mario   ESFP
8        Mario   ESFJ
9        Joel    INFJ
10       Joel    INFP
11       Joel    ENFP
12       Joel    ENFJ
13       Joel    INTJ
14       Joel    INTP
15       Joel    ENTP
16       John    ENTJ
17       John    ESFP
18       Peter   ESFJ
19       Peter   INFJ
20       Peter   INFP
21       Peter   ENFP
22       Eva     ESTJ
上一篇 :

2020年7月16日 星期四

Linux - Shell Scripts (2)

sh vs bash

    我有時候用 sh 會讓 shell script 執行不過,通常會報 Bad substitution 之類的錯誤。其實是因為我用 ubuntu,ubuntu 的 sh 其實是指到 dash 而非 bash。dash 在這不作多介紹, 把它想像成輕量型的 bash,所以支援的功能有限,所以有機會報錯。
ubuntu: cd /bin/
ubuntu: /bin$ ls -l
lrwxrwxrwx 1 root root       4  4月  9 16:36 sh -> dash

shell 字串操作

    #!/bin/bash
    # Shell 字串操作
    # 執行 bash string.sh

        file=/dir1/dir2/dir3/my.file.txt

        # 字串刪減
        # '#' 專刪右邊
        echo ${file#*/}   # 從左邊開始批配,刪掉 / 自身和其左邊的字串:dir1/dir2/dir3/my.file.txt
        echo ${file##*/}  # 從右邊開始批配,刪掉 / 自身和其左邊的字串:my.file.txt
        echo ${file#*.}   # 從左邊開始批配,刪掉 . 自身和其左邊的字串:file.txt
        echo ${file##*.}  # 從右邊開始批配,刪掉 . 自身和其左邊的字串:txt
        # '%' 專刪右邊
        echo ${file%/*}   # 從右邊開始批配,刪掉 / 自身和其右邊的字串:/dir1/dir2/dir3
        echo ${file%%/*}  # 從左邊開始批配,刪掉 / 自身和其右邊的字串:
        echo ${file%.*}   # 從右邊開始批配,刪掉 . 自身和其右邊的字串:/dir1/dir2/dir3/my.file
        echo ${file%%.*}  # 從左邊開始批配,刪掉 . 自身和其右邊的字串:/dir1/dir2/dir3/my

        # 取子字串
        echo ${file:0:5}  # 從 index 為 0 之字元,往後取 5 個:/dir1
        echo ${file:5:5}  # 從 index 為 5 之字元,往後取 5 個:/dir2

        # 字串取代
        echo ${file/dir/path}  # 將第一個 dir 取代成 path:/path1/dir2/dir3/my.file.txt
        echo ${file//dir/path} # 將全部的 dir 取代成 path:/path1/path2/path3/my.file.txt

    exit 0

shell 陣列操作

    # Array 操作

        declare -a ARRAY      # 宣告 array 可以省略
        ARRAY=(first second third)
        echo ${ARRAY[0]}      # first
        echo ${ARRAY[1]}      # second
        echo ${ARRAY[2]}      # third
        echo ${ARRAY[*]}      # first second third
        echo ${ARRAY[*]:1:2}  # second third
        echo ${ARRAY[@]:1:2}  # second third
        
    exit 0
上一篇 :
下一篇 :

2020年7月14日 星期二

Linux - Shell Scripts (1)

sh v.s source

    如果直接用 sh 執行 script,基本上就是開一個子程序去執行 script。所以父程序要獲得子程序的結果,通常都是靠著 export 解決 scope 的不同。若是使用 source 去執行 script,則是直接用本身程序去執行,所以本身與腳本享有共同 scope。更多請參考 : 鳥哥私房菜

基本的 variable & operator

    基本上 shell 的語法網路上有很多相關資料,這邊我想紀錄的是那些對於菜鳥不怎麼直觀的 variable & operator。而且其實我認為 shell 的精華就是在這些 variable & operator。
  • variable
        #!/bin/sh
            # 執行 : sh variable.sh a b c 
            echo $0         # script 本身
            echo $1 $2 $3   # 執行 script 時, 後面跟著的 arguments
            echo $#         # arguments 之總數
            echo $*         # 全部 arguments, 這個例子就是 a b c 
            echo $@         # 全部 arguments, 這個例子就是 a b c ($* $@ 應該有所不同, 不過我試不出來...)
            echo $?         # 上一個程式最後回傳值 (exit 0)
            echo $$         # 此 shell 的 pid
            echo $!         # 此 shell 最後執行的 background process 的 pid
        exit 0
  • operator
        #!/bin/sh
        # 執行 : sh operator.sh 20 10 
            
            # 此格式為固定的, ``, operator 前後要有空格
            # expr 不會檢查 $1 跟 $2, 所以沒有的話會出錯
            # 即使加了 "" 也會被判定 null, 等於 non-integer 也是錯 
            val=`expr $1 + $2`    # 相加
            val=`expr $1 - $2`    # 相減
            val=`expr $1 \* $2`   # 相乘 (*須加反斜線)
            val=`expr $1 / $2`    # 相除
            val=`expr $1 % $2`    # 取餘數
    
            # ==, !=, >, <, >=, <=
            if [ $1 = $2 ]; then
                val="equal"
            fi
    
            if [ $1 != $2 ]; then
                val="non-equal"
            fi
    
            if [ $1 -eq $2 ]; then
                val="equal"
            fi
    
            if [ $1 -ne $2 ]; then
                val="non-equal"
            fi
    
            if [ $1 -gt $2 ]; then
                val="greater than"
            fi
    
            if [ $1 -lt $2 ]; then
                val="less than"
            fi
    
            if [ $1 -ge $2 ]; then
                val="greater or equal"
            fi
    
            if [ $1 -le $2 ]; then
                val="less or equal"
            fi
        exit 0
  • 更多 operator
        #!/bin/sh
            # 執行 : sh operator2.sh 字串1 字串2 ...
            # 參數判斷時, 最好加上 "", 否則有高機率失效, 並出現 unary operator expected 等錯誤.
            if [ -z "$1" ]; then
                echo "there's no argument"
            fi
    
            if [ ! -z "$1" ]; then
                echo "there's at least one argument (!-z)"
            fi
    
            if [ -n "$1" ]; then
                echo "there's at least one argument (-n)"
            fi
    
            if [ "$1" ]; then
                echo "#1 argument is exist"
            fi
    
            # boolean operator -a = and, -o = or
            if [ "$1" -a "$2" ]; then
                echo "#1 and #2 arguments are both exist"
            fi
    
            if [ "$1" -o "$2" ]; then
                echo "there's at least one argument"
            fi
        exit 0
參考資料 :
下一篇 :

2020年7月11日 星期六

英文歌詞翻譯 Rudimental - These Days feat. Jess Glynne, Macklemore & Dan Caplen


I know you moved onto someone new
我知道你已經開始尋找新對象
Hope life is beautiful
希望你過得很好
You were the light for me to find my truth
你曾經是我追尋自我的燈塔
I just wanna say, thank you
現在我只想說 謝謝你

Leaving to find my soul
為了找尋真正的自我
Told her I had to go
告訴她我必須走了
And I know it ain't pretty
我知道這一點都不美好
When our hearts get broke
當彼此的心破碎
Too young to feel this old
還太年輕去體驗這殘酷的離別
Watching us both turn cold
看著兩個人漸行漸遠
Oh, I know it ain't pretty
我知道這一點都不美好
When two hearts get broke
當彼此的心破碎
Yeah, I know it ain't pretty
我知道這一點都不美好
When two hearts get broke
當彼此的心破碎

I hope someday
我希望有天
We'll sit down together
我們仍能坐在同一塊地方
And laugh with each other
一起暢談歡笑
About these days, these days
回憶曾經的時光
All our troubles
所有當初的不和
We'll lay to rest
我們都能釋懷放下
And we'll wish we could come back to these days, these days
讓我們甜蜜的回憶這段時光
These days, these days
那些時光
These days, these days
曾經的時光

Three years of ups and downs
經歷了三年的起起伏伏
Nothing to show for it now
現在卻一句話也說不出來
And I know it ain't pretty when the fire burns out
我知道這一點都不美好 當感情消逝殆盡
Calling me when I'm drunk, remind me of what I've done
在我沉浸酒精時數落我 一筆一筆算著我曾經錯誤
And I know it ain't pretty when you're trying to move on, yeah
我知道這一點都不美好 當你試著離開
I hope someday
我希望有天
We'll sit down together
我們仍能坐在同一塊地方
And laugh with each other
一起暢談歡笑
About these days, these days
回憶曾經的時光
All our troubles
所有當初的不和
We'll lay to rest
我們都能釋懷放下
And we'll wish we could come back to these days, these days
讓我們笑著回憶這段時光
Oh I know, I know
噢 我知道 我知道
Oh I know, I know
噢 我知道 我知道
Oh I know, I know
噢 我知道 我知道
These days, these days
這些時光 這些美好的時光
Oh I know, I know
噢 我知道 我知道
Oh I know, I know
噢 我知道 我知道
Oh I know, I know
噢 我知道 我知道
These days, these days
這些時光 這些美好的時光

Cigarettes in the ash tray
香菸靜靜地躺在菸灰缸裡
Reminiscing on those past days
回憶著那些日子
I thought you'd end up with my last name
我曾經以為你最終會是我的老婆
But that changed
但一切都變了
And I travelled around the world
我的足跡踏遍全世界
Think where you living at now?
卻想著你住在何方
I heard you moved to Oxford
我聽說你搬到 Oxford
Got an apartment and settled down
在一個公寓安頓下來
And every once in a while
每過段時間
I start texting
我就想著與你聯繫
Write a paragraph
但我寫了一個段落後
But then I delete the message
便停筆不寫了
Think 'bout you like a past time
像過往一樣地想起妳
I could cry you a river
我便淚流滿面
Get you baptised or
足以讓你沉浸在裡的那種
I wasn't ready to act right
我還沒準備好與你相遇
Used to always think I'd get you back, right
過去總想著挽回妳
They say that things fall apart (yeah)
他們說一切早已分崩離析
We were gonna move to Brooklyn
我們原本要一起搬到 Brooklyn
You were gonna study Art (oh no, oh)
妳原本要學藝術
Love is just a tool
原來愛情是一個工具
To remind us who we are
提醒著我們真正的自己
And that we are not alone
但現在我們已不孤單
When we're walking in the dark
當我們走在夜空下
備註 :
單身 30 年的我在聽到這首歌,腦中總會顯現那段不曾存在的戀情。這首歌送給有妄想症的單身狗。

讀書心得 - C++ Primer (5th Edition) - Chapter 2 (3) - Type Aliases



Type Aliases

    創造資料型態的同義詞,通常是用 typedef 來去實現。
    typedef double wages;   // wages is a synonym for double
    typedef wages base, *p; // base is a synonym for double, p for double*

auto

    寫程式常會有想要一個 variable 去儲存 expression 的運算結果,但要事先知道 expression 的最終資料型態非常困難。所以有 auto 這個特別的資料型態,讓編譯器決定其資料型態,但相對的 variable 一定要 initialize。
    auto item = val1 + val2; // item initialized to the result of val1 + val2
    因為 auto 一次只能當一個資料型態,所以在宣告時如果是用 ", " 分開的複數宣告,資料型態要一致。
    auto i = 0, *p = &i;      // ok: i is int and p is a pointer to int
    auto sz = 0, pi = 3.14;   // error: inconsistent types for sz and pi
    auto 與 const 的互動,基本上 auto 會忽視 top level const ( 物件本身為 const )。
    const int ci = i, &cr = ci;
    auto b = ci;       // b is an int (top-level const in ci is dropped)
    auto c = cr;       // c is an int (cr is an alias for ci whose const is top-level)
    auto d = &i;       // d is an int*(& of an int object is int*)
    auto e = &ci;      // e is const int*(& of a const object is low-level const)
    若想要個 top level auto const,你只能在 auto 前加 const 修飾詞。
    const auto f = ci; // deduced type of ci is int; f has type const int

decltype()

    不同於 auto,decltype() 可以不用去計算 expression,只讓編譯器去分析其資料型態。下面程式碼,編譯器不會呼叫 f(),而是去檢查 f() 的 return 資料型態去給予 sum。
    decltype(f()) sum = x;     // sum has whatever type f returns
    decltype() 也可以與 top level const 互動。
    const int ci = 0, &cj = ci;
    decltype(ci) x = 0;     // x has type const int
    decltype(cj) y = x;     // y has type const int& and is bound to x
    decltype(cj) z;         // error: z is a reference and must be initialized
上一篇 :

2020年7月9日 星期四

讀書心得 - C++ Primer (5th Edition) - Chapter 2 (2) - Const



修飾詞 : Const

    如果我們想要一個不可被改變的參數,這時就會需要這個修飾詞 ( Qualifier ) const。
    const int bufSize = 512;  // input buffer size
    bufSize = 1024;           // error: attempt to write to const object

Const + Reference

    Const + Reference 代表 Reference to const,等於你不能使用這個 Reference 去改變物件。沒有 const 修飾詞 ( plain object ) 的 reference 不可以 bind 有 const 修飾詞的物件,但反之有 const 修飾詞的 reference 可以 bind 無 const 修飾詞的物件。
    int i = 1;
    const int &r = i;  // we can bind a const int& to a plain int object
    i = i + 1;         // "r" will be 2 too.
    r = r + 1;         // error: assignment of read-only reference "r"
    int &j = r;        // error: binding ‘const int’ to reference of type ‘int&’ discards qualifiers

Const + Pointer ( const type * var )

    跟 Reference 一樣 Pointer to const,等於你不能使用這個 Pointer 去改變物件。沒有 const 修飾詞 ( plain object ) 的 Pointer 不可以 bind 有 const 修飾詞的物件,但反之有 const 修飾詞的 Pointer 可以 bind 無 const 修飾詞的物件。
    const double pi = 3.14;     // pi is const; its value may not be changed
    double *ptr = &pi;          // error: ptr is a plain pointer
    const double *cptr = &pi;   // ok: cptr may point to a double that is const
    *cptr = 42;                 // error: cannot assign to *cptr

Const + Pointer ( type * const var )

    但 Pointer 跟 Reference 不一樣,本身也是物件,所以可以 const pointer。const pointer 則必須被 initialized。但因為是 const pointer 而不是 const 指向物件,所以可以用 const pointer 去改變物件。
    int numb = 0;
    int * const cptr1 = &numb;  // cptr1 will always point to numb
    int * const cptr2;          // error: uninitialized const 'cptr2'
    *cptr1 = 5;                 // numb will be 5 too.

top-level/lower-level const

    像 pointer 的 const 就有兩種,所以稱 const pointer object 這種本身為 top-level,稱 point to const 這種為 lower-level。
    int i = 0;
    int *const p1 = &i;       // we can't change the value of p1; const is top-level
    const int ci = 42;        // we cannot change ci; const is top-level
    const int *p2 = &ci;      // we can change p2; const is low-level
    const int *const p3 = p2; // right-most const is top-level, left-most is not
    const int &r = ci;        // const in reference types is always low-level

constexpr ( constant expression )

    constant expression 等於 expression 不能被改變,也就等於這個 expression 必須在編譯時期就可以被計算出來。以下為 constant expression 的例子。
    const int max_files = 20;        // max_files is a constant expression
    const int limit = max_files + 1; // limit is a constant expression
    int staff_size = 27;             // staff_size is not a constant expression
    const int sz = get_size();       // sz is not a constant expression
    int i; 
    const int size1 = i; 
    constexpr int size2 = i;      // error: the value of ‘i’ is not usable in a constant expression
    constexpr int mf = 20;        // 20 is a constant expression
    constexpr int limit = mf + 1; // mf + 1 is a constant expression
    constexpr int sz = size();    // ok only if size is a constexpr function
    constexpr 跟 pointer 的作用
    const int *p = nullptr;       // p is a pointer to a const int
    constexpr int *q = nullptr;   // q is a const pointer to int
相關文章 :
上一篇 :
下一篇 :

2020年7月3日 星期五

讀書心得 - C++ Primer (5th Edition) - Chapter 2 (1) - Primitive Types



C++ 原生內建的資料型態 ( Primitive Types )

    C++ 定義了一些算術用的資料型態 ( Arithmetic Types ) 和一個特殊的資料型態 void。Arithmetic Types : Integer, Character, Boolean, Floating Point, Double Floating Point, Wide Character。

    下面為一些資料型態轉換 :
    bool b = 42;            // b is true
    int i = b;              // i has value 1
    i = 3.14;               // i has value 3
    double pi = i;          // pi has value 3.0
    unsigned char c = -1;   // assuming 8-bit chars, c has value 255
    signed char c2 = 256;   // assuming 8-bit chars, the value of c2 is undefined

Initialization

    各種初始 :
    int units_sold1 = 0;
    int units_sold2 = {0};
    int units_sold3{0};
    int units_sold4(0);
    {} 跟 () 的不同 :
    long double ld = 3.1415926536;
    int a{ld}, b = {ld};    // error: narrowing conversion required
    int c(ld), d = ld;      // ok: but value will be truncated

Declaration 跟 Definition

    為了支援多個檔案的編譯,cpp 區分了 Declaration 跟 Definition。Declaration 代表著跟整個程式宣告一個帶有資料型態的名子,任何檔案都可以去作存取。Definition 則是創造一個物件實體去對應那個帶有資料型態的名子。
    如果想要只宣告名子(代表實際物件會被在其他檔案給實現),則會在宣告時加上 extern,有了 extern 就代表你不能下 Definition。
    extern int i;              // declares but does not define i
    int j;                     // declares and defines j
    extern double pi = 3.1416; // error: ‘pi’ has both ‘extern’ and initializer

Compound Types - References

    Reference 等於創造額外的一個名子給一個物件。所以下面第 refVal2, refVal4 會 error,因為你不能給不存在的物件 ( 或 rvalue ) 取名。References must be initialized.
    int ival = 1024;
    int &refVal = ival;     // refVal refers to (is another name for) ival
    int &refVal2;           // error: a reference must be initialized
    int &refVal3 = refVal;  // ok: refVal3 is bound to the object to which refVal is bound, i.e., to ival
    int &refVal4 = 10;      // error: initializer must be an object

Compound Types - Pointers

    Pointer為一個 "指向" 物件。類似 References 但卻是一個物件,所以可以不用被 initialized,也可以用來複製、分配。下面的 & 是作為 operator,用來取物件的實際位置,與上面提到用來宣告資料型態的的 & 有著不同的意義。
    double dval;
    double *pd = &dval; // ok: initializer is the address of a double
    double *pd2 = pd;   // ok: initializer is a pointer to double
    int *pi = pd;       // error: types of pi and pd differ
    pi = &dval;         // error: assigning the address of a double to a pointer to int
    下面的 * 作為 operator,放在 pointer 前面等於取其指向的該物件
    *p = 0;             // * yields the object; we assign a new value to ival through p
    std::cout << *p;    // prints 0

更多 References & Pointers 範例

    double dval;
    double *pd = &dval;  // ok: initializer is the address of a double
    double *pd2 = pd;    // ok: initializer is a pointer to double
    int *pi = pd;        // error: types of pi and pd differ
    pi = &dval;          // error: assigning the address of a double to a pointer to int

    int i = 42;
    int &r = i;          // & follows a type and is part of a declaration; r is a reference
    int *p;              // * follows a type and is part of a declaration; p is a pointer
    p = &i;              // & is used in an expression as the address-of operator
    *p = i;              // * is used in an expression as the dereference operator
    int &r2 = *p;        // & is part of the declaration; * is the dereference operator
相關文章 :
上一篇 :
下一篇 :

2020年6月14日 星期日

讀書心得 - C++ Primer (5th Edition) - Chapter 1

簡單的 C++ 程式

    要寫一個 C++ 程式跟 C 一樣,寫好 cpp 檔,交給編譯器 ( g++ ) 編成可執行程式。程式至少包含一個名為 main 的 function,用以當作程式的入口 function。
    int main() {
        return 0; // return 0 的原因為 0 通常代表著程式執行成功。
    }

Input/Output

    C++ 本身並沒有定義 I/O,需要額外的標準函式庫,iostream。
    #include <iostream>
    int main()
    {
        // operator << >> 會回傳左邊的 operand
        // 以 std::cin >> v1 >> v2; 為例
        // 執行結果 =
        //     std::cin >> v1;
        //     std::cin >> v2;
        int v1, v2;
        std::cout << "Enter two numbers:" << std::endl;
        std::cin >> v1 >> v2;
        std::cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << std::endl;
        return 0;
    }

Note

常用詞彙

  • prompt 提示 驅使

    We can extend our main program to prompt the user to give us two numbers and then print their sum

  • expression (數學中的)式,表達式

    The smallest unit of computation. An expression consists of one or more operands and usually one or more operators. Expressions are evaluated to produce a result. For example, assuming i and j are ints, then i + j is an expression and yields the sum of the two int values.

  • angle brackets : <>

  • quotation marks : ""

  • curly braces : {}

  • operator : 運算子

  • operand : 運算元

  • assignment

    Obliterates an object’s current value and replaces that value by a new one.

  • comments : 註解

  • statement : wiki

    The new part of this program is the while statement.A while has the form

    while (condition)
        statement

    A while executes by(alternately) testing the condition and executing the associated
    statement until the condition is false.A condition is an expression that yields a result
    that is either true or false..So long as condition is true, statement is executed.
下一篇 :

Popular Posts