Scala regexps: как вернуть совпадения в виде массива или списка

Есть ли простой способ вернуть регулярные выражения в виде массива?
Вот как я пытаюсь в 2.7.7:

val s = """6 1 2"""
val re = """(\d+)\s(\d+)\s(\d+)""".r
for (m <- re.findAllIn (s)) println (m) // prints "6 1 2"
re.findAllIn (s).toList.length // 3? No! It returns 1!

Но я тогда попытался:

s match {
  case re (m1, m2, m3) => println (m1)
}

И это отлично работает! m1 - 6, м2 - 1 и т.д.

Затем я нашел кое-что, что добавило моей путанице:

val mit = re.findAllIn (s)
println (mit.toString)
println (mit.length)
println (mit.toString)

Что печатает:

non-empty iterator
1
empty iterator

Звонок "длина" каким-то образом изменяет состояние итератора. Что здесь происходит?

Ответ 1

Хорошо, прежде всего, понять, что findAllIn возвращает Iterator. Iterator - однообразный изменчивый объект. НИЧЕГО вы это сделаете, это изменит его. Читайте на итераторах, если вы не знакомы с ними. Если вы хотите, чтобы он был повторно использован, преобразовать результат findAllIn в List и использовать только этот список.

Теперь, кажется, вам нужны все соответствующие группы, а не все совпадения. Метод findAllIn вернет все совпадения полного регулярного выражения, которое можно найти в строке. Например:

scala> val s = """6 1 2, 4 1 3"""
s: java.lang.String = 6 1 2, 4 1 3

scala> val re = """(\d+)\s(\d+)\s(\d+)""".r
re: scala.util.matching.Regex = (\d+)\s(\d+)\s(\d+)

scala> for(m <- re.findAllIn(s)) println(m)
6 1 2
4 1 3

Посмотрите, что есть два совпадения, и ни один из них не содержит "," в середине строки, так как это не является частью какого-либо совпадения.

Если вы хотите группы, вы можете получить их так:

scala> val s = """6 1 2"""
s: java.lang.String = 6 1 2

scala> re.findFirstMatchIn(s)
res4: Option[scala.util.matching.Regex.Match] = Some(6 1 2)

scala> res4.get.subgroups
res5: List[String] = List(6, 1, 2)

Или, используя findAllIn, вот так:

scala> val s = """6 1 2"""
s: java.lang.String = 6 1 2

scala> for(m <- re.findAllIn(s).matchData; e <- m.subgroups) println(e)
6
1
2

Метод matchData сделает Iterator, который возвращает Match вместо String.

Ответ 2

Существует разница между тем, как unapplySeq интерпретирует группы mulitple и как работает findAllIn. findAllIn сканирует ваш шаблон над строкой и возвращает каждую строку, которая соответствует (продвигается по совпадению, если она преуспевает, или один символ, если она терпит неудачу).

Итак, например:

scala> val s = "gecko 6 1 2 3 4 5"
scala> re.findAllIn(s).toList
res3: List[String] = List(6 1 2, 3 4 5)

С другой стороны, unapplySeq предполагает идеальное соответствие последовательности.

scala> re.unapplySeq(s)
res4: Option[List[String]] = None

Итак, если вы хотите разбирать отдельные группы, которые вы указали в точной строке регулярного выражения, используйте unapplySeq. Если вы хотите найти те подмножества строки, которые выглядят как ваш шаблон регулярного выражения, используйте findAllIn. Если вы хотите сделать то же самое, соедините их самостоятельно:

scala> re.findAllIn(s).flatMap(text => re.unapplySeq(text).elements )
res5: List[List[String]] = List(List(6, 1, 2), List(3, 4, 5))

Ответ 3

Попробуйте следующее:

  val s = """6 1 2"""
  val re = """\d+""".r
  println(re.findAllIn(s).toList) // List(6, 1, 2)
  println(re.findAllIn(s).toList.length) // 3

И если вам действительно нужен список совпадающих групп в одном регулярном выражении:

  val s = """6 1 2"""
  val Re = """(\d+)\s(\d+)\s(\d+)""".r
  s match {  // this is just sugar for calling Re.unapplySeq(s)
      case Re([email protected]_*) => println(mg) // List(6, 1, 2)
  }