面試題之正規表示式解析一個完整的url

飯妖精發表於2019-03-03

最近在補充基礎知識,看到了一道面試題 地址傳送點這裡

題目:解析一個完整的url,返回Object包含域與window.location相同

答案:

/**
 * 解析一個url並生成window.location物件中包含的域
 * location:
 * {
 *      href: '包含完整的url',
 *      origin: '包含協議到pathname之前的內容',
 *      protocol: 'url使用的協議,包含末尾的:',
 *      username: '使用者名稱', // 暫時不支援
 *      password: '密碼',  // 暫時不支援
 *      host: '完整主機名,包含:和埠',
 *      hostname: '主機名,不包含埠'
 *      port: '埠號',
 *      pathname: '伺服器上訪問資源的路徑/開頭',
 *      search: 'query string,?開頭',
 *      hash: '#開頭的fragment identifier'
 * }
 *
 * @param {string} url 需要解析的url
 * @return {Object} 包含url資訊的物件
 */
 
 
 function parseUrl(url) {
    var result = {};
    var keys = ['href', 'origin', 'protocol', 'host',
                'hostname', 'port', 'pathname', 'search', 'hash'];
    var i, len;
    var regexp = /(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?/;

    var match = regexp.exec(url);
	 console.info('match=', match);
	 
    if (match) {
        for (i = keys.length - 1; i >= 0; --i) {
            result[keys[i]] = match[i] ? match[i] : '';
        }
    }
	 console.info('result=', result);
    return result;
}

parseUrl("http://test.com:8080?name=1&password=2#page1");

結果:
match=[ 
  'http://test.com:8080?name=1&password=2#page1',
  'http://test.com:8080',
  'http:',
  'test.com:8080',
  'test.com',
  ':8080',
  undefined,
  '?name=1&password=2',
  '#page1',
  index: 0,
  input: 'http://test.com:8080?name=1&password=2#page1' 
]

result={ 
  hash: '#page1',
  search: '?name=1&password=2',
  pathname: '',
  port: ':8080',
  hostname: 'test.com',
  host: 'test.com:8080',
  protocol: 'http:',
  origin: 'http://test.com:8080',
  href: 'http://test.com:8080?name=1&password=2#page1' 
}
複製程式碼

沒錯,一眼就看到了難以理解的是那段正規表示式:

/(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?/

複製程式碼

剛好最近看了精通正規表示式一書(的20%,哈哈看不完),剛好可以練練手了

1.匹配的陣列長度為11個,為什麼

2.每一個是怎麼匹配來的

首先,在分析之前,先給大家補補基礎概念,知道的就跳過

?:  匹配0個或一個
*   匹配0個或多個,
+:  一次或多次,至少出現一次 
.*  貪婪匹配:在滿足匹配時,匹配儘可能長的字串,預設情況下,採用貪婪匹配(沒有問號)

// 非貪婪匹配:在滿足匹配時,匹配儘可能短的字串,使用?來表示非貪婪匹配
?  非貪婪,最小匹配(重點,後面會用到)
*? 重複任意次,但儘可能少重複  
+? 重複1次或更多次,但儘可能少重複  
?? 重複0次或1次,但儘可能少重複  
{n,m}? 重複n到m次,但儘可能少重複  
{n,}? 重複n次以上,但儘可能少重複  

//環視
'jeffs'.replace(/(?<=jeff)(?=s)/i, '"')
//順序環視和逆向環視,?= 匹配的目標位置後緊跟s,匹配的目標位置前緊鄰jeff  jeff目標s,結果輸出:jeff"s

[^]: ^表示非 //示例:[^u]

//[]裡面的元字元只是普通字元
/03[-./]22/.test('03-22') 結果輸出:true

//() :標記一個子表示式的開始和結束位置。子表示式可以獲取供以後使用

複製程式碼

進入正題,匹配的陣列長度為11個,為什麼?

因為匹配結果是按照()的個數和順序決定的,重點是前面9個

最後兩個[index: 0,input: 'http://test.com:8080?name=1&password=2#page1']是regexp.exec函式自帶返回的

所以上面的表示式可以拆分為如下子表示式:

(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))(\/[^?#]*)?(\?[^#]*)?(#.*)?
(([^:]+:)\/\/(([^:\/\?#]+)(:\d+)?))
([^:]+:)
(([^:\/\?#]+)(:\d+)?)
([^:\/\?#]+)
(:\d+)?
(\/[^?#]*)?
(\?[^#]*)?
(#.*)?
複製程式碼

具體分析見下圖:

正規表示式分析

思考題:

正規表示式分割之千分符格式:
213435324.099.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
複製程式碼

今天就到這裡,喜歡記得點贊~

相關文章