iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
1
自我挑戰組

30天找回寫程式手感計劃!!!系列 第 18

Day18 - 正規表示式學好就可以知道如何正確的表達心意是否搞錯了什麼

  • 分享至 

  • xImage
  •  

怎麼有一種我標題越下越宅的感覺......
我相信同好應該一看就知道我在跟哪一部致敬吧XD

總之,正規表示式就是用在你要找出 match 某個 pattern 的字串,
正規表示式就是你要事先訂好的 pattern,
例如你想在一篇文章中找出含有 cathy 的句子,
正規表示式就要訂為 /cathy/ 。
所以正規表示式學好讓你可以跟電腦告白應該是沒問題的。~

正片開始

今日素材:鐵人賽系列文 RSS

其實會想挑戰 正規表示式 是因為昨天的題目,
偶然發現了鐵人賽系列文的 RSS 長這樣:
https://ithelp.ithome.com.tw/upload/images/20200924/20129873VwKlSnIWOx.png
所以想要去問資料回來後取得 title 跟 link,
再將之列出來!

一樣先將資料取回來再說

其實問 RSS 資料的方法跟昨日提到的一樣,
讓我們把 URL 改成 RSS 的網址試試看吧!

// 拿即時更新的 JSON 檔
let requestURL = "https://ithelp.ithome.com.tw/rss/series/3573";
let xhr = new XMLHttpRequest();
xhr.open("GET",requestURL,true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
xhr.responseType = 'json';
xhr.send(); 

// onload 是等資料拿到的時候才執行
xhr.onload = function(){
    // responseData 設為拿到的回傳 JSON 
    let responseData = xhr.response;
    console.log(responseData);
}

https://ithelp.ithome.com.tw/upload/images/20200924/20129873Y0XCpcFu18.png
null?怎麼會是 null?
看到錯誤或結果不如預期的時候不要怕,
console.log 是我們的好朋友,
那我們不要拿 xhr.response,
只拿 xhr 看看:

console.log(xhr);

xhr 有回傳回來的東西,
一個一個點開來看,
發現在 responseXML 有寫到相關的錯誤訊息:
https://ithelp.ithome.com.tw/upload/images/20200924/201298735MOWX64K6Q.png
然後我就會想,啊,因為這次的 RSS 其實是 XML 格式,
會不會是因為它不支援 json 所以噴錯呢?
讓我們把 xhr.responseType = 'json' 註解掉再試一遍:

// 拿即時更新的 JSON 檔
let requestURL = "https://ithelp.ithome.com.tw/rss/series/3573";
let xhr = new XMLHttpRequest();
xhr.open("GET",requestURL,true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
// xhr.responseType = 'json'; // 註解掉試試看
xhr.send(); 

// onload 是等資料拿到的時候才執行
xhr.onload = function(){
    let responseData = xhr.response;
    console.log(xhr);
}

哦哦哦,看起來有拿到東西了!
https://ithelp.ithome.com.tw/upload/images/20200924/20129873xQzpzTBmCe.png
讓我們就可以開始今日的課題了!

一樣良好習慣,先把動態拿資料的邏輯拿掉,先用靜態資料寫我們的正規表示式

原因昨天的文章有提到XD
就是小的還太弱,如果還在練習正規表示式,
我就被 鐵人賽 RSS 視為攻擊就不好了,
所以就一樣在最前面宣告變數放靜態資料。
但因為內容龐大,先貼個 title + link 附近幾行的內容意思意思一下就好,
像這樣:

let responseData = `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title>30天找回寫程式手感計劃!!! :: 第 12 屆 iT 邦幫忙鐵人賽</title>
        <link>https://ithelp.ithome.com.tw/users/20129873/ironman</link>
        <item>
                <title>Day17 - Open Data x JavaScript x JSON ( 1/2 )</title>
                <link>https://ithelp.ithome.com.tw/articles/10242719?sc=rss.iron</link>
                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10242719?sc=rss.iron</guid>
                <description><![CDATA[<p><del>這絕對不是向獵人致敬的標題哦XD</del><br />
                    <pubDate>2020-09-23 22:24:58</pubDate>
                                                                                                                                            </item>
                    <item>
                <title>Day16 - 雖然很久沒有國際賽事,還是要用 JavaScript 關心一下小戴的 World Rankings!─ addEventListener 篇</title>
                <link>https://ithelp.ithome.com.tw/articles/10242034?sc=rss.iron</link>
                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10242034?sc=rss.iron</guid>
                <description><![CDATA[<p>對不起,在下小的我要用小戴女神撐過 2 天了QQ<br />`;
console.log(`responseData 是: ${responseData}`);

https://ithelp.ithome.com.tw/upload/images/20200924/20129873AtuDdRjn2T.png

本日大魔王:正規表示式

正規表示式我沒有很常寫,
所以格式語法我背不起來orz
讓我們站在巨人的肩膀上吧→
正規表示式 Regular Expression
[JS] 正則表達式(Regular Expression, regex)
這是我在寫卡關時最常參考的 2 篇,
然後中間我卡關的地方就不一一在這裡貼出了,
直接貼出我試的結果:

let matchPattern =  new RegExp('<title>.+', 'g'); // 訂定正規表示式的比對 Pattern
let matchResult = responseData.match(matchPattern); 
if ( matchResult.length > 0 ){
    for ( let i=0; i<matchResult.length;i++ ){
        console.log(`${matchResult[i]}`);
    }
}

https://ithelp.ithome.com.tw/upload/images/20200924/201298736jQMbJI5Xm.png
看起來有比對到我們要的樣子,
這邊講解一下 Pattern 的訂定:

  1. <title>.+ 意思是要找到含 <title> 且後面至少包含 1 個任意字元。
    https://ithelp.ithome.com.tw/upload/images/20200924/20129873QluktAtFBK.png
  2. RegExp('<title>.+', 'g')
    g: global 的意思,找到之後會繼續往後配對
    如果沒有設定 g,則找到一個之後就會停止尋找。
    如果要忽略大小寫,則為 RegExp('<title>.+', 'i')
    當然也可以兩個混搭變:RegExp('<title>.+', 'gi')
  3. responseData.match(matchPattern)

String.prototype.match():以陣列回傳字串中匹配到的部分,否則回傳 null。

弄回動態取得資料試試看正規表示式的比對有沒有 work 吧!

let requestURL = "https://ithelp.ithome.com.tw/rss/series/3573";
let xhr = new XMLHttpRequest();
xhr.open("GET",requestURL,true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
// xhr.responseType = 'json';
xhr.send(); 

// onload 是等資料拿到的時候才執行
xhr.onload = function(){
    // responseData 設為拿到的回傳資料 
    let responseData = xhr.response;
    // let responseData = xhr;
    // *****等拿到資料再進行後面字串比對
    // *****如果沒有這樣寫,則會跟前面同時執行,就會因為沒資料而出錯
    compareData(responseData);
}

function compareData(responseData){
    let matchPattern =  new RegExp('<title>.+', 'g'); // g: global 的意思,找到之後會繼續往後配對
    let matchResult = responseData.match(matchPattern);
    if ( matchResult.length > 0 ){
        console.log("有包含 title");
        for ( let i=0; i<matchResult.length;i++ ){
            console.log(`${matchResult[i]}`);
        }
    }
}

https://ithelp.ithome.com.tw/upload/images/20200924/20129873bZZtDW62W0.png
雖然比到了很開心,
但有沒有發現一件事情?
我們要取的只有標籤裡面的內容,
並非整行.......
/images/emoticon/emoticon17.gif

好吧,還是先弄回靜態資料,
繼續克服這個難關吧。

如何只取標籤裡面的內容?

這邊小的硬弄出來了,
寫法可能不是很好,
(因為我不知道要怎麼用該 Pattern 比對,可是又讓比對結果不包含 Pattern orz)
但有試出來就好,
之後再 tune 個寫法。

matchPattern =  new RegExp('>.+<', ''); // 找出包含在 > < 中間的字
matchResult_content = responseData.match(matchPattern);
console.log(`${matchResult_content[0]}`);
let str = matchResult_content[0].replace(/>/, '').replace(/</, ''); // 然後再用 replace 把 < >取代成空白 (JavaScript支援 .replace.replace 的寫法)
console.log(`str: ${str}`);

https://ithelp.ithome.com.tw/upload/images/20200924/20129873qSC4gP21Pv.png
弄出來了!
剩下的就是把結果用到網頁上呈現就好!
別忘了還要附上連結哦!

這邊我的做法是先比對有含 >< 的字串,
再用 replace 將 >< 取代成空白。

html:

<h3>第 12 屆 iT 邦幫忙鐵人賽</h3>
<h3>「30天找回寫程式手感計劃!!!」系列文 RSS</h3>
<ul id="articleList"></ul>

JavaScript:

const articleListElement = document.getElementById("articleList");
let articleListHTML = "";

let matchArray = compareData(responseData);
for ( let x=0; x<matchArray.length;x++ ){
    articleListHTML += `<li><a href="${matchArray[x].Link}" target="_blanck">${matchArray[x].Title}</a></li>`;
}
articleListElement.innerHTML = articleListHTML;

function compareData(responseData){
    let matchArray = [];
    let matchPattern =  new RegExp('<title>.+', 'g'); // g: global 的意思,找到之後會繼續往後配對
    let matchResult_title = responseData.match(matchPattern);
    
    matchPattern =  new RegExp('<link>.+', 'g');
    matchResult_link = responseData.match(matchPattern);
    if ( matchResult_title.length > 0 ){
        for ( let i=0; i<matchResult_title.length;i++ ){    
            matchPattern =  new RegExp('>.+<', '');
            matchResult_content = matchResult_title[i].match(matchPattern);
            matchResult_title[i] = matchResult_content[0].replace(/>/, '').replace(/</, '');

            matchResult_content = matchResult_link[i].match(matchPattern);
            matchResult_link[i] = matchResult_content[0].replace(/>/, '').replace(/</, '');

            matchArray.push({Title:matchResult_title[i],Link:matchResult_link[i]}); // 將 trim 過的 title & link 存起來
        }
    }
    return matchArray;
}

https://ithelp.ithome.com.tw/upload/images/20200924/20129873udEnJn3Fii.png
やった!太好了!
やった!(太好了!)
讓我們一鼓作氣再改成動態取資料試試看吧!

let requestURL = "https://ithelp.ithome.com.tw/rss/series/3573";
let xhr = new XMLHttpRequest();
xhr.open("GET",requestURL,true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
// xhr.responseType = 'json';
xhr.send(); 

// onload 是等資料拿到的時候才執行
xhr.onload = function(){
    // responseData 設為拿到的回傳資料 
    let responseData = xhr.response;
    // let responseData = xhr;
    // *****等拿到資料再進行後面字串比對等邏輯
    // *****如果沒有這樣寫,則會跟前面同時執行,就會因為沒資料而出錯
    let matchArray = compareData(responseData);
    for ( let x=0; x<matchArray.length;x++ ){
        articleListHTML += `<li><a href="${matchArray[x].Link}" target="_blanck">${matchArray[x].Title}</a></li>`;
    }
    articleListElement.innerHTML = articleListHTML;
}

https://ithelp.ithome.com.tw/upload/images/20200924/2012987307Tbm0X76z.png
本日打完收工!

[後記]

覺得今天有很多程式碼都寫得很重複,
但先求有再求好,
正規表示式果然博大精深XD
覺得應該要好幾天都來寫正規表示式才行!!!!!

[本日程式碼]

Codepen


上一篇
Day17 - Open Data x JavaScript x JSON
下一篇
Day19 - 正規表示式 x JavaScript x JSON ( 1/2 )
系列文
30天找回寫程式手感計劃!!!36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言