[regex] Scala에서 정규 표현식을 사용하여 패턴 일치를 수행하는 방법은 무엇입니까?

단어의 첫 글자와 “ABC”와 같은 그룹의 글자 중 하나가 일치하는 항목을 찾고 싶습니다. 의사 코드에서 이것은 다음과 같이 보일 수 있습니다.

case Process(word) =>
   word.firstLetter match {
      case([a-c][A-C]) =>
      case _ =>
   }
}

하지만 Java 대신 Scala에서 첫 글자를 잡는 방법은 무엇입니까? 정규식을 올바르게 표현하려면 어떻게해야합니까? 케이스 클래스 내에서이 작업을 수행 할 수 있습니까?



답변

정규식이 추출기를 정의하기 때문에이를 수행 할 수 있지만 먼저 정규식 패턴을 정의해야합니다. 나는 이것을 테스트하기 위해 Scala REPL에 액세스 할 수 없지만 이와 같은 것이 작동합니다.

val Pattern = "([a-cA-C])".r
word.firstLetter match {
   case Pattern(c) => c bound to capture group here
   case _ =>
}


답변

버전 2.10부터 Scala의 문자열 보간 기능을 사용할 수 있습니다.

implicit class RegexOps(sc: StringContext) {
  def r = new util.matching.Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*)
}

scala> "123" match { case r"\d+" => true case _ => false }
res34: Boolean = true

더 좋은 방법은 정규식 그룹을 바인딩 할 수 있습니다.

scala> "123" match { case r"(\d+)$d" => d.toInt case _ => 0 }
res36: Int = 123

scala> "10+15" match { case r"(\d\d)${first}\+(\d\d)${second}" => first.toInt+second.toInt case _ => 0 }
res38: Int = 25

더 자세한 바인딩 메커니즘을 설정할 수도 있습니다.

scala> object Doubler { def unapply(s: String) = Some(s.toInt*2) }
defined module Doubler

scala> "10" match { case r"(\d\d)${Doubler(d)}" => d case _ => 0 }
res40: Int = 20

scala> object isPositive { def unapply(s: String) = s.toInt >= 0 }
defined module isPositive

scala> "10" match { case r"(\d\d)${d @ isPositive()}" => d.toInt case _ => 0 }
res56: Int = 10

무엇을 사용할 수 있는지에 대한 인상적인 예 Dynamic는 블로그 게시물 Introduction to Type Dynamic에서 볼 수 있습니다 .

object T {

  class RegexpExtractor(params: List[String]) {
    def unapplySeq(str: String) =
      params.headOption flatMap (_.r unapplySeq str)
  }

  class StartsWithExtractor(params: List[String]) {
    def unapply(str: String) =
      params.headOption filter (str startsWith _) map (_ => str)
  }

  class MapExtractor(keys: List[String]) {
    def unapplySeq[T](map: Map[String, T]) =
      Some(keys.map(map get _))
  }

  import scala.language.dynamics

  class ExtractorParams(params: List[String]) extends Dynamic {
    val Map = new MapExtractor(params)
    val StartsWith = new StartsWithExtractor(params)
    val Regexp = new RegexpExtractor(params)

    def selectDynamic(name: String) =
      new ExtractorParams(params :+ name)
  }

  object p extends ExtractorParams(Nil)

  Map("firstName" -> "John", "lastName" -> "Doe") match {
    case p.firstName.lastName.Map(
          Some(p.Jo.StartsWith(fn)),
          Some(p.`.*(\\w)$`.Regexp(lastChar))) =>
      println(s"Match! $fn ...$lastChar")
    case _ => println("nope")
  }
}


답변

delnan이 지적했듯이 matchScala 의 키워드는 정규식 과 관련이 없습니다. 문자열이 정규식과 일치하는지 확인하려면 String.matches메서드를 사용할 수 있습니다 . 문자열이 소문자 또는 대문자로 a, b 또는 c로 시작하는지 확인하려면 정규식은 다음과 같습니다.

word.matches("[a-cA-C].*")

이 정규식은 “문자 a, b, c, A, B 또는 C 중 하나 다음에 아무 것이나 오는 것”으로 읽을 수 있습니다 ( .“모든 문자”를 *의미하고 “0 회 이상 “을 의미하므로 “. *”는 모든 문자열). .


답변

Andrew의 대답을 조금 확장하려면 : 정규식이 추출기를 정의한다는 사실은 Scala의 패턴 일치를 사용하여 정규식과 일치하는 하위 문자열을 매우 잘 분해하는 데 사용할 수 있습니다.

val Process = """([a-cA-C])([^\s]+)""".r // define first, rest is non-space
for (p <- Process findAllIn "aha bah Cah dah") p match {
  case Process("b", _) => println("first: 'a', some rest")
  case Process(_, rest) => println("some first, rest: " + rest)
  // etc.
}


답변

String.matches는 정규식 의미에서 패턴 일치를 수행하는 방법입니다.

하지만, 실제 Scala 코드의 word.firstLetter는 다음과 같습니다.

word(0)

Scala는 Strings를 Char의 시퀀스로 취급하므로 어떤 이유로 든 String의 첫 번째 문자를 명시 적으로 가져와 일치 시키려면 다음과 같이 사용할 수 있습니다.

"Cat"(0).toString.matches("[a-cA-C]")
res10: Boolean = true

정규식 패턴 일치를 수행하는 일반적인 방법으로 이것을 제안하지는 않지만 먼저 String의 첫 번째 문자를 찾은 다음 정규식과 일치시키는 제안 된 접근 방식과 일치합니다.

편집 : 명확하게 말하면 다른 사람들이 말했듯이 이것을하는 방법은 다음과 같습니다.

"Cat".matches("^[a-cA-C].*")
res14: Boolean = true

가능한 한 초기 의사 코드에 가까운 예제를 보여주고 싶었습니다. 건배!


답변

@AndrewMyers의 답변의 접근 방식은 전체 문자열을 정규식 과 일치시키고 ^및을 사용하여 문자열의 양쪽 끝에 정규식을 고정하는 효과가 $있습니다. 예:

scala> val MY_RE = "(foo|bar).*".r
MY_RE: scala.util.matching.Regex = (foo|bar).*

scala> val result = "foo123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = foo

scala> val result = "baz123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = No match

scala> val result = "abcfoo123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = No match

그리고 .*끝 이 없습니다 .

scala> val MY_RE2 = "(foo|bar)".r
MY_RE2: scala.util.matching.Regex = (foo|bar)

scala> val result = "foo123" match { case MY_RE2(m) => m; case _ => "No match" }
result: String = No match


답변

먼저 정규 표현식을 별도로 사용할 수 있음을 알아야합니다. 다음은 그 예입니다.

import scala.util.matching.Regex
val pattern = "Scala".r // <=> val pattern = new Regex("Scala")
val str = "Scala is very cool"
val result = pattern findFirstIn str
result match {
  case Some(v) => println(v)
  case _ =>
} // output: Scala

둘째, 정규 표현식과 패턴 매칭을 결합하는 것이 매우 강력하다는 것을 알아야합니다. 다음은 간단한 예입니다.

val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
"2014-11-20" match {
  case date(year, month, day) => "hello"
} // output: hello

사실 정규 표현식 자체는 이미 매우 강력합니다. 우리가해야 할 유일한 일은 Scala로 더 강력하게 만드는 것입니다. 다음은 Scala 문서의 더 많은 예제입니다. http://www.scala-lang.org/files/archive/api/current/index.html#scala.util.matching.Regex