【问题标题】:Scala: how to split using more than one delimiterScala:如何使用多个分隔符进行拆分
【发布时间】:2012-10-12 09:56:54
【问题描述】:

我想知道如何在 Scala 中使用多个分隔符拆分字符串。

例如,如果我有一个分隔符列表:

List("Car", "Red", "Boo", "Foo")

还有一个要收获的字符串:

Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed

我希望能够输出类似:

List(   ("Car", " foerjfpoekrfopekf "),
    ("Red", " ezokdpzkdpoedkzopke dekpzodk "),
    ("Foo", " azdkpodkzed")     
)

【问题讨论】:

    标签: list scala split


    【解决方案1】:

    您可以使用列表创建正则表达式并使用其拆分方法:

    val regex = List("Car", "Red", "Boo", "Foo").mkString("|").r
    regex.split("Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed")
    

    但是,这并没有告诉您在哪里使用了哪个分隔符。如果你需要,我建议你试试 Scala 的解析器库。

    编辑:

    或者您可以使用正则表达式一次提取一对,如下所示:

    def split(s:String, l:List[String]):List[(String,String)] = {
      val delimRegex = l.mkString("|")
      val r = "("+delimRegex+")(.*?)(("+delimRegex+").*)?"
      val R = r.r
      s match {
        case R(delim, text, rest, _) => (delim, text) :: split(rest, l)
        case _ => Nil
      }
    }
    

    【讨论】:

    • .mkString("\\Q", "\\E|\\Q","\\E") (希望)减少可能的正则表达式攻击的数量。不过,我不确定这是否有效。
    • 当然,如果列表来自某个外部来源,那么应该使用.map(java.util.regex.Pattern.quote)
    • 更有意义。不知道那个。
    【解决方案2】:

    有点冗长,但它有效:
    已弃用的版本:(它有一个错误,因为你已经接受了答案,所以把它留在这里)

    def f(s: String, l: List[String], g: (String, List[String]) => Int) = {
        for {
            t <- l
            if (s.contains(t))
            w = s.drop(s.indexOf(t) + t.length)
        } yield (t, w.dropRight(w.length - g(w, l)))
    }
    
    def h(s: String, x: String) = if (s.contains(x)) s.indexOf(x) else s.length
    
    def g(s: String, l: List[String]): Int = l match {
        case Nil => s.length
        case x :: xs => math.min(h(s, x), g(s, xs))
    }
    
    val l = List("Car", "Red", "Boo", "Foo")
    
    val s = "Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed"
    

    输出:

    f(s, l, g).foreach(println)
    > (Car, foerjfpoekrfopekf )
    > (Red, ezokdpzkdpoedkzopke dekpzodk )
    > (Foo, azdkpodkzed)
    

    它返回Array[String] 而不是列表。但你也可以这样做:f(s, l, g).toList

    编辑: 刚刚注意到如果分隔符仅在字符串中出现一次,则此代码很好。如果已将s 定义如下:

    val s = "Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed Car more..."
    

    我仍然会得到相同的结果,而不是另一对 ("Car"," more...")

    EDIT#2:BUGLESS 版本这是固定的 sn-p:

    def h(s: String, x: String) = if (s.contains(x)) s.indexOf(x) else s.length
    
    def multiSplit(str: String, delimiters: List[String]): List[(String, String)] = {
        val del = nextDelimiter(str, delimiters)
        del._1 match {
            case None => Nil
            case Some(x) => {
                val tmp = str.drop(x.length)
                val current = tmp.dropRight(tmp.length - nextDelIndex(tmp,delimiters))
                (x, current) :: multiSplit(str.drop(x.length + current.length), delimiters)
            }
        }
    }
    
    def nextDelIndex(s: String, l: List[String]): Int = l match {
        case Nil => s.length
        case x :: xs => math.min(h(s, x), nextDelIndex(s, xs))
    }
    
    def nextDelimiter(str: String, delimiters: List[String]): (Option[String], Int) = delimiters match {
        case Nil => (None, -1)
        case x :: xs => {
            val next = nextDelimiter(str, xs)
            if (str.contains(x)) {
                val i = str.indexOf(x)
                next._1 match {
                    case None => (Some(x), i)
                    case _ => if (next._2 < i) next else (Some(x), i)
                }
            } else next
        }
    }
    

    输出:

    multiSplit(s, l).foreach(println)
    > (Car, foerjfpoekrfopekf )
    > (Red, ezokdpzkdpoedkzopke dekpzodk )
    > (Foo, azdkpodkzed)
    > (Car, more...)
    

    现在它可以工作了:)

    【讨论】:

    • 非常感谢!这正是我想要做的
    • 读起来很糟糕,而且——正如已经证明的——容易出错
    • @KimStebel 欢迎您进行编辑并使其更具可读性,但我认为答案很好而且很清楚。我知道 initial 版本容易出错。我演示了它...但我声明它已加载并清楚,它是一个已弃用的版本。在这种情况下,否决票只是拖钓……(我只能假设是你。如果不是,请原谅错误的指控。)
    • downvote 不是针对错误,而是针对错误的方法。我认为手动实现解析器通常不是一个好主意。现在可能相当简单,但如果数据格式发生变化,则很难维护。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-17
    • 2019-05-05
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多