记一次正则表达式问题

376 阅读4分钟
    本人前端小白一枚,写文章只是为了记录自己学习过程中的疑问,大佬可直接忽略。 最近在leetcode刷一些简单的算法题,今天遇到一道字符串翻转相关的题目,题目为:

反转字符串中的元音字母

编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

 示例 1: 输入: "hello" 输出: "holle" 

示例 2: 输入: "leetcode" 输出: "leotcede" 

说明: 元音字母不包含字母"y"。 

 我的思路是先把字符串中的元音字母找出来,然后翻转插入一个新的字符串中。

第一步:找出元音字母

对于给定字符串s,利用正则表达式筛选出元音字母,

写出正则表达式

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'

为什么字符串通过下标无法修改,而赋值可以“修改”,上面看起来是赋值修改了字符串,实际上并没有被修改,

字符串是基本数据类型, 基本数据类型的值一旦创建,便无法修改

创建字符串的时候,实际上在栈内开辟了一块内存空间来存放该字符串值,内存空间大小在初始化时已经固定了,无法修改。

进行赋值时,实际上开辟了另一块内存空间来存放要赋值的新值,将变量指向了这个栈空间。

而原来的字符串值内存会被垃圾回收机制回收,因为没有变量指向这个字符串了。


总结

自己的基础还是太薄弱了啊,看似一道小小的算法题,牵扯出这么多小的知识点,都是自己不懂了,慢慢加油吧。~~~