走近正則:仿Nodejs的Url模塊到前端

正則學起來

說真的,不去正兒八經的學正則,對付通常的工做是沒啥問題的,雖然咱們可能會常常用到replace,但畢竟度娘能提供大多時候你想要的;可當我看一些框架的源碼,總會被裏面一長串一長串的正則給嚇到;以前一篇博客裏有關於簡單的爬蟲實踐,其實離達到我預期的效果就差一批正則(Nodejs,不同的爬蟲實踐);因此,通常的正則毫無壓力,非通常的狀況下的正則,使人望而生畏;簡直就是語言界的一朵小奇葩!html

能本身動手,就不勞煩度娘了

正則其實就是用一串字符去描述一套規則,因此,首先得自個兒很清楚要實現的正則具有哪些規則,而後用一串字符組合搭配去描述它;如下正則,能力有限,暫不考慮性能問題,僅供參考;前端

一、分組

指定一個子表達式,用小括號包括起來;node

參與結果的分組:結果保存在$1-$9中:瀏覽器

'abc-123'.replace(/(\w+)-(\d+)/,'$2-$1');
// '123-abc'

不參與結果的分組:(?:reg) reg參與匹配規則,匹配到的結果不參與分組的結果框架

var str1='abc123';
var str2='abc';
var reg=/\w+(?:\d+)?/;
reg.test(str1);    // true
reg.test(str2);    // true

反向引用:對相同的分組簡寫爲對第一個分組結果的引用性能

// 匹配h1-h6的標籤
var reg=/<h[1-6]>.*?<\/h[1-6]>/i;

'<h1>hhhhh1111</h1>'.test(reg)   // true
'<h2>hhhhhaaaa</h5>'.test(reg)   // true

// 若是像這樣確實能夠匹配到通常的h1-h6,
// 但其實它也會匹配<h1>....</h6>這種先後不對等的狀況,
// 使用分組,反向引用就能避免這種狀況

var reg=/<h([1-6])>.*?<\/\1>/i;
// \1便是對第一個分組([1-6])的引用

'<h1>hhhhh1111</h1>'.test(reg)   // true
'<h2>hhhhhaaaa</h5>'.test(reg)   // false

二、貪婪模式與非貪婪模式:

貪婪模式與非貪婪模式隻影響用次數修飾的子表達式,貪婪模式在整個表達式匹配成功後會繼續向後儘量多的匹配,非貪婪模式在表達式首次匹配成功後即返回結果;?除了表示匹配0次或1次以外,還有在次數修飾的子表達式後面表示是否啓用貪婪模式;this

// 過濾html標籤
var str1='<b class="aa" data-value="1">abc</b><div class="a">cde</div>hf';
var reg=/<\/?\w+.*>/ig;      // 貪婪模式
var reg1=/<\/?\w+.*?>/ig;  // 非貪婪模式

str1.replace(reg,'');                  // hf
str1.replace(reg1,'');                // abccdehf

// 能夠看到非貪婪模式下的結果纔是咱們過濾標籤後想要的結果

三、斷言

在JS裏只支持向前斷言,也就是向前查找;url

(?=reg) 零寬正向先行斷言

(?!reg) 零寬負向先行斷言

這兩種斷言裏的reg只做爲整個表達式的一個必要條件,而不返回匹配結果,一樣不能與分組弄混淆;通常小括號用來劃分一個子表達式,(?:reg)的reg匹配的結果不參與整個表達式結果的分組;斷言的reg只做爲一個必要條件,參與整個表達式匹配的判斷,匹配的內容卻不參與整個表達式匹配的結果;spa

向前查找即從斷言開始的位置的右邊開始匹配;code

var str='abc123cdef456...........';

// 匹配數字後面跟着字母的內容,
//「跟着字母」僅做爲一個條件,這個條件匹配到的結果不參與整個表達式匹配的結果
str.match(/\d+(?=[a-z]+)/g);     // 123 而不是 456
str.match(/\d+(?!\w+)/g);          // 456

四、其餘

其餘基礎如:s S w W b B d D [0-9a-zA-Z_] 等基礎符號組合應該相對好理解,做爲很基礎的部分,必須牢記咯;

用正則仿一把Nodejs的Url模塊

其實絕大多數人都用過正則,但我更相信大多數人用的正則都是度娘提供的,爲了真正的走近正則,而不僅是道聽途說,必須全面的過一遍,而且儘量的用到實際工做中,好比:驗證個url,過濾個標籤等,相信本身弄出來一串字符的話,確定別有一番意思;以下Url模塊,對於前端來講基本上是多餘的,由於瀏覽器有location;parse方法的做用在於對非location情形下的自定義url或可認爲是url的字符串進行解析;好比:用戶輸入一串url,首先驗證是否符合url規則,而後提取協議類型,提取域名等,這些就是location作不到的,並且對於簡單的爬蟲操做替換或補全url,該方法就派上用場了;

Url.parse(url);//返回一個object

若是不帶url參數,則默認爲location.href;這時返回的結果和location能取到的同樣;

若是帶上url,不管是否自定義的,都將從新用正則解析,並返回一個和location能獲取到的同樣的結果集,包括格式化後的query;

Url.normalize(url);//返回url

首先對接收的url去空白字符,嘗試將多個連續的/符合替換爲一個,將多個.去掉(../或./的狀況),至關於放寬url的限定規則;

Url.isAccessUrl(url);//返回true|false

驗證是否符合正確的url規範;

Url.query;//返回 object

取得url上的查詢參數,{name:value}格式,如:Url.query.id取得url上id=123的值即返回123;

var Url=function(loc){
    var urlUtils=function(){
        return {
            trim:function(url){
                return url.replace(/^\s+|\s+$/,'');
            },
            norProtocol:function(url){
                if (!/https?\:\/\//i.test(url)) throw url+' http or https protocol needed !';
                return url.replace(/(https?\:\/\/)\/+/,'$1');
            },
            getProtocol:function(url){
                return this.norProtocol(url).match(/(https?\:\/\/)/)[1];
            },
            tryOnce:function(url){
                return this.getProtocol(url)+
                       this.norProtocol(url).substring(this.getProtocol(url).length)
                       .replace(/\.+(?=\/)/g,'')
                       .replace(/\/+/g,'/');
            },
            normalize:function(url){
                var nor=this.tryOnce(this.trim(url));
                return nor.match(/https?\:\/\/(\w+(?:\.\w+)+)(\:\d{2,5})?$/)?nor+='/':nor;
            },
            isAccessUrl:function(url){
                return /https?\:\/\/(?:\w+(?:\.\w+)+|localhost)(?:\:\d{2,5})?\/\S*/i.test(this.normalize(url));
            },
            convertQs:function(qs){
                var qs=qs||loc.search.substring(1);
                if(!qs) return null;
                var qsArr=qs.split(/&/),qsPar={};
                for(var i=0;i<qsArr.length;i++){
                    if (qsArr[i]) {
                        var nm=qsArr[i].split(/=/);
                        qsPar[decodeURIComponent(nm[0])]=decodeURIComponent(nm[1]||'');
                    }
                }
                return qsPar;
            }
        }
    }();
    function parse(url){
        var url=url||loc.href;
        var noredUrl=urlUtils.normalize(url);
        if (!urlUtils.isAccessUrl(url)) throw url+' is not access URI !';
        var medUrl=noredUrl.match(/https?\:\/\/(\w+(?:\.\w+)+|localhost)(?:\:(\d{2,5}))?(\/(?:[^#?]+)?)(?:\?([^#]+))?(?:#{1}(.+))?/i);
        console.log(medUrl)
        return {
            href:medUrl[0],
            protocol:urlUtils.getProtocol(noredUrl).replace(/\/\//,''),
            query:urlUtils.convertQs(medUrl[4]),
            host:medUrl[1],
            port:medUrl[2]||'80',
            pathname:medUrl[3],
            search:medUrl[4]||null,
            hash:medUrl[5]||null
        };
    }
    return {
        parse:parse,
        normalize:urlUtils.normalize,
        isAccessUrl:urlUtils.isAccessUrl,
        query:urlUtils.convertQs(null)      //{name:value}
    }
}(location);

module.exports=Url;
如:console.log(Url.parse('http:////www.famanoder.com/../dsvdsk?ds=w3q&&cdsvds=00   '));

將獲得以下結果:

圖片描述

正則研究的不太深,該模塊不免會有BUG,或繁瑣的地方,我的認爲在實際工做中仍是有用武之地的,後續慢慢修復吧;

正則這朵小奇葩仍是挺有意思的,相信更加熟練後在之後的前端之路上會帶給我意想不到的收穫!

若是你已在路上,就勇敢的向前吧!

原文來自:花滿樓(http://www.famanoder.com/boke...

相關文章
相關標籤/搜索