js使用一种方法搞定所有的字符串截取:突破substring的限制

567 阅读5分钟

字符串是最基本的数据类型,它有三种最常见的操作:拼接、分隔、截取,今天我们就来说说字符串的截取。

概述

说到字符串的截取,我们可能用到最多的就是substring了,它确实给我们提供了便利,但是用的多了你会发现,其实它也有很多的限制,让你在用的时候总是感觉力不从心。

无力

有没有一种方法,在我截取字符串的时候想怎么弄就怎么弄,而不用去写一堆的辅助代码?

这就是今天我们要介绍的,让你写起代码开心的就像个十八岁的孩子。

准备

我们考虑一下substring的输入和输出,接收两个参数,都是字符串的索引位置,返回这两个索引之间的字符串,可以交换顺序,但是不能传入负数,并且不能按照指定的方向返回。

更不用说它无法传入字符串等等。

我们要实现多参数、多类型的调用,首先想到的就是:重载。

这里我用一个之前我的文章里面的一段代码,用来实现方法重载。

首先,最基本的,我们先用一个数值型参数来截取。

一个数值索引参数

先把我们重载方法引入进来,然后简单实现一下:

import { Overload } from '@/utils/tools/function.js'
const take = Overload()
take.load.n = function(ind: number) {
  if(ind < 0) {
    return this.substring(this.length + ind)
  }
  return this.substring(ind)
}
String.prototype.take = take

利用Overload生成一个重载方法,命名为take,表示我们的截取方法。

然后定义它的第一个重载,其中load.n表示重载一个只有一个参数并且为数值型的方法。

最后将它赋值给字符串构造函数的原型上。

如果传入负数,就从后面截取,否则正常截取:

let a = '1234567'
console.log(a.take(4))
console.log(a.take(-3))

看下输出结果:

结果

现在我们继续扩展,可以输入两个数值型参数。

两个数值索引参数

这里就区分了几种情况:都是正数、都是负数、一正一负、前面的大、后面的大。

并且我们希望,可以指定返回字符串的顺序,如果索引1<索引2,那么正常返回,如果索引1>索引2,那么按逆序返回。

这时候我们继续重载一下,然后做一些判断:

take.load.nn = function (ind1: number, ind2: number) {
  if(ind1 < 0) {
    ind1 = this.length + ind1
    if(ind2 < 0) {
      ind2 = this.length + ind2
      if(ind1 > ind2) {
        let s = ''
        while(ind1 > ind2) {
          s += this.charAt(ind1)
          ind1--
        }
        return s
      }else {
        this.substring(ind1, ind2)
      }
    }else{
      return this.substring(ind1) + this.substring(0, ind2)
    }
  }
  if(ind2 < 0) {
    ind2 = this.length + ind2
    let s = ''
    while(ind1 > -1) {
      s += this.charAt(ind1)
      ind1--
    }
    let last = this.length - 1
    while(ind2 < last) {
      s += this.charAt(last)
      last--
    }
    return s
  }
  if(ind1 < ind2) {
    return this.substring(ind1, ind2)
  }else{
    let s = ''
    while(ind1 > ind2) {
      s += this.charAt(ind1)
      ind1--
    }
    return s
  }
}

来测试一下,假设我们有这样一些数据:

let a = '1234567'
console.log(a.take(2,5))
console.log(a.take(5,3))
console.log(a.take(-2,-5))
console.log(a.take(-4,-2))
console.log(a.take(3,-2))
console.log(a.take(2,-3))
console.log(a.take(-4,2))
console.log(a.take(-2,4))

列举出了多种情况,比如第4行,第一个索引是-2,第二个索引是-5,由于索引1>索引2,因此它应该按照逆序返回,而第8行,从倒数第4个数一直截取到正数第3(索引是2)个数。

看下结果:

结果

其中第3、4、6、7都是返回的逆序字符串。

这样我们就能够传入任意的位置,来方便的截取子串,并且能控制返回结果的顺序。

再考虑这样一种场景,我们能不能根据指定的字符串返回结果呢?也就是不关心索引,只要找到某个字符串就返回呢?

我们来尝试一下。

一个字符串参数

同样,我们继续来重载一下:

take.load.s = function(str: string) {
  return this.take(this.indexOf(str))
}

这里的load.s表示重载一个只有一个参数并且为字符串型的方法。

它找到这个字符串的位置,并一直截取到最后,看个例子:

let a = 'abcd.docx'
console.log(a.take('.'))

这是一个非常常见的获取文件后缀名的方法,这样我们就可以得到它的值:

结果

那如果它包含多个点呢?没关系,我们再写一个重载,来指定是否从后面截取。

一个字符串,一个布尔值

当第二个参数为false时,从后面开始截取,默认为true:

take.load.sb = function(str: string, head: boolean = true) {
  if(head) {
    return this.take(this.indexOf(str))
  }
  return this.take(this.lastIndexOf(str))
}

其中load.sb表示重载一个只有两个参数并且第一个为字符串型,第二个为布尔型的方法。

那么这时即使字符串中包含多个点,也没有关系:

let a = 'abcd.efgh.docx'
console.log(a.take('.'))
console.log(a.take('.', false))

来看下结果:

结果

可以看到非常符合预期。

既然可以这样,那么我们可不可以传入两个字符串,截取它们之间的子串呢?当然可以,你很聪明。

两个字符串参数

跟两个数值型参数一样,我们不但要根据字符串截取子串,并且能够根据它们的位置和传入顺序,来决定返回的子串的顺序:

take.load.ss = function (str1: string, str2: string) {
  let ind1
  if(!str1){
    ind1 = 0
  }else{
    ind1 = this.indexOf(str1)
  }
  if(ind1 === -1) {
    ind1 = this.length
  }
  let ind2
  if(!str2){
    ind2 = this.length
  }else{
    ind2 = this.indexOf(str2)
  }
  if(ind2 === -1) {
    ind2 = this.length
  }
  return this.take(ind1, ind2)
}

这样我们就重载了一个可以传入两个字符串的方法,并且给它加了一些判断,第一个参数为空,默认从第一个字符串开始,第二个参数为空,默认截取到最后一个字符。

比如我们看一个它的截取qq号的例子:

let a = '123456@qq.com'
console.log(a.take("", "@"))

这样不用管qq号是多少位的,都能直接提取出来:

结果

我们也可以提取邮箱的后缀:

let a = '123456@163.com'
console.log(a.take("@", ""))

结果

或者邮箱的种类:

let a = '123456@souhu.com'
console.log(a.take("@", "."))

结果

既然到了这一步,那么我们不禁又想,那能不能数字和字符串混合输入呢?聪明如你,一定也想到了,那完全没问题。

一个字符串,一个数值

字符串用来匹配字符,数值用来指定索引。

take.load.sn = function(str: string, ind: number) {
  let ind1 = this.indexOf(str)
  return this.take(ind1, ind)
}

load.sn表示第一个参数为字符串,第二个参数为数值。

我们同样来考虑数值为正负值的情况:

let a = 'hello world'
console.log(a.take('o', 8))
console.log(a.take('w', -1))

结果

一个数值,一个字符串

不用多说:

take.load.ns = function(ind: number, str: string) {
  let ind1 = this.indexOf(str)
  return this.take(ind, ind1)
}

来个例子:

let a = 'hello world'
console.log(a.take(2, 'w'))
console.log(a.take(-5, 'o'))

结果

由于o的索引为4,因此-5到4共有9个字符,所以第3行不会到末尾就停止,会继续从头开始找。

相信看到这里,你应该就对开头那段代码的输出结果了然于胸了,可以说整个字符串的截取都是通过take方法来实现的,利用了重载的方式,我们根据不同的参数类型,进行了不同的处理,并且扩展了substring所不能支持的功能,

最后

通过一个函数,我们实现了很多的字符串截取方式,使得我们对于字符串的操作更加灵活,其实这里还能扩展更多的截取方法,比如像Python的切片一样,可以跳步截取,也可以灵活的指定顺序或者逆序,还能扩展出正则匹配的方式进行截取。

不光是字符串截取,字符串的分隔、插入、大小写转换等等都可以以此类推。

只要能提升我们的效率,你想怎么玩那就怎么玩!

谢谢