首頁 >web前端 >js教程 >JavaScript拆分字串時產生空白字元的解決方案_javascript技巧

JavaScript拆分字串時產生空白字元的解決方案_javascript技巧

WBOY
WBOY原創
2016-05-16 16:35:131647瀏覽

問題描述

使用JavaScript的split方法拆分字串時出現一些空字串"",尤其是當使用正規表示式作為分隔符號的時候。

相關問題

javascript正規表示式對字串分組時產生空字串群組?

在上面這個問題中,題主使用正規表示式對字串進行分割時產生了多個空字串"",程式碼如下:

複製程式碼 程式碼如下:

'張sdf四上法asdf翁芬aa33網s'.split(/([u4e00-u9fa5]{1})/gi);
//輸出["", "張", "sdf", "四", "", "上", "", "法", "asdf", "翁", "", "芬", "aa33 ", "網", "s"]

 那麼,產生這些空字串的原因是什麼?

問題分析

在Google上搜尋了一番,發現相關的結果並不多,即便有,詳細解釋的也不多,大概的說了一下,然後就給出了一個ECMAScript規範的連結。看來要知道真正的原因,就只能硬著頭皮看規範了。

相關標準

那麼,接下來,依照國際慣例,先上ECMAScript的標準鎮樓。

複製程式碼 程式碼如下:

String.prototype.split (separator, limit)

這個章節詳細介紹了split方法的執行步驟,如果感興趣的話可以一步一步的認真看完,我在這裡只把和產生空字符串相關的步驟拿出來解釋一下,不當之處,歡迎大家提出。

相關步驟

摘取部分步驟:

整個過程中最主要的步驟就是第13步這個循環,而這個循環主要做的事情如下:
 •定義p, q的值,每一次循環開始的時候p和q的值是相同的(該步驟在循環之外);
 •呼叫SplitMatch(S, q, R)這個方法對字串進行拆分;
 •根據回傳結果的不同,執行不同的分支,主要分支為分支ⅲ;
 •分支ⅲ又分成了8個小步用來將回傳的結果填入事先定義好的陣列A中
•在這個8小步驟中,步驟1的作用是傳回原始字串的一個子字串,起始位置是p(包含在內),結束位置是q(不包含在內),注意:在這一步驟中會產生空字串,我將其標記為截取字串,方便下文引用。
 •將上一步驟的子字串加到陣列A中
 •接下來的幾步是更新相關的變量,繼續下一次循環。 (步驟7的作用是將正規表示式中的捕獲分組保存到陣列A中,和產生空字串無關)
 
SplitMatch(S, q, R)

接下來,我們需要了解SplitMatch(S, q, R)這個方法做了些什麼。這個方法在split規範的下方有提及。它主要做的事是,根據分隔符號(separator)的類型進行相應的操作:
 •如果分隔符號是RegExp類型的,呼叫RegExp的內部方法[[Match]]來對字串進行匹配,如果匹配失敗,返回failure,否則,返回一個MatchResult類型的結果。
 •如果分隔符號是字串,進行比對判斷,失敗回傳failure,成功回傳MatchResult類型的結果。
 
MatchResult

上面的步驟中又引出了一個MatchResult類型的變數。透過查文檔發現,該類型的變數有兩個屬性endIndex和captures,endIndex的值是字串匹配的位置加上1,captures可以理解為一個數組,當分隔符號為正規表示式時,它裡面的元素是分組捕獲的值;當分隔符號為字串時,它為一個空數組。

接下來

我們從上面的步驟可以看出,分割的字串是在截取字串這一步驟中產生的(正規表示式的分組捕獲除外)。它的作用是截取指定開始(包含在內)和結束位置(不包含在內)之間的字串,那它什麼時候會回傳""呢?有一種特殊情況是開始位置和結束位置的值相等,這只是猜想而已,因為該規範沒有給出截取字串的規範步驟。

都走到這裡了,為什麼不再往前走一步呢?

於是,我試著搜尋了一些V8的源碼,看看能不能找到具體的實作方法。確實找到了相關的程式碼,原始碼連結

這裡摘取其中一部分:

複製程式碼 程式碼如下:

function StringSplitJS(separator, limit) {
  ...
  ...
  //分隔符號是字串的情況
  if (!IS_REGEXP(separator)) {
    var separator_string = TO_STRING_INLINE(separator);

    if (limit === 0) return [];

    // ECMA-262 says that if separator is undefined, the result should
    // be an array of size 1 containing the entire string.
    if (IS_UNDEFINED(separator)) return [subject];

    var separator_length = separator_string.length;

    //分隔符號是空字串,直接回傳了字元數組
    if (separator_length === 0) return %StringToArray(subject, limit);

    var result = %StringSplit(subject, separator_string, limit);

    return result;
  }

  if (limit === 0) return [];

  // 分隔符號是正規表示式的情況,呼叫StringSplitOnRegExp
  return StringSplitOnRegExp(subject, separator, limit, length);
}

//此處省略若干程式碼

 我在程式碼中發現,在填滿陣列的時候會呼叫%_SubString這個方法來截取字串,可惜的是我沒有找到他的相關定義,如果有找到的同學歡迎告知。但是,我發現JavaScript中substring這個方法所對應的StringSubstring這個方法會呼叫%_SubString這個方法,並將其結果傳回。那如果'abc'.substring(1,1)回傳"",則表示%_SubString這個方法在起始位置和結束位置相同的時候會回傳"",結果大家一試便知。

那麼,什麼時候會出現起始位置等於結束位置(即q === p)的情況呢?我按照上面的步驟一步一步的分析,最後發現:
 •當原始字串S符合過一次分隔符號之後,緊接著,字串S的下一個位置還符合分隔符號。如:'abbbc'.split('b'),'abbbc'.split(/(b){1}/)
 •另一種情況是字串開頭的一個或幾個字元匹配分隔符號。如:'abc'.split('a'),'abc'.split(/ab/)
•還有一種情況是字串結尾的一個或幾個字串匹配分隔符,與之相關的步驟是第14步。
 如:'abc'.split('c'),'abc'.split(/bc/)

此外,當使用正規表示式作為分隔符號的時候,傳回的結果中還有可能出現undefined。
 如:'abc'.split(/(d)*/)

回過頭來再看看開頭的那個例子,是不是滿足上面幾種情況?

題外話

這是我第一次這麼仔細的看ECMAScript的標準規範,看的過程確實很痛苦,但明白之後就感覺很痛快了。也感謝題主提出的這個問題,以及追問。
 順便提一句,正規表示式作為分隔符號時,global修飾符g是會被忽略的,這也算是額外的收穫。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn