출처 : http://home.postech.ac.kr/%7Edada/methodx/reg_ex.html

정규표현식 (Regular Expressions)

PHP에서 정규표현식을 지원하는 함수로는 ereg(), eregi(), ereg_replace(), eregi_replace(), split(), spliti() 등이 있다. 이 때 ereg_replace 보다 preg_replace가 훨씬 빠르다고 한다.
이 외에도 펄 형식을 지원하는 정규표현식에 관련된 함수들도 있다.

1. 기초 (Basic Syntax)

First of all, let's take a look at two special symbols: '^ (start)' and '$ (end)'.

  • "^The": matches any string that starts with "The";
  • "of despair$": matches a string that ends in the substring "of despair";
  • "^abc$": a string that starts and ends with "abc" -- that could only be "abc" itself!
  • "notice": a string that has the text "notice" in it. "notice" 그 자체가 아닌 "notice"를 포함하는 것을 뜻한다.

There are also the symbols '* (zero or more)', '+ (one or more)', and '? (zero or one)', which denote the number of times a character or a sequence of characters may occur.

  • "ab*": matches a string that has an a followed by zero or more b's ("a", "ab", "abbb", etc.);
  • "ab+": same, but there's at least one b ("ab", "abbb", etc.);
  • "ab?": there might be a b or not;
  • "a?b+$": a possible a followed by one or more b's ending a string.

You can also use bounds, which come inside braces and indicate ranges in the number of occurences:

  • "ab{2}": matches a string that has an a followed by exactly two b's ("abb");
  • "ab{2,}": there are at least two b's ("abb", "abbbb", etc.);
  • "ab{3,5}": from three to five b's ("abbb", "abbbb", or "abbbbb").
  • "ab{0,}" → "ab*"
  • "ab{1,}" → "ab+"
  • "ab{0,1}" → "ab?"

Now, to quantify a sequence of characters, put them inside parentheses:

  • "a(xxyy)*": matches a string that has an a followed by zero or more copies of the sequence "xxyy";
  • "a(bc){1,5}": one through five copies of "bc."; ("abc", "abcbc" , "abcbcbc", "abcbcbcbc", or "abcbcbcbcbc")

There's also the '|' symbol, which works as an OR operator:

  • "hi|hello": matches a string that has either "hi" or "hello" in it;
  • "(b|cd)ef": a string that has either "bef" or "cdef";
  • "(a|b)*c": a string that has a sequence of alternating a's and b's ending in a c; ("ac", "bc", or "c")

A period ('.') stands for any single character:

  • "a.[0-9]": matches a string that has an a followed by one character and a digit; ("ax1", "ak7", etc.)
  • "^.{3}$": a string with exactly 3 characters.
  • "^(100)(\.[0-9]{1,2})?$" : "100"이거나 100 다음에 소수점이 있다면 첫째나 둘째자리까지 있다.

Bracket expressions specify which characters are allowed in a single position of a string:

  • "[ab]": matches a string that has either an a or a b (that's the same as "a|b");
  • "[a-d]": a string that has lowercase letters 'a' through 'd' (that's equal to "a|b|c|d" and even "[abcd]");
  • "^[a-zA-Z]": a string that starts with a letter;
  • "[0-9]%": a string that has a single digit before a percent sign;
  • ",[a-zA-Z0-9]$": a string that ends in a comma followed by an alphanumeric character.

You can also list which characters you DON'T want -- just use a '^' as the first symbol in a bracket expression (i.e., "%[^a-zA-Z]%" matches a string with a character that is not a letter between two percent signs).

  • "[^abc]": a, b, 또는 c를 제외한 문자.
  • "[^0-9]: 숫자를 제외한 문자.

특수문자에 해당하는 문자 사용하기

\ 는 특수 문자를 문자 자체로 해석하도록 하는 Escape 문자로 사용된다. ? 자체를 찾으려고 하면 \? 와 같이 사용되어야 한다.
\ 를 사용해야 하는 문자로는 ^ \ | [] () {} . * ? + 가 있다. 이 문자들을 문자 자체로 해석을 하기위해서는
\^ \\ \| \[ \( \{ \. \* \? \+ 등과 같이 사용해야 한다.

In order to be taken literally, you must escape the characters "^.[$()|*+?{\" with a backslash ('\'), as they have special meaning.
On top of that, you must escape the backslash character itself in PHP3 strings, so, for instance, the regular expression "(\$|?[0-9]+" would have the function call: ereg("(\\$|?[0-9]+", $str) (what string does that validate?)

Just don't forget that bracket expressions are an exception to that rule--inside them, all special characters, including the backslash ('\'), lose their special powers (i.e., "[*\+?{}.]" matches exactly any of the characters inside the brackets). And, as the regex man pages tell us: "To include a literal ']' in the list, make it the first character (following a possible '^'). To include a literal '-', make it the first or last character, or the second endpoint of a range."

예를 들어 what?이라는 물음표로 끝나는 문자를 찾고 싶다고, egrep 'what?' ...이라고 하면 ?이 특수문자이므로 wha를 포함한 whale도 찾게 된다. 또, 3.14로 찾을때는 3+14 등도 찾게 된다.

특수문자가 [] 안과 밖에서 다르다는 점을 생각하여 각각의 경우를 살펴보자. 우선 [] 밖의 경우는,

  • \을 특수문자 앞에 붙이기. 예, "what\?", "3\.14"
  • []을 사용하기. 예, "what[?]", "3[.]14"

첫번째 방법은 보통 escape라고 부르며, 특수문자 앞에 \을 붙여서 특수문자의 특수한 기능을 제거한다. 두번째 방법은 [] 밖의 많은 특수문자들이 [] 안에서는 일반문자가 되는 점을 이용한 것이다. 보통 첫번째 방법을 많이 사용한다.
이 때 \도 뒤에 나오는 특수문자를 일반문자로 만드는 특수문자이기 때문에, 문자 그대로의 \을 나타내려면 \\을 사용해야 한다. 물론 [\]도 가능하다.

[] 안의 특수문자는 위치를 바꿔서 처리한다. 먼저, ^는 [^abc]와 같이 처음에 나와야만 의미가 있으므로 [abc^]와 같이 다른 위치에 사용하면 된다. -는 [a-z]와 같이 두 문자 사이에서만 의미가 있으므로 [-abc]나 [abc-]와 같이 제일 처음이나 마지막에 사용한다. (grep과 같이 도구에 따라 역으로 일반 문자앞에 \를 붙여서 특수문자를 만드는 경우가 있다.)

For completeness, I should mention that there are also collating sequences, character classes, and equivalence classes. I won't be getting into details on those, as they won't be necessary for what we'll need further down this article. You should refer to the regex man pages for more information.

character class의 예

  • [[:alpha:]][a-zA-Z]
  • [[:digit:]][0-9]
  • [[:alnum:]][0-9a-zA-Z]
  • [[:space:]]공백문자

 

2. 응용 예제

lidating Money Strings

Ok, we can now use what we've learned to work on something useful: a regular expression to check user input of an amount of money. A quantity of money can be written in four ways we can consider acceptable: "10000.00" and "10,000.00", and, without the cents, "10000" and "10,000". Let's begin with:

^[1-9][0-9]*$

That validates any number that doesn't start with a 0. But that also means the string "0" doesn't pass the test. Here's a solution:

^(0|[1-9][0-9]*)$

"Just a zero OR some number that doesn't start with a zero." We may also allow a minus sign to be placed before the number:

^(0|-?[1-9][0-9]*)$

That means: "a zero OR a possible minus sign and a number that doesn't start with a zero." Ok, let's not be so strict and let the user start the number with a zero. Let's also drop the minus sign, as we won't be needing it for the money string. What we could do is specify an optional decimal fraction part in the number:

^[0-9]+(\.[0-9]+)?$

It's implicit in the highlited construct that a period always comes with at least one digit, as a whole set. So, for instance, "10." is not validated, whereas "10" and "10.2" are.

^[0-9]+(\.[0-9]{2})?$

We specified that there must be exactly two decimal places. If you think that's too harsh, you can do the following:

^[0-9]+(\.[0-9]{1,2})?$

That allows the user to write just one number after the period. Now, as for the commas separating the thousands, we can put in:

^[0-9]{1,3}(,[0-9]{3})*(\.[0-9]{1,2})?$

"A set of 1 to 3 digits followed by zero or more sets of a comma and three digits." Easy enough, isn't it? But let's make the commas optional:

^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(\.[0-9]{1,2})?$

That's it. Don't forget that the '+' can be substituted by a '*' if you want empty strings to be accepted also (why?). And don't forget to escape the backslash for the function call (common mistake here). Now, once the string is validated, we strip off any commas with str_replace(",", "", $money) and typecast it to double so we can make math with it.

 

Validating E-mail Addresses

Ok, let's take on e-mail addresses. There are three parts in an e-mail address: the POP3 user name (everything to the left of the '@'), the '@', and the server name (the rest). The user name may contain upper or lowercase letters, digits, periods ('.'), minus signs ('-'), and underscore signs ('_'). That's also the case for the server name, except for underscore signs, which may not occur.

Now, you can't start or end a user name with a period, it doesn't seem reasonable. The same goes for the domain name. And you can't have two consecutive periods, there should be at least one other character between them. Let's see how we would write an expression to validate the user name part:

^[_a-zA-Z0-9-]+$

That doesn't allow a period yet. Let's change it:

^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*$

That says: "at least one valid character followed by zero or more sets consisting of a period and one or more valid characters."

To simplify things a bit, we can use the expression above with eregi(), instead of ereg(). Because eregi() is not sensitive to case, we don't have to specify both ranges "a-z" and "A-Z" -- one of them is enough:

^[_a-z0-9-]+(\.[_a-z0-9-]+)*$

For the server name it's the same, but without the underscores:

^[a-z0-9-]+(\.[a-z0-9-]+)*$

Done. Now, joining both expressions around the 'at' sign, we get:

^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*$

Microsoft .NET framework의 RegularExpressionValidate 컨트롤에서는
^[_a-zA-Z0-9]+([-+.][_a-zA-Z0-9]+)*@[_a-zA-Z0-9]+([-.][_a-zA-Z0-9]+)*\.[_a-zA-Z0-9]+([-.][_a-zA-Z0-9]+)*$
이렇게 쓰고있다. 이게 더 정확한 듯 하다. 위의 것은 '-'으로 시작하는 것을 허용하는데 이것은 그렇지 않다.

 

주민등록번호

주민등록번호는 "6자리의 숫자 - (1|2|3|4)로 시작하는 7자리의 숫자" 의 형태로 되어있다.

[0-9]{6}-(1|2|3|4)[0-9]{6}

 

자연수 및 유리수

<script language="javascript">
<!--
function realCheck(Str) {
      var fooPat=/^[1-9][0-9]*(\.[0-9]+)?$/
      var matchArray=Str.match(fooPat)
      if (matchArray==null) {return false;}
      return true;
}

function positiveIntCheck(Str) {
      var fooPat=/^[1-9][0-9]*$/
      var matchArray=Str.match(fooPat)
      if (matchArray==null) {return false;}
      return true;
}
//-->
</script>

 

URL 주소

URL주소의 형식은 www.aaa.net 과 같으며 영숫자 . \ ? & _ = ~ + 등으로 이루어져 있다.

[0-9a-zA-Z_]+(\.[0-9a-zA-Z/\?_=+~]+)*

(http|ftp|https):/{2}[0-9a-zA-Z_]+(\.[0-9a-zA-Z/\?_=+~]+)*

 

E-mail 기타 등등


if(!eregi("^([[:alnum:]_%+=.-]+)@([[:alnum:]_.-]+)\.([a-z]{2,3}|[0-9]{1,3})$",$email)) 
{ 
    echo "잘못된 email address 다.";  
} 

if(!ereg("([^[:space:]])",$name)) {
echo("<script>alert('이름을 입력하세요!');history.go(-1)</script>");
exit;
}

if(!ereg("([a-zA-Z0-9]+)@([a-zA-Z0-9])([.a-zA-Z0-9]+)([a-zA-Z0-9])$",$email)) {
echo("<script>alert('올바른 E-Mail 주소가 아닙니다.');history.go(-1)</script>");
exit;
}

if(!ereg("(^[0-9a-zA-Z]{4,8}$)",$passwd)) {
echo("<script>alert('비밀번호는 4자에서 8자까지의 영문자 또는 숫자로 입력하세요!');history.go(-1)
</script>");
exit;
}

if(!ereg("([^[:space:]])",$title)) {
echo("<script>alert('제목을 입력하세요!');history.go(-1)</script>");
exit;
}

 

Other uses

Extracting Parts of a String

ereg() and eregi() have a feature that allows us to extract matches of patterns from strings (read the manual for details on how to use that). For instance, say we want do get the filename from a path/URL string -- this code would be all we need:

ereg("([^\\/]*)$", $pathOrUrl, $regs);
echo $regs[1];

Advanced Replacing

ereg_replace() and eregi_replace() are also very useful: suppose we want to separate all words in a string by commas:

ereg_replace("[ \n\r\t]+", ",", trim($str));

 

찾은문자열 사용하기

정규식을 이용하여 문자열을 찾는 함수는 ereg()와 eregi() 함수이다. 이 함수 문자열에서 찾고자 하는 문자의 패턴이 있는지 확인할 수 있다. 그런데 문자열이 존재하는지 확인만하고 어떤 문자열을 찾았는지 볼수 있는 방법은 없을까?
ereg("a(b*)(c+)", "abbbbcccd", $reg);
다음과 같이 세번째 인자를 주면 주어진 패턴과 일치하는 문자열을 구할수 있다. 그럼 $reg 에는 어떤것들이 저장되어있을까 ? $reg는 배열의 형태로 반환이 된다.
$reg[0] 에는 주어진 전체 패턴인 a(b*)(c+)에 대한 내용이 들어간다. 위의 함수 결과에서는 abbbbccc 가 된다.
$reg[1]에는 첫 번째 (b*) 에 해당하는 bbbb가 들어간다.
$reg[2]에는 두 번째 ()인 (c+) 에 해당하는 ccc가 들어가게 된다.
그러면 ()안에 또 ()가 들어가면 어떤 식으로 해석이 될까?
만약에 (a+(a*))a(b+(c*))라는 패턴이 있으면 어떤 식으로 들어가게 될 지를 알아보자.
먼저 $reg[0]에는 전체 패턴인 (a+(a*))a(b+(c*))에 대한 내용이 들어간다.
$reg[1]에는 첫 번째 큰 ()인 (a+(a*)) 에 대한 내용이 들어가고, $reg[2] 에는 이 괄호 안에 있는 작은 괄호 (a*) 에 대한 내용이 들어간다. $reg[3], $reg[4] 에는 각각 (b+(c*)), (c*)의 내용이 들어가게 된다.

 

정규식을 이용한 문자열 치환

정규 표현식을 이용하여 문자열을 치환하는 함수는 ereg_replace()와 eregi_replace() 함수가 있다.
두 함수의 차이점은 eregi_replace()의 경우에는 영문자의 대소문자 구분을 하지 않는것이다.

$str = "test test"; // 두 개의 test 사이에는 4개의 스페이스가 있다.
&str = ereg_replace(" "," ", $str);
이 명령의 실행결과로 $str = "test test"; 가 된다.
이는 내용을 출력할 때 많이 사용되는 구문이다. 그럼 문자열 치환시에 문자열 패턴을 사용할 경우 찾아낸 문자열들을 다시 사용할 방법은 어떤 것이 있는가?
이럴 때 사용되는 것들이 \\0, \\1, \\2, ... 등이다.
다음과 같은 패턴이 있다고 하자.
([_0-9a-zA-Z]+(\.[_0-9a-zA-Z]+)*)@([0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)
이런 패턴이 있을 경우 전체가 \\0이 된다.
\\1([_0-9a-zA-Z]+(\.[_0-9a-zA-Z]+)*) 이 된다. \\2\.[_0-9a-zA-Z]+)이 된다. \\3([0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*) 이 되고 \\4(\.[0-9a-zA-Z]+)이 된다.
다음과 같은 구문이 있을 경우:

$mail = "abc@abc.net";
$pattern = "([_0-9a-zA-Z]+(\.[_0-9a-zA-Z]+)*)@([0-9a-zA-Z]+(\.[0-9a-zA-Z]+)*)";
$link = ereg_replace($pattern, "<a href = 'mailto:\\0'>\\0</a>",$mail);

결과는 <a href='mailto:abc@abc.net'>abc@abc.net</a>
이 된다.

 

Some exercises

Now here's something to make you busy:

  • modify our e-mail-validating regular expression to force the server name part to consist of at least two names (hint: only one character needs to be changed);
  • build a function call to ereg_replace() that emulates trim();
  • make up another function call to ereg_replace() that escapes the characters '#', '@', '&', and '%' of a string with a '~'.

다음의 문서들을 참고했습니다.
[1] Learning to Use Regular Expressions by Example by Dario F. Gomes
[2] 전정호님의 정규표현식 기초 ( http://www.whiterabbitpress.com/osp/unix/regex.html )
[3] 모두랑의PHP 따라잡기 게시물 ( http://modulang.net/board/view_study.php?code=class&mode=view&no=13 )

출처 : http://www.javastudy.co.kr/docs/techtips/020502.html

javax.util.regex 패키지는 J2SE(Java 2 Platform, Standard Edition) 1.4 에서 새로 추가된 패키지이다.
이 패키지는 정규식(regular expression) 라이브러리를 제공한다.
정규식은 일련의 집합으로 표현되는 문자들의 패턴이고, 패턴 매칭(pattern matching)에서 자주 사용되어진다.
javax.util.regex 패키지에 있는 클래스들은 정규식을 고려하여 문자들의 순서를 일치시켜준다.
정규식 라이브러리를 포함하는 이러한 클래스들은 펄(Perl) 5 정규식 패턴 문법(syntax)을 사용한다.
그리고, 문장을 분석하는 일에 있어 이전에 java.io.StreamTokenizer와 java.util.StringTokenizer 클래스를 이용하여 것 보다 훨씬 더 강력한 방법을 제공한다.

정규식 라이브러리는 다음 세개의 클래스를 포함한다. Pattern, Matcher, PatternSyntaxException.
예외(exception)와 관련된 클래스를 중요하지 않게 생각하면, 정말 필요한 것은 일치하기를 원하는 정규식을 정의하기 위한 클래스(Pattern) 하나와, 주어진 문자열에서 패턴을 찾아내기 위한 또 다른 클래스(Matcher) 하나, 이렇게 단 두개의 클래스뿐이다.

정규식 라이브러리를 사용하기 위해서 가장 필요한 것은 패턴 문법을 이해하는 것이다.
실제로 문장을 분석하는 것은 비교적 쉬운 부분이다. 그러므로, 여기서는 정규식을 구성하는 것들이 무엇인지 살펴보도록 하자.

정규식의 가장 간단한 예제는 리터럴(literal)이다.
리터럴은 정규식에 포함되어 있는 단순한 문자가 아니라, 정규식에 포함된 어떤 특별한 배합이나 식의 일부분이 아닌 문자이다.

예를 들면, 리터럴 "x"는 정규식이다.
리터럴과 매처(matcher), 그리고 문자열을 사용하면, "정규식 'x'가 전체 문자열과 일치하는가?"라고 물어볼 수 있다.
다음은 이러한 질문에 대한 소스 코드이다.

boolean b = Pattern.matches("x", someString);


 만약 패턴 "x"가 someString 과 일치하는 일련의 문자라면, b는 true 값을 가지게 된다.
그렇지 않다면, b의 값은 false가 된다. 홀로 사용되는 경우에, 리터럴은 이해하기 위해서 분석하는 것이 어렵지 않다.
매처(matcher)가 Matcher 클래스가 아닌 Pattern 클래스에 의해서 만들어진다는 것을 주의해야 한다.
정규식이 단 한번만 사용되는 경우에 손 쉽게 사용 하기 위해서, matches 메서드는 Pattern 클래스에 정의된다.
일반적으로, Pattern 인스턴스를 정의하고, 그 Pattern 에 대한 Matcher 인스턴스를 정의한 후에, Matcher 클래스에 정의된 matches 메서드를 사용하는 것이 좋다.

Pattern p = Pattern.compile("x");  
Matcher m = p.matcher("sometext");
boolean b = m.matches();


이 팁의 후반부에 이런 단계를 다룰것이다.

물론, 정규식은 리터럴보다 더 복잡해질 수 있다.
와일드카드(wildcard)와 한정사(quantifier)를 추가하면 더 복잡해진다.
정규식에서는 오직 하나의 와일드카드만이 존재하는데, 바로 마침표(.) 문자이다.
와일드카드는 어느 한 문자와 일치시키기 위해서 사용되어지는데, 심지어는 개행문자도 가능하다.
한정사를 나타내는 문자는 +와 *이다.(원칙적으로는, 물음표 또한 한정사를 나타내는 문자이다)
정규식 뒤에 위치하는 + 문자는 정규식이 한번 또는 여러번 나타나는 것을 인정한다.
* 문자는 + 문자와 비슷하지만, 한번도 나오지 않거나 여러번 나오는 것을 인정한다.
예를 들어, 만약 처음에 j로 시작해서 마지막은 z로 끝나고 이 두 문자 사이에 적어도 하나의 문자가 존재하는 문자열을 찾기를 원한다면, 식 "j.+z"를 사용하면 된다. 만약 j와 z 사이에 문자가 없을수도 있다면, 앞에 나오는 식 대신 "j.*z"를 사용하면 된다.

패턴 매칭은 문자열 내에서 패턴을 만족시키는 가장 긴 문자열을 찾으려고 한다는 사실에 대해 주의해라.
만일 문자열 "jazjazjazjaz"가 주어지고 패턴 "j.*z"과 일치하는 문자열을 요구한다면, 하나의 "jaz" 문자열이 아닌 전체 문자열을 리턴한다. 이러한 특징을 "greedy behavior"라고 부른다.
만약 정규식에 다른 설정이 없다면, 이 특징이 정규식의 기본(default)이 된다.

이제 좀 더 복잡한 예제를 살펴보도록 하자.
소괄호 안에 여러개의 식을 사용하면, 여러문자 패턴들에 대해 일치하는 문자열을 찾게 할 수 있다.
예를 들어, j 다음에 z가 나타나는 문자열을 원하는 경우에, "(jz)" 패턴을 사용할 수 있다.
이렇게 홀로 사용되는 경우에는, 추가적인 기능을 활용할 수 없다. 즉, "jz"와 똑같게 된다.
그러나, 소괄호를 사용함으로써, 한정사를 사용할수 있게 되고 "jz" 패턴이 여러번 나타난다는 표현을 할 수 있다: "(jz)+".

패턴을 사용하는 또 다른 방법은 다른 문자 종류(class)들과 함께 사용하는 것이다.
특정 문자 종류를 사용하면, 각각의 문자들을 하나 하나 지정하는 대신에 가능한 문자들의 범위를 지정할 수도 있다.
예를 들어, j에서 z사이의 어떤 문자와도 일치하기를 원한다면, 대괄호를 사용하여 j-z의 범위를 지정할 수 있다:
"[j-z]". 또한 j와 z 사이에 적어도 한 문자와 일치하면서 이것이 여러번 나타날 수 있는 식이 필요하다면, 위의 식에 예를 들어 다음과 같이, "[j-z]+", 한정사를 추가할 수도 있다.

어떤 문자 종류들은 미리 정의되어져 있다. 이러한 문자 종류들은 공통으로 사용되어지므로, 공통의 단축 문자를 가지고 있다.
미리 정의된 몇개의 문자 종류는 다음과 같다.

\d    숫자: ([0-9])
\D    숫자가 아닌 문자: ([^0-9])
\s    공백 문자: [ \t\n\x0B\f\r]
\S    공백이 아닌 문자: [^\s]
\w    word 문자: [a-zA-Z_0-9]
\W    word가 아닌 문자: [^\w]

문자 종류에 있어서, ^ 문자는 식의 부정을 나타내기 위해서 사용되어짐을 주의하자.

다음은 POSIX 문자 셋이라 불리는 미리 정의된 문자 종류이다. 다음의 항목들은 POSIX 기술 명세서에서 가져왔고, 오직 US-ASCII 문자들과만 함께 동작한다.

\p{Lower}    알파벳 소문자: [a-z]
\p{Upper}    알파벳 대문자: [A-Z]
\p{ASCII}    모든 ASCII 문자: [\x00-\x7F]
\p{Alpha}    알파벳 문자: [\p{Lower}\p{Upper}]
\p{Digit}    10진법의 숫자: [0-9]
\p{Alnum}    문자숫자식의(alphanumeric) 문자: [\p{Alpha}\p{Digit}]
\p{Punct}    구두점(punctuation): !"#$%&'()*,-./:;<=>?@[\]^_`{|}~ 중의 하나
\p{Graph}    눈에 보이는 문자: [\p{Alnum}\p{Punct}]
\p{Print}    프린트 가능한 문자: [\p{Graph}]
\p{Blank}    공백(space) 또는 탭: [ \t]
\p{Cntrl}    제어 문자: [\x00-\x1F\x7F]
\p{XDigit}   16진법의 숫자: [0-9a-fA-F]
\p{Space}    흰공백(whitespace) 문자: [ \t\n\x0B\f\r]

마지막으로 언급되는 문자 종류의 집합은 경계를 나타내는 매처이다.
다시말해 문자(구체적으로 말하면 행, 단어 또는 패턴)들의 순서의 처음 또는 끝이 일치한다는 것을 의미한다.

^     행의 시작
$     행의 끝
\b    단어의 경계
\B    단어가 아닌 것의 경계
\A    입력의 시작
\G    이전 일치의 끝
\Z    마지막 종결자가 없을 경우 입력의 끝
\z    입력의 끝

문자 종류의 표현에 대해 이해하기 위해서 꼭 알아야 할 것은 \ 의 사용이다.
자바 문자열로 정규식을 만들때는, \ 문자를 사용하지 말아야 한다.
그렇지 않고 \ 문자를 사용하면, \ 다음에 오는 문자는 자바 컴파일러에 의해 특별하게 다루어 진다. \ 문자를 사용하기 위해서는, \\ 로 지정하면 된다. 즉, 문자열에 \\ 를 사용하면, 실제로 그 자리에 \ 문자가 오기를 원한다고 말하는 것이 된다.
예를 들어, 만약 문자숫자(alphanumeric) 문자로 이루어진 모든 문자열에 대한 패턴을 사용하기를 원하는 경우, 간단하게 \p{Alnum}* 만을 포함하는 문자열로는 충분하지 않다.
꼭 다음과 같이 \ 문자를 피해야만 한다.

boolean b = Pattern.matches("\\p{Alnum}*", someString);


이름이 함축하고 있는것처럼, Pattern 클래스는 패턴을 정의하기 위한 것이다.
패턴을 정의한다는 것은, 일치하기를 원하는 정규식을 정의하는 것이다.
패턴이 전체 문자열과 일치하는지를 알기 위해서 사용하는 것 보다는, 패턴이 문자열의 다음 부분과 일치하는지를 확인하기 위해서 사용하는 것이 일반적이다.

패턴을 사용하기 위해서는 먼저 패턴을 컴파일 해야 한다. compile 메서드를 사용하면 된다.

Pattern pattern = Pattern.compile(somePattern);


패턴 컴파일에는 약간의 시간이 필요하므로 한번만 하는 것이 현명하다.
Pattern 클래스의 matches 메서드는 매번 호출이 될때마다 패턴을 컴파일한다.
만약 패턴을 여러 번 사용해야 하는 경우라면, Pattern 클래스에서 Matcher 클래스를 얻고 Matcher 클래스를 사용함으로써 컴파일이 여러번 발생하는 것을 피할 수 있다.

패턴을 컴파일한 후에는, 명시된 문자열에 대한 Matcher 클래스를 얻을 수 있다.

Matcher matcher = pattern.matcher(someString);


Matcher 클래스는 전체 문자열을 검사하는 matches 메서드를 제공한다. 이 클래스는 패턴과 일치하는 것을 다음 부분(문자열의 시작이 아니라)에서부터 찾아주는 find() 메서드도 제공한다.

일치하는 것을 찾아낸 후에는, group 메서드를 사용해서 일치하는 것을 얻을 수 있다.

if (matcher.find()) {
  System.out.println(matcher.group());
}

또한 일치하는 것을 검색하고 바꾸기 위해서 matcher 클래스를 사용할 수 있다. 예를 들면, 문자열 내부에서 패턴과 일치하는 모든 부분을 바꾸기 위해서, 다음과 같이 사용할 수 있다.

String newString = matcher.replaceAll("replacement words");


이렇게 하면, 패턴이 발생하는 모든 부분은 교체 단어로 변경된다.

다음은 패턴 매칭에 대한 실제 예제이다.
다음 프로그램은 세 개의 명령줄 아규먼트를 필요로 한다.
첫 번째 인자는 검색하기 위한 문자열이다. 두번째는 검색하기 위한 패턴이다.
세번째는 교체 문자열이다. 검색하기 위한 문자열에서 패턴과 일치하는 모든 부분은 교체 단어로 변경된다.

import java.util.regex.*;

public class MyMatch {
  public static void main(String args[]) {

    if (args.length != 3) {
      System.out.println(
        "Pass in source string, pattern, " +
        "and replacement string");
      System.exit(-1);
    }

    String sourceString = args[0];
    String thePattern = args[1];
    String replacementString = args[2];

    Pattern pattern = Pattern.compile(thePattern);
    Matcher match = pattern.matcher(sourceString);
    if (match.find()) {
      System.out.println(
        match.replaceAll(replacementString));
    }
  }
}


예를 들어, 프로그램이 컴파일 되어 있다면 다음과 같이 실행할 수 있다.

java MyMatch "I want to be in lectures" "lect" "pict"

실행의 결과는 다음과 같다.

I want to be in pictures


그리고 이 프로그램을 실행할때, 명령줄에서 \ 문자의 사용은 불필요하다.
그 이유는 자바 컴파일러가 그러한 정보를 처리하지 않기 때문이다.
예를 들어, 만약 검색하기 위한 문자열이 

"I want to be in lectures\I want to be a star"

위와 같다면, 똑같은 패턴 ("lect")과 똑같은 교체 문자열("pict")을 이용하여 프로그램을 실행하면, 실행의 결과는 다음과 같다.

I want to be in pictures\I want to be a star

패턴 매칭과 정규식에 대한 자세한 정보는, 기술적인 기사(technical article)인 Regular Expressions and the Java Programming Language 를 참고하면 된다.

+ Recent posts