記一次正則表達式問題

    本人前端小白一枚,寫文章只是爲了記錄本身學習過程當中的疑問,大佬可直接忽略。 最近在leetcode刷一些簡單的算法題,今天遇到一道字符串翻轉相關的題目,題目爲:

反轉字符串中的元音字母

編寫一個函數,以字符串做爲輸入,反轉該字符串中的元音字母。javascript

 示例 1: 輸入: "hello" 輸出: "holle" 前端

示例 2: 輸入: "leetcode" 輸出: "leotcede" java

說明: 元音字母不包含字母"y"。 正則表達式

 個人思路是先把字符串中的元音字母找出來,而後翻轉插入一個新的字符串中。
算法

第一步:找出元音字母

對於給定字符串s,利用正則表達式篩選出元音字母,express

寫出正則表達式api

var reg = /['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']/數組

使用上述正則,利用s.match(reg)去匹配,發現每次只能匹配一個,且',也會被匹配上瀏覽器

修正: var reg = /aeiou/ig函數

s.match(reg)會返回一個匹配結果的數組,示例:

'leetcode'.match(reg) // ['e', 'e', 'o', 'e']

第二步:依次檢測字符串的元素是否匹配正則,匹配則用第一步中的數組的逆序去替換,代碼以下:

var reg = /[aeiou]/ig
var arr = s.match(reg)
if(!arr) return s;  // 結果爲null,則無元音字母
var res = '' // 結果字符串
var index = arr.length - 1 // 元音數組索引,倒序省去reverse操做
while(i < s.length) {
  if(reg.test(s[i])) { // 若是是元音字母
    res += arr[index] // 替換
    index += 1
  } else {
    res += s[i]
  }
    i += 1;
}

複製代碼

代碼看上去沒什麼問題,可是在瀏覽器中運行卻怎麼都不成功


如上圖,指望的結果應該是'leotcede'....................

       反覆看了一個多小時,最後發現是reg.test() 的問題,這個api有點坑爹,當reg中的檢索是全局檢索時,即加了g修飾符,test()若是連續使用結果會不一樣,舉個栗子:


如上圖所示,沒有全局修飾符和加上全局修飾符時檢索結果徹底不一樣。

經過查閱MDN:

若是正則表達式設置了全局標誌, test() 的執行會改變正則表達式 lastIndex屬性。連續的執行 test()方法,後續的執行將會從 lastIndex 處開始匹配字符串,( exec() 一樣改變正則自己的 lastIndex屬性值).

原來是lastIndex搞得鬼,由於使用時我都是對單個字符去匹配,因此若是匹配到了的時候,lastIndex就會變爲1,下一個若是出現連續的匹配,就會沒法匹配成功。

解決辦法

咱們在每一次匹配成功時重置lastIndex就行了,即設置reg.lastIndex = 0

因此最終代碼爲:

var reverseVowels = function(s) {
    var reg = /[aeiou]/ig
    var arr = s.match(reg)
    if(!arr) return s;
    var res = ''
    var index = arr.length - 1
    var i = 0
    while(i < s.length) {
        if(reg.test(s[i])) {
            res += arr[index]
            reg.lastIndex = 0 // 重置lastIndex
            index -= 1
        } else {
            res += s[i]
        }
        i += 1;
    }
    return res;
};複製代碼

但迴歸開頭,這是算法題,我寫出的這個方法並非優質解法,好吧,看了大佬的解法後,能夠用雙指針來解決這個問題。

思路以下:

題目的要求是翻轉字符串中的元音字母,因此咱們只要找出頭尾的元音字母依次交換就行了,根據大佬的思路寫出以下代碼:

var reverseVowels = function(s) {
    var reg = /[aeiou]/i
    var i = 0
    var len = s.length - 1
    while(i < len) {
        if(reg.test(s[i]) && reg.test(s[len])) { // 首尾都爲元音,交換
            [s[i], s[len]] = [s[len], s[i]]
            i += 1;
            len -= 1;
        } else if(!reg.test(s[i])) { // 首不是元音,比較下一位
            i += 1;
        } else if(!reg.test(s[len])) { // 尾不是元音,比較上一位
            len -= 1;
        }
    }
    return s;
};複製代碼

運行的時候發現s並無被改變,???這是爲何,又是一個坑人的問題,明明一切看起來是那麼有道理。。百思不得其解,後來發現問題就出在字符串,字符串一旦初始化是沒法被修改的,而我卻使用了[s[i], s[len]] = [s[len], s[i]] 去試圖修改字符串。舉個栗子:

var str = 'str'
str[0] = 'a'
str // 'str'

str = 'aaa'
str // 'aaa'複製代碼

爲何字符串經過下標沒法修改,而賦值能夠「修改」,上面看起來是賦值修改了字符串,實際上並無被修改,

字符串是基本數據類型, 基本數據類型的值一旦建立,便沒法修改

建立字符串的時候,實際上在棧內開闢了一塊內存空間來存放該字符串值,內存空間大小在初始化時已經固定了,沒法修改。

進行賦值時,實際上開闢了另外一塊內存空間來存放要賦值的新值,將變量指向了這個棧空間。

而原來的字符串值內存會被垃圾回收機制回收,由於沒有變量指向這個字符串了。


總結

本身的基礎仍是太薄弱了啊,看似一道小小的算法題,牽扯出這麼多小的知識點,都是本身不懂了,慢慢加油吧。~~~

相關文章
相關標籤/搜索