2011년 6월 2일 목요일

Standard Types : Regular Expressions

Regular Expressions

정규표현식은 문자열에서 패턴을 매치하기 위해서 사용한다. 루비는 패턴 매칭과 치환을 간편하게 쓸 수 있도록 정규표현식 지원을 내장하고 있다. 정규표현식은 Regexp 클래스의 객체이다. 클래스의 생성자를 명시적으로 호출하여 만들 수도 있고, /패턴/, %r{패턴} 같은 리터럴을 사용해서 만들수 있다.
a = Regexp.new('^\s*[a-z]')»/^\s*[a-z]/
b = /^\s*[a-z]/»/^\s*[a-z]/
c = %r{^\s*[a-z]}»/^\s*[a-z]/


Regexp#match 메서드나 =~, !~ 연산차를 이용해서 문자열에 대한 매치를 수행할 수 있다. 매치 연산자를 사용할 때에는 적어도 피연산자 하나가 정규표현식이어야 한다.

a = "Fats Waller"
puts a =~ /a/
puts a =~ /z/
puts a =~ "ll"

>> 1
>> nil
>> ... `=~': type mismatch: String given (TypeError)


매 치 연산자는 매치가 발견된 부분의 위치를 반환한다. 동시에 여러 가지 루비 변수의 값을 바꾸느 부수효과도 일어난다. 패턴에 매치되는 부분은 "$&"에 저장되고, "$`"에는 매치되는 부분의 앞부분이 저장되고, "$'"에는 뒷부분이 저장된다.

def showRE(a,re)
 if a =~ re
"#{$`}<<#{$&}>>#{$'}"
 else
"no match"
 end
end

puts showRE('very interesting', /t/)
puts showRE('Fats Waller', /ll/)

>> very in<<t>>eresting
>> Fats Wa<<ll>>er

매치 연산자는 또한 쓰레드-지연변수인$~과 $1부터 $9까지의 값도 바꾼다. 변수 "$~"에는 매치에 대한 모든 든 정보를 담고 있는 MatchData 객체가 저장된다. 또한 $1~$9 에는 매치된 부분의 값이 저장된다.

Patterns

모든 정규표현식은 패턴을 포함한다. 패턴은 문자열에서 정규표현식을 매치하기 위해 사용된다.
패턴에는 ., |, (, ), [, {, +, \, ^, $, *, ?를 제외한 모든 문자는 자신에 매치된다.
showRE('kangaroo', /angar/)»k<<angar>>oo
showRE('!@%&-_=+', /%&/)»!@<<%&>>-_=+


위의 특수 문자 자체를 매치하고 싶으면 "/"와 같이 써주면된다. 또 정규표현식에서도 #{...} 표현식 치환을 포함할 수 있다.
showRE('yes | no', /\|/)»yes <<|>> no
showRE('yes (no)', /\(no\)/)»yes <<(no)>>
showRE('are you sure?', /e\?/)»are you sur<<e?>>


Anchors

기 본적으로 정규표현식은 문자열에서 패턴이 나타나는 첫 위치를 찾는다. 하지만 문자열의 맨 앞 문자나 맨 뒤 문자를 매치하고자 할 때에는 ^, $ 를 사용하면된다. 또한 "\A"는 지정된 문자열의 첫 부분을 "\Z", "\z"는 문자열의 끝을 나타낸다.
showRE("this is\nthe time", /^the/)»this is\n<<the>> time
showRE("this is\nthe time", /is$/)»this <<is>>\nthe time
showRE("this is\nthe time", /\Athis/)»<<this>> is\nthe time
showRE("this is\nthe time", /\Athe/)»no match


"\b", "\B" 는 각각 워드 경계와 워드가 아닌것의 경계를 나타낸다.
showRE("this is\nthe time", /\bis/)»this <<is>>\nthe time
showRE("this is\nthe time", /\Bis/)»th<<is>> is\nthe time


Character Classes

문자 클래스는 대괄호로 둘러싸인 문자의 집합이다. 정규표현식 특수문자 .|()[{+^$*?는 대괄호 안에서는 그 의미가 사라지고 원래 문자를 의미한다. 하지만 일반적인 문자열 치환은 일어나며 \b 백스페이스, \n은 개행문자로 바뀐다. \s는  스페이스 리터럴뿐 아니라 공백문자 전체와 매치된다.
showRE('It costs $12.', /[aeiou]/)»It c<<o>>sts $12.
showRE('It costs $12.', /[\s]/)»It<< >>costs $12.

a = 'Gamma [Design Patterns-page 123]'
showRE(a, /[]]/)»Gamma [Design Patterns-page 123<<]>>
showRE(a, /[B-F]/)»Gamma [<<D>>esign Patterns-page 123]
showRE(a, /[-]/)»Gamma [Design Patterns<<->>page 123]
showRE(a, /[0-9]/)»Gamma [Design Patterns-page <<1>>23]


만약 리터럴 문자 ], -을 문자 클래스에 포함하고 싶다면 반드시 맨 앞에 써줘야 한다. 그리고 대괄호를 열자마자 ^를 써주면 문자 클래스의 역이 된다.

a = 'see [Design Patterns-page 123]'
puts showRE(a, /[]]/)
puts showRE(a, /[-]/)
puts showRE(a, /[^a-z]/)
puts showRE(a, /[^a-z\s]/)

>> see [Design Patterns-page 123<<]>>
>> see [Design Patterns<<->>page 123]
>> see<< >>[Design Patterns-page 123]
>> see <<[>>Design Patterns-page 123]

몇 가지 문자 클래스는 자주 사용되기 때문에 축약어의 형태를 제공한다. 축약어는 아래와 같다. 이 축약어는 대괄호 안에서 사용해도 되고, 패턴의 중간에 나와도 된다.

Character class abbreviations
SequenceAs [ ... ]Meaning
\d[0-9]Digit character
\D[^0-9]Nondigit
\s[\s\t\r\n\f]Whitespace character
\S[^\s\t\r\n\f]Nonwhitespace character
\w[A-Za-z0-9_]Word character
\W[^A-Za-z0-9_]Nonword character


대괄호 밖에서 "."는 개행문자를 제외한 어떤 문자와도 매치된다. multi-line mode에서는 개행문자와도 매치된다.
a = 'It costs $12.'
showRE(a, /c.s/)»It <<cos>>ts $12.
showRE(a, /./)»<<I>>t costs $12.
showRE(a, /\./)»It costs $12<<.>>


Repetition

"r"이 패턴에서 바로 앞의 정규표현식을 의미한다고 하면.
r *matches zero or more occurrences of r.
r +matches one or more occurrences of r.
r ?matches zero or one occurrence of r.
r {m,n}matches at least ``m'' and at most ``n'' occurrences of r.
r {m,}matches at least ``m'' occurrences of r.


반복 구조는 결합 우선순위가 높기 때문에 패턴에서 바로 앞의 정규표현식에만 적용된다.
a = "The moon is made of cheese"
showRE(a, /\w+/)»<<The>> moon is made of cheese
showRE(a, /\s.*\s/)»The<< moon is made of >>cheese
showRE(a, /\s.*?\s/)»The<< moon >>is made of cheese
showRE(a, /[aeiou]{2,99}/)»The m<<oo>>n is made of cheese
showRE(a, /mo?o/)»The <<moo>>n is made of cheese


Alternation

세로 막대는 세로 막대 앞쪽의 정규표현식과 바로 다음에 오는 정규표현식 둘 중 하나와 매치함을 의미한다.
a = "red ball blue sky"
showRE(a, /d|e/)»r<<e>>d ball blue sky
showRE(a, /al|lu/)»red b<<al>>l blue sky
showRE(a, /red ball|angry sky/)»<<red ball>> blue sky


Grouping

정규표현식 안에서 괄호를 사용하면 몇 개를 그룹으로 묶어줄 수 있다. 그룹 안의 패턴은 모두 하나의 정규표현식 처럼 처리 된다.
showRE('banana', /an*/)»b<<an>>ana
showRE('banana', /(an)*/)»<<>>banana
showRE('banana', /(an)+/)»b<<anan>>a

a = 'red ball blue sky'
showRE(a, /blue|red/)»<<red>> ball blue sky
showRE(a, /(blue|red) \w+/)»<<red ball>> blue sky
showRE(a, /(red|blue) \w+/)»<<red ball>> blue sky
showRE(a, /red|blue \w+/)»<<red>> ball blue sky

showRE(a, /red (ball|angry) sky/)»no match
a = 'the red angry sky'
showRE(a, /red (ball|angry) sky/)»the <<red angry sky>>


또 한 괄호는 패턴 패칭의 결과를 저장해 준다. 괄호가 열린 횟수를 세어서, 괄호 각각에 대해서 닫힌 부분까지의 부분 매치 결과를 저장한다. 저장된 값을 정규표현식 내부의 다른 부분에 다시 사용할 수도 있다. 패턴에서는 \1, \2 와 같은 식으로, 외부에서는 $1, $2등을 통해 결과를 사용할 수 있다.
"12:50am" =~ /(\d\d):(\d\d)(..)/»0
"Hour is #$1, minute #$2"»"Hour is 12, minute 50"
"12:50am" =~ /((\d\d):(\d\d))(..)/»0
"Time is #$1"»"Time is 12:50"
"Hour is #$2, minute #$3"»"Hour is 12, minute 50"
"AM/PM is #$4"»"AM/PM is am"

# match duplicated letter
showRE('He said "Hello"', /(\w)\1/)»He said "He<<ll>>o"
# match duplicated substrings
showRE('Mississippi', /(\w+)\1/)»M<<ississ>>ippi

showRE('He said "Hello"', /(["']).*?\1/)»He said <<"Hello">>
showRE("He said 'Hello'", /(["']).*?\1/)»He said <<'Hello'>>


Pattern-Based Substitution

String#sub, String#gsub 메서드는 첫 번째 매개변수와 매치되는 문자열 부분을 찾아서 두 번째 매개변수로 바꿔준다. String#sub은 처음 한번만, String#gsub 메서드는 발견되는 모든 매치에 대해서 치환을 수행한다.
a = "the quick brown fox"
a.sub(/[aeiou]/,  '*')»"th* quick brown fox"
a.gsub(/[aeiou]/, '*')»"th* q**ck br*wn f*x"
a.sub(/\s\S+/,  '')»"the brown fox"
a.gsub(/\s\S+/, '')»"the"


두 번째 매개변수는 String이어도 되고 블록이어도 된다. 블록일 경우 매치된 부분 문자열을 넘겨서 계산된 값으로 치환작업을 수행한다.
a = "the quick brown fox"
a.sub(/^./) { $&.upcase }»"The quick brown fox"
a.gsub(/[aeiou]/) { $&.upcase }»"thE qUIck brOwn fOx"


"\b\w"는 단어의 첫 번째 문자를 나타내는 패턴이다.
def mixedCase(aName)
 aName.gsub(/\b\w/) { $&.upcase }
end
mixedCase("fats waller")»"Fats Waller"
mixedCase("louis armstrong")»"Louis Armstrong"
mixedCase("strength in numbers")»"Strength In Numbers"


Backslash Sequences in the Substitution

\1,\2.. 그룹, \& 최근 매치 전체, \+ 마지막으로 매치된 그룹, \` 매치 앞의 문자열, \' 매치 뒤의 문자열, \\역슬래시 리터럴을 나타낸다.
"fred:smith".sub(/(\w+):(\w+)/, '\2, \1')»"smith, fred"
"nercpyitno".gsub(/(.)(.)/, '\2\1')»"encryption"

str = 'a\b\c'»"a\b\c"
str.gsub(/\\/, '\\\\\\\\')»"a\\b\\c"

str = 'a\b\c'»"a\b\c"
str.gsub(/\\/, '\&\&')»"a\\b\\c"

str = 'a\b\c'»"a\b\c"
str.gsub(/\\/) { '\\\\' }»"a\\b\\c"


def unescapeHTML(string)
str = string.dup
str.gsub!(/&(.*?);/n) {
match = $1.dup
case match
when /\Aamp\z/ni then '&'
when /\Aquot\z/ni then '"'
when /\Agt\z/ni then '>'
when /\Alt\z/ni then '<'
when /\A#(\d+)\z/n then Integer($1).chr
when /\A#x([0-9a-f]+)\z/ni then $1.hex.chr
end
}
str
end

puts unescapeHTML("1&lt;2 &amp;&amp; 4&gt;3")
puts unescapeHTML("&quot;A&quot; = &#65; = &#x41;")

>> 1<2 && 4>3
>> "A" = A = A

Object-Oriented Regular Expressions

Regexp#match 메서드는 정규표현식을 문자열에 대해 매치한다. 실패하면 nil을 성공한 경우에는 MatchData 클래스 인스턴스를 반환한다. MatchData 객체는 매치에 대한 모든 정보를 제공하고 $- 변수를 통해 얻을 수 있는 모든 것이 포함되어 있다.
re = /(\d+):(\d+)/     # match a time hh:mm
md = re.match("Time: 12:34am")
md.type»MatchData
md[0]         # == $&»"12:34"
md[1]         # == $1»"12"
md[2]         # == $2»"34"
md.pre_match  # == $`»"Time: "
md.post_match # == $'»"am"


매치의 결과 각각이 별개의 객체로 저장되기 때문에, 여러 번의 패턴 매치 결과를 동시에 가질 수 있다.
re = /(\d+):(\d+)/     # match a time hh:mm
md1 = re.match("Time: 12:34am")
md2 = re.match("Time: 10:30pm")
md1[1, 2]»["12", "34"]
md2[1, 2]»["10", "30"]

댓글 없음:

댓글 쓰기

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...