JavaScript拆分字串時產生Null 字元怎麼解決?

來源:互聯網
上載者:User

   一、問題描述

  使用JavaScript的split方法拆分字串時出現一些Null 字元串"",尤其是當使用Regex作為分隔字元的時候。

 

  二、相關問題

  javascriptRegex對字串分組時產生Null 字元串組?

  在上面這個問題中,題主使用Regex對字串進行分割時產生了多個Null 字元串"",代碼如下:

  代碼如下:

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

  //輸出["", "張", "sdf", "四", "", "上", "", "法", "asdf", "翁", "", "芬", "aa33", "網", "s"]

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

 

  三、問題分析

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

 

  四、相關標準

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

  代碼如下:

  String.prototype.split (separator, limit)

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

 

  五、相關步驟

  摘取部分步驟:

  整個過程中最主要的步驟是第13步這個迴圈,而這個迴圈主要做的事情如下:

  定義p, q的值,每一次迴圈開始的時候p和q的值是相同的(該步驟在迴圈之外);

  調用SplitMatch(S, q, R)這個方法對字串進行拆分;

  根據返回結果的不同,執行不同的分支,主要分支為分支ⅲ;

  分支ⅲ又分成了8個小步用來將返回的結果填充到事先定義好的數組A中

  在這個8小步中,步驟1的作用是返回原始字串的一個子串,開始位置是p(包含在內),結束位置是q(不包含在內),注意:在這一步中會產生Null 字元串,我將其標記為截取字串,方便下文引用。

  將上一步的子串添加到數組A中

  接下來的幾步是更新相關的變數,繼續下一次迴圈。(步驟7的作用是將Regex中的捕獲分組儲存到數組A中,和產生Null 字元串無關)

  SplitMatch(S, q, R)

  接下來,我們需要瞭解一下SplitMatch(S, q, R)這個方法做了些什麼事。這個方法在split規範中的下方有提及。它主要做的事是,根據分隔字元(separator)的類型進行相應的操作:

  如果分隔字元是RegExp類型的,調用RegExp的內部方法[[Match]]來對字串進行匹配,如果匹配失敗,返回failure,否則,返回一個MatchResult類型的結果。

  如果分隔字元是字串,進行匹配判斷,失敗返回failure,成功返回MatchResult類型的結果。

  MatchResult

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

  接下來

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

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

  於是,我試著搜尋了一些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;

  //分隔字元是Null 字元串,直接返回了字元數組

  if (separator_length === 0) return %StringToArray(subject, limit);

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

  return result;

  }

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

  // 分隔字元是Regex的情況,調用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/)

  此外,當使用Regex作為分隔字元的時候,返回的結果中還有可能出現undefined。

  如:'abc'.split(/(d)*/)

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

 

  六、題外話

  這是我第一次這麼仔細的看ECMAScript的標準規範,看的過程確實很痛苦,但明白之後就感覺很痛快了。也感謝題主提出的這個問題,以及追問。

  順便提一句,Regex作為分隔字元時,global修飾符g是會被忽略的,這也算是一次額外的收穫。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.