Scala регулярные выражения: как вернуть совпадения в виде массива или списка - PullRequest
14 голосов
/ 14 января 2010

Есть ли простой способ вернуть совпадения с регулярным выражением в виде массива?
Вот как я пытаюсь в 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, m2 - 1 и т. д.

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

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

Это печатает:

non-empty iterator
1
empty iterator

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

Ответы [ 3 ]

27 голосов
/ 14 января 2010

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

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

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.

9 голосов
/ 14 января 2010

Существует разница между тем, как unapplySeq интерпретирует несколько групп, и тем, как выполняет 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))
2 голосов
/ 14 января 2010

Попробуйте это:

  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(mg@_*) => println(mg) // List(6, 1, 2)
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...