在Scala中,如何在空格上拆分字符串,以解释嵌入的引用字符串?

时间:2017-04-27 18:40:36

标签: regex scala

我知道Scala可以在正则表达式上拆分字符串,就像在空格上这样简单的拆分:

myString.split("\\s+").foreach(println)

如果我想拆分空格,考虑输入中可能有引号的字符串(我希望将其视为1件事),该怎么办?

"""This is a "very complex" test"""

在这个例子中,我希望得到的子串是:

This
is
a
very complex
test

4 个答案:

答案 0 :(得分:4)

虽然使用split处理带引号的表达式可能很棘手,但使用Regex匹配这样做非常容易。我们只需要将所有非空白字符序列与([^ \\ s] +)和所有引用的字符序列匹配为"(。*?)\" (添加toList以避免重复):

import scala.util.matching._

val text = """This is a "very complex" test"""
val regex = new Regex("\"(.*?)\"|([^\\s]+)")
val matches = regex.findAllMatchIn(text).toList
val words = matches.map { _.subgroups.flatMap(Option(_)).fold("")(_ ++ _) }

words.foreach(println)

/*
This
is
a
very complex
test
*/

请注意,该解决方案还将引用本身视为单词边界。如果要将引用的字符串内联到周围的表达式中,则需要从引用的案例的两边添加[^ \\ s] *并相应地调整组边界:

...
val text = """This is a ["very complex"] test"""
val regex = new Regex("([^\\s]*\".*?\"[^\\s]*)|([^\\s]+)")
...
/*
This
is
a
["very complex"]
test
*/

通过拆分正则表达式组来内联字符串时,也可以省略引号:

...
val text = """This is a ["very complex"] test"""
val regex = new Regex("([^\\s]*)\"(.*?)\"([^\\s]*)|([^\\s]+)")
...
/*
This
is
a
[very complex]
test
*/

答案 1 :(得分:1)

在更复杂的情况下,当您必须处理CSV字符串时,您最好使用CSV解析器(例如scala-csv)。

对于像这个问题的字符串,当你不必处理转义的引号时,也不需要处理任何" wild"出现在字段中间的引号,您可以调整已知的Java解决方案(请参阅Regex for splitting a string using space when not surrounded by single or double quotes):

val text = """This is a "very complex" test"""
val p = "\"([^\"]*)\"|[^\"\\s]+".r
val allMatches = p.findAllMatchIn(text).map(
    m => if (m.group(1) != null) m.group(1) else m.group(0)
)
println(allMatches.mkString("\n"))

请参阅online Scala demo,输出:

This
is
a
very complex
test

正则表达式相当基础,因为它包含2个备选方案,一个捕获组和一个否定字符类。以下是其详细信息:

  • \"([^\"]*)\" - ",其次是"以外的0 +字符(捕获到第1组),然后是"
  • | - 或
  • [^\"\\s]+ - 除"和空格以外的1个字符。

如果第1组参加了比赛,您只会抓取.group(1),否则,抓住整个匹配值(.group(0))。

答案 2 :(得分:0)

这应该有效:

override func viewWillAppear(_ animated: Bool) {

        super.viewWillAppear(animated)

        self.navigationController!.navigationBar.setBackgroundImage(UIImage(named:"mainhead.png"), for: UIBarMetrics.default)
    } 

答案 3 :(得分:0)

我使用递归方法而不是使用split。将输入字符串视为List[Char],然后单步执行,检查列表的头部以查看它是引号还是空格,并相应处理。

def fancySplit(s: String): List[String] = {
    def recurse(s: List[Char]): List[String] = s match {
        case Nil => Nil
        case '"' :: tail =>
            val (quoted, theRest) = tail.span(_ != '"')
            quoted.mkString :: recurse(theRest drop 1)
        case c :: tail if c.isWhitespace => recurse(tail)
        case chars =>
            val (word, theRest) = chars.span(c => !c.isWhitespace && c != '"')
            word.mkString :: recurse(theRest)
    }
    recurse(s.toList)
}
  • 如果列表为空,则表示您已完成递归
  • 如果第一个字符是",请抓住所有内容到下一个引号,然后用剩下的内容递归(在丢弃第二个引号后)。
  • 如果第一个字符是空格,则将其抛出并从下一个字符递归
  • 在任何其他情况下,抓住一切直到下一个分裂角色,然后用剩下的东西递归

结果:

scala> fancySplit("""This is a "very complex" test""") foreach println
This
is
a
very complex
test