'2016/11/02'에 해당되는 글 1건

  1. 2016.11.02 [scala] 문자열 패턴 매치 (pattern match) 예시



scala의 문자열과 연관된 패턴 매치에 대한 예시이다.


자바는 정규 표현식과 관련된 클래스가 Pattern, Matcher가 있지만, scala에서는 scala.util.matching.Regex 클래스가 존재한다. 패턴을 정의하고, 문자열에 대한 패턴 정보를 findAllIn 메소드(findXXX 시리즈로 다양한 제어를 할 수 있다)로 확인할 수 있다. 


val pattern1 = new scala.util.matching.Regex("(H|h)ello")
val str = "Hello Mom, hello Dad"
println(pattern1.findAllIn(str).mkString(","))

결과는 다음과 같다. 


Hello,hello




하지만, 꼭 scala.util.matching.Regex를 명시해서 사용할 필요는 없다. 정규 표현식으로 사용할 표현식에 .r을 붙이면 알아서 Regex 타입의 인스턴스가 생성된다. 

val numPattern = "[0-9]+".r
// scala.util.matching.Regex = [0-9]+
val str1 = "Hello Mom 911, hello Dad 119?"
val matches = numPattern.findAllIn(str1)
println(matches.toList)


결과는 다음과 같다.


List(911, 119)




Regex의 findFirstIn을 사용하면 Option으로 리턴한다. 




val numPattern = "[0-9]+".r val str1 = "Hello Mom 911, hello Dad 119?"

println(numPattern.findFirstIn(str1))
println(numPattern.findFirstIn(str))


결과는 다음과 같다. 


Some(911)

None




Option의 특징을 활용해서 Some과 None에 대한 case 문을 작성할 수 있다.


numPattern.findFirstIn(str1) match {
case Some(s) => println(s)
case None => println("None")
}

numPattern.findFirstIn(str) match {
case Some(s) => println(s)
case None => println("None")
}


결과는 다음과 같다. 


911

None




이번에는 반대로, 특정 문자열에 대해 regex 타입을 case 문에 넣을 수 있다.  그리고, 

// string match
val sp = "[a-zA-Z]".r
val np = "xxx".r
"bus 1503, 5, 222" foreach {
case sp => println("s : " + sp)
case np => println("n : " + np)
}



결과는 다음과 같다. 


s : b

s : u

s : s

s :  

s : 1

s : 5

s : 0

s : 3

s : ,

s :  

s : 5

s : ,

s :  

s : 2

s : 2

s : 2






case 문이나 find 메소드 없이 아주 간단하게 정규 표현식을 읽을 수 있다. 

val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2016/11/01"
println(year, month, day)

결과는 다음과 같다. 


(2016,11,01)





그리고, 정확치 않은 정규 표현식이라면, 적당히 알아서 나온다(뒤에서 부터 파싱되었다).

val regex1 = """(\d+)(\d+)(\d+)""".r
val regex1(a, b, c) = "112801"
println(a, b, c)

결과는 다음과 같다.

(1128,0,1)




간단한 문자열도 이와 같이 실행할 수 있다. 

val Name = """(\w+)\s+(\w+)""".r
val Name(firstName, lastName) = "samuel kim"
println(firstName, lastName)


"samuel kim" match {
case Name(first, last) => println(first, last)
case _ => println("x")
}

위 두 결과는 모두 다음과 같다. 


(samuel,kim)

(samuel,kim)





이젠 full name도 쉽게 할 수 있다. 

val FullName = """(\w+)\s+(\w+)\s+(\w+)""".r
val FullName(first, middle, last) = "samuel jackson kim"
println(first, middle, last)

결과는 다음과 같다.


(samuel,jackson,kim)




시간, 분, 초를 한번에 로그에 남겼을 경우 이를 예쁘게 보여줄 수 있는 함수이다. 


val pattern = """([0-9]{1,2})([0-9]{1,2})([0-9]{1,2})""".r
val allMatches = pattern.findAllMatchIn("112801")
allMatches.foreach { m =>
println(m.group(1) + ":" + m.group(2) + ":" + m.group(3))
}

결과는 다음과 같다. 


11:28:01




이를 Spark-Zeppelin으로 간단하게 보여주기 위해 getTime이라는 메소드를 만들었다. 



val reqLog = textFile.filter(line=>line.contains("12314")).map(s=>s.split("\t")).map(

    s=>RequestLog(getTime(s(1).toString),

            s(5), 

            s(11),

            s(21)

        ))




하나는 Regex에 findAllMatchIn과 foreach, group을 사용한 함수와

또 다른 하나는 Regex에 findAllIn과 case를 이용한 방식을 사용했다. 

def getTime(time: String): String = {
val pattern = """([0-9]{1,2})([0-9]{1,2})([0-9]{1,2})([0-9]{1,5})""".r
val allMatches = pattern.findAllMatchIn(time)
var result = ""
allMatches.foreach { m =>
result = m.group(1) + ":" + m.group(2) + ":" + m.group(3) + "." + m.group(4)
}
result
}

def getTime2(time: String): String = {
val pattern = """([0-9]{1,2})([0-9]{1,2})([0-9]{1,2})([0-9]{1,5})""".r
var result = ""
pattern.findAllIn(time) foreach {
case pattern(hour, minute, second, millisecond) => result = hour + ":" + minute + ":" + second + "." + millisecond
}
result
}



println(getTime("1128000000"))
println(getTime2("1128000000"))

결과는 다음과 같다. 


11:28:00.0000

11:28:00.0000



Posted by '김용환'
,