2017年4月30日 星期日

能找到最內層的tag的正規表示法

今天要來介紹一個能找到最內層一組tag的正規表示法,

例如有一個需求如下:
tag格式為[quotePostId=XX]YY[/quotePostId],XX填數字,YY填文字,
可嵌套,如何從以下tag嵌套中找出最內層的XX和YY:
[tagId=1]kk[tagId=2][tagId=3]kk[/tagId]kk[/tagId][/tagId]

這裡提供一個解法,匹配的Group1即為XX、Group2為YY
\[tagId=(\d+)\](?=((?:[^\[]|\[(?!(?:tagId=\d+\]|\/tagId\])))*))\2\[\/tagId\]

下面來做解釋:
思路是:

  1. 必須符合[tagId=XX]YY[/tagId]。
  2. XX必須為非空值之數字。
  3. YY可為空值或任何字元。
  4. YY不可含有[tagId=??]或[/tagId],因為含有即代表此次匹配YY不為最內層tag之內容。

實作步驟(???為還未確定的部份,用紅色標色,當下確定的地方用藍色標色,之前確定的用黑色標色):

  1. 首先因為必須符合[tagId=XX]YY[/tagId],即XX必須為非空值之數字,所以先寫出一開始的正規表示法。
    \[tagId=(\d+)\](???)\[\/tagId\]
  2. 歩驟1的???需要符合條件(思路4)才須匹配,否則匹配不成功,因為x(?=y)這個語法不會把y算在匹配成功的字串內,所以用\2來裝Group2選到的YY放到tag中間。
    \[tagId=(\d+)\](?=(???))\2\[\/tagId\]
  3. YY可為空值,所以在Group2裡面放一個不記Group編號的Group,然後此Group可以0到多個。
    \[tagId=(\d+)\](?=((?:???)*))\2\[\/tagId\]
  4. 開始匹配YY,分成碰到非左中括弧( [ )、或是左中括弧的兩種情況。
    當是左中括弧時,根據後面接的字串決定要不要匹配。
    \[tagId=(\d+)\](?=((?:[^\[]|\[(???))*))\2\[\/tagId\]
  5. 如是是左中括弧時,根據後面接的字串情況決定要不要匹配,不匹配的情況有兩種,
    第一種:為[tagId=x]
    \[tagId=(\d+)\](?=((?:[^\[]|\[(?!(?:tagId=\d+\]|???)))*))\2\[\/tagId\]
    第二種:為[/tagId]
    \[tagId=(\d+)\](?=((?:[^\[]|\[(?!(?:tagId=\d+\]|\/tagId\])))*))\2\[\/tagId\]

最後我們完成了正規表示法:
\[tagId=(\d+)\](?=((?:[^\[]|\[(?!(?:tagId=\d+\]|\/tagId\])))*))\2\[\/tagId\]

以下為可測試的線上工具:

  1. 圖示化正規表示法
  2. 正規表示法線上實驗,可以檢查Group1、Group2有沒有取到。

2017年4月29日 星期六

Instagram access token 的取得

今天要介紹的是如何取得Instagram的Access Token,及
用Access Token來取得使用者的資訊,如發佈的貼文(Instagram的貼文都是圖片或影片)圖片url等。

今天的需求是:

  1. 想要取得自己在Instagram上面的貼文圖片。
  2. 想要將取得的貼文圖片展示在網頁上。

使用技術:

  1. JSP
  2. Jquery

先講Instagram 官方步驟:

  1. 一開始如果沒有Instagram帳號,請註冊一個。
  2. 進入Instagram的developer console,填好相關資訊註冊成為Developer。
  3. 自己建立一個網頁,假設網址為REDIRECT-URI。
  4. 到Manage Clients建立一個App,其中"Valid redirect URIs"填上步驟3的REDIRECT-URI。
  5. 進入
    https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=code
    其中 CLIENT-ID 為已建立之App的 CLIENT-ID , REDIRECT-URI 即為自己建立網頁的網址。
  6. 待使用者登入並授權以後,會被引導至 REDIRECT-URI 頁面,並會以GET方式在Url後面帶上一個 code 參數。
  7. 這個 code 只能用一次,
    以以下參數
    client_id : App的client_id
    client_secret : App的client_secret
    grant_type : "authorization_code"
    redirect_uri : App的redirect_uri
    code : 剛剛得到的code
    請求 https://api.instagram.com/oauth/access_token,
    成功的話會在Response中得到access_token的參數,即我們要PO文資訊時需要的access_token。
  8. 要到access_token以後,就可以用來要各種使用者的資料了,在Developer console的左邊"Endpoints"中有提供各種Endpoints,這邊我們使用下面這個就好:
    https://api.instagram.com/v1/users/self/media/recent/?access_token=ACCESS_TOKEN
    請在ACCESS_TOKEN填上剛要到的,代表使用者的ACCESS_TOKEN,成功的話會回傳使用者最近的照片PO文資料。

下面以實測的程式碼來演示,

2017年2月26日 星期日

Chrome WebPush - node.js (客製化訊息 - 需加密)

Web Push

之前介紹的Google Chrome Web Push (客製化訊息 - 需加密)是使用Java來傳送加密的客製化訊息,其中有提到Google有提供了一個node.js版本的webpush程式,其中已經把加密的方法封裝好了,可以很容易地幫助我們傳送客製化的Web Push訊息。

在使用之前記得把node.js安裝好,可以去node.js官網下載適合版本安裝。

Google製作的webPush project的github連結在這,web-push,
要安裝只要下以下指令即可:
npm install web-push --save

即著在同一個資料夾建立一個webPushTest.js來測試,其中內容如下,需要說明的部份已寫在註解中:

webPushTest.js:
const webpush = require('web-push');  //引入web-push模組

webpush.setGCMAPIKey('XXXXXXXXXXXX');  //打入FireBase Server Key或Google API Key

// This is the same output of calling JSON.stringify on a PushSubscription
const pushSubscription = {
  endpoint: 'XXXXXX',  //webpush註冊後得到的endpoint,前面會跟據Webpush Server而不同,
                       //例如Google可能是https://updates.push.services.mozilla.com/wpush/v1/,
                       //FireFox可能是https://updates.push.services.mozilla.com/wpush/v1/
  keys: {
    auth: 'XXXXX',  //加密金鑰的其中一個,auth
    p256dh: 'XXXXX'  //加密金鑰的其中一個,p256dh
  }
};

webpush.sendNotification(pushSubscription, '{"myText": "Hugo Test", "myIcon": "https://www.vermontteddybear.com/media/wysiwyg/VTB-CMS-Content/featuredcat-KBKF36004-20170119.jpg"}');

2017年2月19日 星期日

java抓取網路圖片放到本地

這篇紀錄了如何使用Java獲取網路上的檔案,程式碼很簡單,直接把程式碼寫出

2017/10/11 更新,寫了新的一篇,包含了HTTPS,User-Agent,Content-Type等更詳細的
細節
以Java 由Url下載圖片

package readFileFromUrlTest;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ReadFileFromUrlTest {

    public static void main(String[] args) {
        ReadFileFromUrlTest readFileFromUrlTest = new ReadFileFromUrlTest();
        String url = "https://www.vermontteddybear.com/media/wysiwyg/VTB-CMS-Content/featuredcat-KBKF36004-20170119.jpg";
        String filePath = "D:\\xxx.jpg";
        try {
            readFileFromUrlTest.readFileFromUrl(new URL(url), new File(filePath));
        } catch (IOException ex) {
            Logger.getLogger(ReadFileFromUrlTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void readFileFromUrl(URL url, File file) throws IOException {
        BufferedInputStream bufferedInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            bufferedInputStream = new BufferedInputStream(url.openStream());
            fileOutputStream = new FileOutputStream(file);
            int data;
            //從串流讀取資料寫到檔案中
            while ((data = bufferedInputStream.read()) != -1) {
                fileOutputStream.write(data);
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            //關閉串流
            try {
                if (file != null) {
                    fileOutputStream.close();
                }
                if (bufferedInputStream != null) {
                    bufferedInputStream.close();
                }
            } catch (IOException ex) {
                throw ex;
            }
        }
    }

}


參考資料:

  1. java抓取网络图片放到本地


2017年2月18日 星期六

WebSocket - Tomcat v8.0+ - 簡單的聊天室

WebSocket可以在伺服器和瀏覽器之間建立雙向通訊,今天要來用
WebSocket來實現一個簡單的聊天室做範例,紀錄一下如何使用WebSocket
伺服器採用Tomcat v8.0+、JDK 1.8,IDE 為 NetBeans

需求:

  1. 網頁上面可以輸入名稱登入聊天室。 登入後聊天室會出現登入訊息。
  2. 登入後可在訊息框中輸入訊息並送出,送出後聊天室會出現訊息。
  3. 聊天室出現訊息指的是,任何已登入聊天室的人都會看到訊息。
  4. 登入後的人只能看到登入後有人打的訊息。


先來看一下檔案結構:


2017年2月17日 星期五

正規表示法 - 幫數字字串的每三個位數標上逗號

碰到一個需求是將數字字串的每三個位數標上逗號,
例如:
1234567 ==> 1,234,456

發現原來正規表示法也可以辦到,特別在這邊做個紀錄。

以Javascript為例,以下的方法numberWithCommas()可以將輸入的整數字串每隔3位數標上逗號

function numberWithCommas(x) {
  return x.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

使用的正規表示法為
\B(?=(\d{3})+(?!\d))

先來看看它的圖型化表示

圖型化表示連結


解釋:
我們要匹配的是從低位數數起,每三個位數的位置(是位置,不是字元),但不包括左邊沒數字的位置,
例如123456789,匹配的位置有
3跟4這間、6跟7中間,但1左邊不應該要有。



\B   :   非字元邊界,在此列中即左邊不應該有空白

x(?=y)   :   x接著y才匹配成功,並且y不列入匹配成功的字串中,只匹配出x,如果x沒寫,則 匹配出來的為位置

\d{3}  :   3個數字字元

x(?!y)   :   x後面不跟著y才匹配成功

所以組合出來的就是

  1. 左邊不要空格(即不要左邊界) ==>  \B
  2. 比對後面符合我們要的條件的位置  ==> \B(?=)
  3. 條件之一,1到多個的,3個為一組的數字字元  ==>  \B(?=(\d{3})+)
  4. 條件之二,條件之一的最後面不可是數字字元  ==>  \B(?=(\d{3})+(?!\d))
最後,找到位置以後,把位置替換成逗號就行了。



node.js POST 及 GET的讀取

使用Node.js 架Server時,我們常需要從Client端接收GET或POST方法傳來的參數,
有兩個module可以利用,url及querystring

下面是簡單的範例:
(function(){
   //獲取modules
  var http = require('http');   //架Server用
  var url = require('url');      //解析url,包括GET參數
  var querystring = require('querystring');     //可以用來解析POST的參數語句

  //建立Server
  var server = http.createServer(function(request, response) {
    //得到url資訊
    var urlInfo = url.parse(request.url, true);
    //取得url的path
    var pathname = urlInfo.pathname;
   
    response.writeHead(200, {
      "Content-Type": "application/json",
      "Access-Control-Allow-Origin": "http://XXX"  //如果要當做api給Client利如用Ajax呼叫的
                                                                                   //話,要加跨域的Header
    });
    //判斷是不是要處理的path name
    if (pathname === "/somePathName") {
       var GET_data = urlInfo.query;  // 得到GET的參數資訊
       console.log(GET_data);

       //準備得到POST的參數資訊
       var POST_data = '';     //POST_data 用來存POST資訊
       //POST的資訊息會不斷送進來,不斷送的時候觸發data事件,結束觸發end事件,
       // 綁在request上面以接收POST參數資訊
       request.on("data", function(data){
            POST_data += data
       });  
       request.on("end", function(){
           //可以利用querystring module來將POST參數資訊轉成物件
           var postDataObject = querystring.parse(POST_data );
           console.log(postDataObject);
       });
    } else {
      //
    }
  });

  設置Server的Port
  server.listen(3000);

  console.log('Server跑起來了,現在時間是:' + new Date());
})();

這邊要注意的是,POST的資料不是一次性的得到,而是不斷地傳送進Server,所以我們在request上綁定了"data"及"end"事件來監聽POST資料的傳遞狀態,在"data"事件中不斷地組合POST參數資料,當資料傳送後"end"事件會被觸發,這時才會得到完整的POST參數資料。

參考資料:
  1. Node.jS初學者筆記(1)-用GET傳送資料
  2. Node.jS初學者筆記(2)-用POST傳送資料