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 '김용환'
,