달력

02

« 2012/02 »

  •  
  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  •  
  •  
  •  
Mind-Map 은 생각을 정리하거나 브레인스토밍등 사람의 머리 속에 있는 생각들을 쉽게 문서화 하는데에 도움이 됩니다.
많이들 사용하고 있는 걸로 아는데요... Mind-Map 프로그램 중에 좋은 오픈소스 프로그램이 있어 소개합니다.

사실, 몇 년전에 사용해 보았는데 당시엔 이러한 가치를 모르다가 얼마전에 본격적으로 사용하면서 대단한 가치를 알게 되었습니다.
상당부분 머리 속에 있는 생각들을 XMind 로 정리할 생각입니다^^


이렇게 좋은 게 오픈소스라는 장점까지 가지고 있습니다.
물론 XMind Pro 버전은 유료인데 무료버전만으로도 충분한 활용을 즐길 수 있습니다.

또 이클립스 플랫폼을 기반으로 하고 있는데요... 역쉬 이클립스는 단순한 IDE 가 아닌 것이 이런 툴을 볼 때마다 느껴집니다.
원한다면 이클립스 플러그인으로도 사용할 수 있겠죠? 본인의 능력에 달려 있습니다.
전 그냥 프로그램 형태로 사용합니다. 이클립스 플러그인을 많이 사용하다보니 상당히 느립니다^^;;
더군다나 안드로이드까지 하다보니 메모리를 최소 512M 이상을 차지하고 들어가게 설정해 놓다보니...ㅎㅎ
이클립스 이야기는 여담이었구요...

현재 개인적인 생각을 정리하는 것고 브레인스토밍 모임에서 사용하고 있는데 참 좋다는 생각이 듭니다.
꼭 개발자가 아니더라도 강력하게 추천을 하고 싶네요^^


Posted by korcslewis
2009/07/10 11:50

[소개] fileformat.info Programming Utility2009/07/10 11:50

개발을 하다보면 Hex, Decimal, UTF-8 등의 값들을 알아야 할 때가 있다.

만약 자바 코드 상으로 "\u2212" 를 UTF-16(Hex)로 표현하면?
그리고 UTF-8 으로 표현하면?

위와 같은 질문에 대한 답을 이미지까지 표현해서 대부분의 문자셋으로 표현해 준다.
C/C++, Java, Python source code 형태로도 알려주기 때문에 문자셋을 알아야 할 경우 빠른 시간내에 알 수 있다.

사용자 삽입 이미지


Posted by korcslewis
The flying saucer project 라는 이름으로 XHTML Render 처리를 해 주는 프로젝트가 있다.

브라우저 또는 XHTML, XML 랜더링을 처리하는 프로젝트와 관련된 곳에서 참조할 만한 프로젝트이다.
더군다나 순수 100% 자바로 구현되어있다.
많이 보질 않아서 그런지 조금은 복잡하게 구성이 되어 있는 듯 해서 불편한 느낌도 있다.

중요 컨셉등은 해당 프로젝트 파일에서 문서를 참고하면 되고,
특징 중에 하나는 XHTML DOM 처리는 w3c에서 정의한 Document 인터페이스를 implements 하고 이를 XHTML DOM으로 확장을 해서 사용할 것이라고 생각했는데 그렇치 않다.
그냥 w3c에서 정의해 둔 Document 인터페이스를 구현한 Java XML API를 곧바로 사용한다.

그래서인지 모든 웹 페이지를 다 열어볼 수 있는 것은 아니다.
Javascript 등은 처리하는 구조가 없다. 이유는 Javascript 등을 처리해 주려면 w3c에서 정의한 Document 인터페이스를 실제로 구현하하고 이를 XHTML 또는 HTML 에 맞게 다시금 확장해서 구현해 주어야 하는 데 이에 대한 처리가 없다.
Full browsing 프로젝트가 아니어서 그럴 수도 있을 듯 하다.

흠... 암튼 많이 분석해야 할 프로젝트가 아니어서 여기즘 해서 정리하려고 한다. 프로젝트 중에 중요한 XHTMLPanel 소스가 있는데 이에 대한 클래스 다이어그램을 그려둔 게 있어 올려 놓는다.

사용자 삽입 이미지

더불어서 분석 중에 그려둔 sequence diagram 도 올려 놓는다. Sequence diagram은 HTMLTest.java 를 이용한 디버깅 시에 참고하면 된다.

사용자 삽입 이미지

Posted by korcslewis
현재 개발 방법중에는 ORM이라는 개념을 이용한 Persistence Layer를 처리할 수 있는 프레임워크들이 존재한다. 여기서는 ORM에 대한 이야기를 하려는 듯 생각하는 분이 계실거 같아 미리 아니라고 이야기하고 싶어 잠시 언급했다.

이번 글에서는 이전 글 중에서 "Singleton pattern의 응용 샘플" 에서 설명했던 데이터베이스 커넥션및 access를 줄이는 방법을 제시했었는데, 실제 이를 응용하여 구현시에 이슈가 되는 상황이 있어 잠시 언급해 본다.
간단하게 말하면 '데이터베이스 테이블을 클래스 형태로 구현시의 이슈'이다. ORM과는 다른 관점에서 보아야 한다. 왜냐하면 데이터베이스에 쿼리를 날리는 것이 아니라 순순하게 테이블의 내용을 클래스에 쿼리로 처리하는 것과 동일한 형태로 처리해야 하기 때문이다. 이번에 발생한 이슈는 다음과 같다.

- 검색
- 정렬
- 중복제거

위의 세 이슈는 데이터베이스 쿼리시에 항상 존재하는 것이다. 즉 쿼리로 간단하게 처리할 수 있는 문제인데 이를 클래스 안에서 Collection을 통해서 처리할 때는 어려움이 존재한다. 일단은 해당하는 모든 기능에 대해서 구현의 이슈는 다르기 때문에 해당 이슈를 자바 코드에서 구현한다면 어떠한 키워드가 존재하는지만 소개한다.
다음에 기회가 된다면 예제를 만들어서 각 이슈에 대한 샘플코드를 통해서 소개해 볼 수 있었으면 좋겠다. 일단은 키워드만 언급해 보자.

# 검색
- 데이터베이스 : LIKE문 사용
- 자바코드 : indexOf, substring, compareTo 사용

# 정렬
- 데이터베이스 : ORDER BY (ASC, DESC)
- 자바코드 : 정렬 알고리즘 구현(주로 작은 데이터일 경우 버블 정렬 이용됨)

# 중복제거
- 데이터베이스 : DISTINCT 사용
- 자바코드 : 중복제거 처리


키워드만 언급하면 필자가 생각해도 답답하다. 특히, 중보제거 처리는 실제로 해보니 데이터베이스를 구현한다면 얼마나 어려운지 알게될 정도로 제법 복잡한 느낌을 지울 수가 없었다. 그래도 일단은 위와 같은 구현 이슈를 통해서 처리할 수 있다는 것을 알아두자.

끝으로 "Singleton pattern의 응용 샘플" 와 오늘 이야기한 개념을 사용하면 실제로 Memory DB를 사용하는 것처럼 검색과 리스트 처리가 휠씬 빨라진다. Java Heap에서 돌아가는 것이니 Memory에서 바로 얻어오는 것이나 다름없기 때문이다.
Posted by korcslewis
2009/06/09 10:30

정규표현식 기초 Programming Utility2009/06/09 10:30

정규표현식 관련된 자료를 찾다가 알게된 문서입니다. 출처가 명확하진 않습니다. 문제가 된다면 요청해 주세요... 삭제 또는 링크를 찾아보도록 하겠습니다.


정규표현식 기초

저자 전정호 (mahajjh@myscan.org)

0.1 판 (2001년 11월 20일)

Copyright (c) 2001 Jeon, Jeongho.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.1
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

이 글은 유닉스 사용과 관리에 필수인 정규표현식을 설명합니다. 또, 정규표현식을 처리하는 C 라이브러리도 마지막에 설명합니다.


1. 정규표현식이란?

아마 MS-DOS를 접해본 분이라면 와일드카드(wildcard, 유닉스에서는 glob pattern이라고 부른다)이라고 부르는 *나 ?와 같은 기호에 익숙할 겁니다. a로 시작하는 모든 GIF 파일을 a*.gif와 같이 비슷한 파일명을 일일이 명시하지 않고 지정할 수 있습니다. 정규표현식(regular express, 줄여서 regexp, regex, re)도 MS-DOS의 *나 ?와 같이 패턴을 기술하는 방식입니다. 그러나 정규표현식은 MS-DOS의 와일드카드와는 달리 파일명 뿐만이 아니라 파일 내용을 포함한 일반적인 용도로 사용이 가능하며, 그 기능도 더 강력합니다.

유닉스는 기본적으로 그래픽보다는 문자 기반 인터페이스를 제공하기 때문에, 문자들을 찾거나 다른 문자로 대체하는 도구인 정규표현식은 매우 중요합니다. 사실, 정규표현식을 모르고 유닉스를 사용하는 것이 가능할까란 의문이 들 정도로 정규표현식은 유닉스 사용과 관리의 많은 부분에 적용이 가능합니다. 우리가 자주 사용하는 편집기인 vi와 emacs, 자주 사용하는 도구인 grep과 sed와 awk, portable shell로 불리는 Perl, 자동으로 메일을 정리하는 procmail 등, 정규표현식은 유닉스의 거의 모든 도구와 관련이 있습니다. 개인적으로 뼈아픈 경험뒤에 "멍청하면 손발이 고생한다"는 격언(?)의 적절한 예로 정규표현식을 꼽습니다.

불행히도 도구마다 정규표현식을 지원하는 정도가 조금 차이가 나지만 몇번 시도해보면 이 차이를 알 수 있습니다. 그러면 기본적이고 광범위하게 쓰이는 정규표현식부터 하나씩 알아봅시다.


2. 정규표현식 기초

기본적으로 정규표현식은 다음 세가지로 구별할 수 있습니다.

    * 문자에 해당되는 부분
    * 앞의 해당되는 부분을 반복하는 부분
    * 문자에 해당되지않고 위치나 결합을 나타내는 부분

이제 MS-DOS의 *와 같이 특수한 의미를 가지는 문자들을 만나게 됩니다. 우리가 정규표현식을 배운다는 것은 이런 특수 문자들과 그들의 의미를 아는 것입니다.
2.1. 문자에 해당되는 부분

우선 보통 알파벳과 숫자 등은 그 문자 그대로를 나타냅니다. 물론 대소문자는 서로 구별됩니다.

$ egrep 'GNU' COPYING
                    GNU GENERAL PUBLIC LICENSE
freedom to share and change it.  By contrast, the GNU General Public
the GNU Library General Public License instead.)  You can apply it to
...(생략)...
$

위에서 egrep은 파일들에서 원하는 문자들을 찾는 도구입니다. (흔히들 사용하는 grep의 변종으로 grep보다 다양한 정규표현식을 사용할 수 있습니다.) 첫번째 아규먼트로 원하는 문자를 나타내는 정규표현식을 사용합니다. 여기서 GNU는 정규표현식으로 뒤에 나오는 파일들에서 G, N, U 세 문자가 연이어 나오는 경우를 찾습니다. 여기서 사용한 파일인 COPYING은 자유 소프트웨어 소스코드에서 쉽게 찾을 수 있는 GPL 조항입니다. 결과를 명확하게 하기 위해서 찾은 단어를 굵게 표시했습니다.

그런데 왜 GNU 주위에 따옴표를 했을까요? 여기서 따옴표는 정규표현식에서 쓰이는 *, ?, | 등의 문자들이 쉘에서도 특별한 기능을 하기때문에 이들 문자가 쉘에서 처리되지 않게하려고 필요합니다. 또, egrep 'modified work' COPYING와 같이 찾으려는 패턴에 공백이 포함된 경우에도 따옴표는 이들을 한개의 아규먼트로 처리합니다. 사실 위의 GNU에 따옴표는 필요없지만, 항상 규칙처럼 따옴표를 같이 사용하는 것을 권합니다.

어떤 특정한 문자가 아니라 가능한 여러 문자들을 지정할 수도 있습니다.

$ egrep '[Tt]he' COPYING
  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
software--to make sure the software is free for all its users.  This
...(생략)...
$

위에서 [Tt]는 그 자리에 T나 t가 나올 수 있음을 의미합니다. 이렇게 [와 ]안에 가능한 문자들을 적어줄 수 있습니다.

[a-z]와 같이 [] 안에 -를 사용하여 그 범위 안의 문자들도 지정할 수 있습니다. 예를 들어, [a-zA-Z0-9]는 영문 알파벳 대소문자와 숫자들을 모두 포함합니다. 또, [^a-z]와 같이 [] 처음에 ^를 사용하여 뒤에서 지정된 문자 이외의 문자를 지시할 수도 있습니다. 즉, 이는 영문 알파벳 소문자를 제외한 문자들을 의미합니다.

([a-z]에서 범위는 ASCII 코드값으로 a (97)에서 z (122)까지를 뜻합니다. 만약 [z-a]와 같이 큰 값을 앞에 쓰면 안됩니다. ASCII 코드값은 man ascii로 볼 수 있습니다.)

마지막으로 (보통 행바꿈 문자를 제외한) 어떤 문자에도 대응되는 .이 있습니다. (MS-DOS의 ?와 같습니다.)

$ egrep 'th..' COPYING
 of this license document, but changing it is not allowed.
freedom to share and change it.  By contrast, the GNU General Public
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
...(생략)...
$

이는 th 뒤에 두 문자가 나오는 경우를 찾습니다. 세번째 줄 끝에 This는 대소문자를 구별하기 때문에 패턴에 해당되지않고, the 에서 공백도 한 문자로 취급한 것을 주의하길 바랍니다. 위에서 program will individually obtain patent licenses, in effect making the와 같은 줄을 출력하지 않은 이유는 마지막 the에서 th와 그 뒤의 한 문자는 찾았지만 그 뒤에 문자가 줄바꿈 문자이기 때문에 조건이 만족되지않기 때문입니다.
2.2. 앞의 해당되는 부분을 반복하는 부분

여기서는 *, ?, +을 다룹니다.

*는 바로 앞의 문자를 0번 이상 반복해도 됨을 나타냅니다. 예를 들어, abc*는

    * abccccccccc
    * abc
    * ab

를 모두 만족합니다. 여기서 주의해서 볼 것은 "0번 이상"이기 때문에 마지막 경우와 같이 앞의 문자가 안나와도 된다는 것입니다. (그래서 MS-DOS의 *은 정규표현식으로 .*입니다.)

*와 비슷하게, ?는 앞의 문자가 없거나 하나 있는 경우를 나타내고, +는 앞의 문자가 1번 이상 반복하는 경우를 나타냅니다. 그래서 a+는 aa*와 같습니다.

이제 abc 모두를 반복하고 싶다면 어떻게 해야 되는지 의문이 듭니다. 이 경우 (, ) 괄호를 사용하여 문자들을 묶어주면 됩니다. 그래서 (abc)*는

    * abcabcabcabc
    * abc
*

를 모두 만족합니다. 마지막 예는 0번 반복한 경우로 어떤 문자도 없는 빈 경우입니다. 이제 앞에서 말한 "앞의 문자"라는 말을 정정해야 겠습니다. *, ?, +는 "앞의 문자"에 적용되는 것이 아니라 "앞의 단위"에 적용됩니다. 기본적으로 한 문자는 그 자체로 한 단위입니다. 그래서 abc*에서 *는 바로 앞 문자이자 단위인 c에 적용된 것입니다. 그러나 괄호로 문자들을 묶어서 단위를 만들 수 있고, (abc)*의 경우에 *는 앞의 단위인 abc에 적용된 것입니다.

    주의 위에서 (abc)*가 0번 반복해서 어떤 문자도 없는 것을 나타낼 수 있음을 주의해야 합니다. 정규표현식에서 이런 경우 대상과 관계없이 패턴이 만족한 것으로 판단하기 때문에 egrep '(abc)*' COPYING와 같은 명령어는 COPYING에 abc라는 부분이 없음에도 불구하고 모든 줄을 출력합니다. 즉, egrep '(abc)*' COPYING | wc -l과 wc -l COPYING은 같습니다.

    또, 주의할 점은 정규표현식은 패턴을 만족시키는 가장 긴 부분을 찾는다는 점입니다. 즉, abababab에 대한 정규표현식 (ab)+는 ab나 abab에 대응되지 않고 abababab 모두에 대응됩니다. 이런 행동은 어떻게보면 당연한 것이지만 주의를 하지않으면 문제가 생길 수도 있습니다. 예를 들어, <B>compiler</B> and <B>interpreter<\B>에 대한 정규표현식 <B>.*<\B>는 (의도했을) <B>compiler</B>을 찾지않고 <B>compiler</B> and <B>interpreter<\B> 전체를 찾게 됩니다. 이를 해결하기 위해 <B>[^<]*<\B>을 사용합니다. . 대신에 [^<]를 사용한 것처럼 찾는 대상을 제한하기 위해서 [^...] 형식을 자주 사용합니다.

2.3. 문자에 해당되지않고 위치나 결합을 나타내는 부분

여기서 다루는 ^, $, |는 앞에서와는 달리 특정 문자에 대응하지는 않지만, 위치나 결합의 중요한 기능을 합니다.

우선 ^는 줄의 처음을 뜻합니다.

$ egrep '^[Tt]he ' COPYING
the GNU Library General Public License instead.)  You can apply it to
the term "modification".)  Each licensee is addressed as "you".
the scope of this License.
The source code for a work means the preferred form of the work for
...(생략)...
$

정규표현식의 마지막 문자가 공백임을 주의하길 바랍니다. 만약 이 공백이 없다면 These나 themselves,로 시작하는 줄들도 찾게됩니다. 이렇듯 정규표현식을 적을 때는 자신이 찾길 원하는 부분을 빼먹거나, 자신이 원하는 부분 이외의 것을 포함하지 않도록 주의해야 합니다. 지금처럼 정규표현식을 입력하고 그 결과를 하나씩 살펴볼때는 문제가 없지만, 많은 경우 정규표현식은 스크립트로 많은 문서를 한꺼번에 처리할때 사용하기때문에 주의해야 합니다. 잘못 쓴 정규표현식때문에 원하는 결과를 얻지 못하는 것은 물론이고 원본까지 망치게 되는 경우가 있습니다.

^는 이렇게 [Tt]와 같이 특정 문자에 대응되지는 않지만 원하는 문자가 선택될 수 있게 도와줍니다. 반대로, $는 줄의 끝을 나타냅니다. 그래서 ^$과 같은 정규표현식은 빈 줄을 찾습니다.

|은 기호 앞뒤의 것 중 하나를 선택한다는 뜻입니다. 즉, 문서에서 this(This)나 that(That)을 찾는다면,

    * this|This|that|That
    * [tT]his|[tT]hat
    * [tT]his|hat - 틀림! 이 정규표현식은 [tT]his나 hat을 찾음.
    * [tT](his|hat)
    * [tT]h(is|at)

모두 가능합니다. 세번째와 네번째 경우에서 괄호의 기능을 알 수 있습니다.
2.4. 일반문자와 특수문자

아마도 지금쯤 ^이 두가지 의미로 쓰인다는 것이 이상해 보일 수도 있을 겁니다. 정규표현식에서 쓰이는 문자는 크게 일반문자와 특수문자로 나눠볼 수 있습니다. 여기서 특수문자란 앞에서 다룬 (순서대로) [, ], -, ^, ., *, ?, +, (, ), $, |과 같이 정규표현식에서 문자그대로의 의미로 해석되지 않는 문자들입니다. 반대로 특수문자가 아닌 문자는 일반문자로 G, N, U와 같이 문자그대로의 의미를 가집니다.

여기서 특수문자는 쓰이는 곳에 따라 다릅니다. 자세히 말하면, []안이냐 밖이냐에 따라 특수문자가 달라집니다.

우선 [] 밖에서는 -를 제외한, ^, ., *, ?, +, (, ), $, |이 특수문자입니다. 여기서 ^는 줄의 시작을 의미합니다.

그러나 [] 안에서는 -과 ^만이 특수문자이고, 다른 문자들은 일반문자가 됩니다. 즉, [*?+]는 반복이 아니라 문자그대로 *나 ?나 + 중 하나를 뜻합니다. [] 안에서 (제일 앞에 나오는) ^는 뒤에나오는 조건을 만족하지 않는 문자를 찾는다는 의미가 됩니다.
2.5. 특수문자에 해당하는 문자 사용하기

그렇다면 찾으려는 부분에 특수문자가 포함되있다면 어떻게 할까요? 예를 들어 what?이라는 물음표로 끝나는 문자를 찾고 싶다고, egrep 'what?' ...이라고 하면 ?이 특수문자이므로 wha를 포함한 whale도 찾게 됩니다. 또, 3.14로 찾을때는 3+14 등도 찾게 됩니다.

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

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

첫번째 방법은 보통 escape라고 부르며, 특수문자 앞에 \을 붙여서 특수문자의 특수한 기능을 제거합니다. 두번째 방법은 [] 밖의 많은 특수문자들이 [] 안에서는 일반문자가 되는 점을 이용한 것입니다. 보통 첫번째 방법을 많이 사용합니다.

주의할 점은 첫번째 방법에서 사용하는 \가 뒤에 나오는 특수문자를 일반문자로 만드는 특수문자이기 때문에, 문자 그대로의 \을 나타내려면 \\을 사용해야 합니다. 물론 [\]도 가능합니다.

[] 안의 특수문자는 위치를 바꿔서 처리합니다. 먼저, ^는 [^abc]와 같이 처음에 나와야만 의미가 있으므로 [abc^]와 같이 다른 위치에 사용하면 됩니다. -는 [a-z]와 같이 두 문자 사이에서만 의미가 있으므로 [-abc]나 [abc-]와 같이 제일 처음이나 마지막에 사용합니다.

(grep과 같이 도구에 따라 역으로 일반 문자앞에 \를 붙여서 특수문자를 만드는 경우가 있습니다. 아래 각 도구에 대한 설명 참고.)


3. 정규표현식 고급

고급이라고 제목을 붙였지만 여기서는 도구마다 차이가 나거나 없을 수도 있는 내용을 다룹니다.
3.1. 자세한 반복

반복하는 횟수를 자세히 조정할 수 있습니다.

    * {n} - 정확히 n번 반복. a{3}은 aaa와 같음.
    * {n,} - n번 이상 반복. a{3,}은 aaaa*와 같음.
    * {n,m} - n번 이상 m번 이하 반복. a{2,4}는 aaa?a?와 같음.

물론 (abc){2,4}같이 괄호로 반복할 단위를 지정할 수 있습니다. 여기서 {, }도 *, ?, +와 같이 특수문자임을 주의하길 바랍니다. (엄밀한 의미에서 }은 특수문자가 아닙니다.)
3.2. 기억하기

앞에서 여러 문자를 묶어서 단위로 만드는 괄호는 정규표현식으로 찾은 부분을 기억하여 다른 곳에서 사용할때도 사용합니다. 예를 들어, HTML 제목 테그는 (egrep에서) <[Hh]([1-6])>.*</[Hh]\1>와 같이 찾을 수 있습니다. 여기서 ([1-6])의 (, )는 사이에 대응된 부분을 기억하여 (첫번째 기억된 내용을) \1에서 사용합니다. 즉, <H2>Conclusion</H2>에서 </H2> 외에 </H1>나 </H3> 등은 만족하지 않습니다.

(...)은 여러번 사용할 수 있고 (심지어 겹쳐서도), \n은 기억된 n번째 부분을 지칭합니다. 순서는 기억이 시작되는 (의 순서입니다.

여기에서는 (과 )이 특수문자이고, 그냥 \(와 \)는 일반문자이지만, 도구에 따라 반대인 경우도 있습니다.

이 기능은 또 치환에서 자주 사용됩니다. 아래 vi와 sed 부분을 참고하길 바랍니다.
3.3. 단어 찾기

앞에서 the를 찾으면 the 외에 them 등도 같이 찾는 것을 보았습니다. 그러면 정관사 the만 찾으려면 어떻게 할까요?

간단히 정규표현식 앞뒤에 공백을 추가한  the 를 생각해 볼 수 있습니다. 그러나 이 정규표현식에는 두가지 문제가 있습니다. 첫번째는 탭(tab) 등 다른 공백문자가 있기 때문입니다. 두번째는 이 정규표현식으로 the가 줄 제일 앞이나 제일 뒤에 나오는 경우는 찾지 못하기 때문입니다. 물론 [], ^, $와 |를 복잡하게 결합하여 이들 경우를 모두 처리할 수 있는 정규표현식을 쓸 수 있지만, 자주 사용하는 표현이기 때문에 간단히 할 수 있는 방법이 마련되있습니다.

그것은 \<과 \>로, \<은 공백에서 공백이 아닌 문자 사이, \>는 공백이 아닌 문자에서 공백 사이의 위치를 나타냅니다. 즉, ^나 $와 같이 문자에 해당되지않고 위치만을 나타냅니다. 이제 해답은 \<the\>입니다.
3.4. 단축 표현들

정규표현식에는 이외에도 자주 사용되는 표현에 대한 단축된 형식을 제공합니다. 예를 들어, vim에서 \i는 (C 언어 인식자 이름에서 사용하는 문자인) [_a-zA-Z0-9]와 같습니다. 그러나 이런 단축된 형식은 도구에 따라 많은 차이가 나기때문에 관련 문서를 참고하길 바랍니다.

POSIX.2에서 정의한 단축 표현은 다음과 같습니다. (C 언어에서 <ctype.h>에 선언된 is*() 함수와 비슷한 것을 알 수 있습니다.) 단축된 형식이 나타내는 정확한 값은 locale에 따라 변합니다. 여기서는 영어권에서 사용하는 값을 보입니다. 독일어의 움라우트(ä)와 같이 다른 언어권에서는 다른 값을 가질 수 있습니다.

    * [:alnum:] - 알파벳과 숫자. [a-zA-Z0-9]
    * [:alpha:] - 알파벳. [a-zA-Z]
    * [:cntrl:] - 제어문자. ASCII 값으로 0x00-0x1F와 0x7F
    * [:digit:] - 숫자. [0-9]
    * [:graph:] - 제어문자와 공백을 제외한 문자. ASCII 값으로 0x21-0x7E
    * [:lower:] - 소문자. [a-z]
    * [:print:] - 제어문자를 제외한 문자. ASCII 값으로 0x20-0x7E
    * [:punct:] - [:graph:] 중에 [:alnum:]에 속하지 않은 문자. !, @, #, :, , 등
    * [:space:] - space, tab, carriage return, new line, vertical tab, formfeed. ASCII 값으로 0x09-0x0D와 0x20
    * [:upper:] - 대문자. [A-Z]
    * [:xdigit:] - 16진수에 사용하는 문자. [0-9a-fA-F]

3.5. 눈으로 보는 정규표현식

정규표현식이 패턴을 찾는 과정을 시각적으로 보여주는 프로그램들이 있습니다.

    * Visual REGEXP (Tcl/Tk 사용)
    * RegExplorer (Qt 사용)

4. 정규표현식 사용

이제 이런 정규표현식을 실제로 어떻게 사용하는지 알아봅시다. 평소에 많이 사용하는 vi, grep/egrep/fgrep, sed/awk의 예를 들어보겠습니다.
4.1. vi에서

vi에서 정규표현식은 ':'상태에서 사용합니다. (실제로 이 상태에서 실행하는 명령어는 ed나 ex라는 프로그램이 처리하게 됩니다. 그래서 보통 이 상태를 "ed-모드"라고 합니다.) 문서에서 원하는 패턴을 찾으려면, (커서 다음에서 찾을때) /패턴이나 (커서 전에서 찾을때) ?패턴을 사용합니다.

정규표현식은 문자치환과 결합하여 강력한 기능을 합니다. 문자치환 명령은 다음과 같습니다.

:범위s/변경전/변경후/수정자

"범위"는 명령이 실행될 범위를 나타내며, 보통은 현재 편집하고 있는 문서 전체를 지시하는 (첫번째 줄에서 마지막 줄까지를 뜻하는) 1,$나 줄여서 %를 사용합니다.

뒤에 "s"는 치환(substitute) 명령어입니다.

"변경전"과 "변경후"에 치환할 내용을 입력합니다. "변경전"에 정규표현식을 적습니다. 정규표현식으로 ., *, ^, $, [], \(...\), \<...\>, POSIX.2 단축 표현을 사용할 수 있습니다. 여기서 여러 문자를 묶여서 단위를 만들고 찾은 내용을 기억하는 특수문자가 \(, \)임을 주의해야 합니다. 반대로 (, )가 일반문자입니다. vim(VI iMproved)에서는 vi에 추가로 |, +, (?와 같은) =, {n,m}을 사용할 수 있지만, 앞에 \를 붙여야 합니다. 또, vim에는 \i, \k, \p, \s 등의 단축 표현들이 있습니다.

"변경후"에 \n과 &를 사용할 수 있습니다. \n는 "변경전"에서 n번째 \(...\)에 대응하는 부분이고, &는 "변경전"에 만족한 전체를 나타냅니다. 예를 들어, :%s/\([0-9][0-9]*\) \([Cc]hapter\)/\2 \1/는 문서에서 12 Chapter같은 부분을 Chapter 12와 같이 치환하고, :%s/F[1-9][12]*/&/g는 HTML 문서에서 "F1" ~ "F12"란 단어 모두를 굵은 체로 바꿉니다. (주의! &는 정규표현식의 특수문자는 아니지만 vi의 특수문자이므로, 문자그대로의 &를 사용하려면 대신 \&를 사용해야 한다.) 이외에도 (뒤를 모두 대문자로) \u나 (뒤를 모두 소문자로) \l같은 기능이 있습니다.

"수정자"는 치환 명령의 세부사항을 결정합니다. 필요한 것만 뒤에 적어주면 됩니다.

    * g (global) - 한 줄에서 정규표현식을 만족하는 부분을 여러개 찾았을 때 모두다 치환한다. 이 수정자를 사용하지 않으면 처음 것만 치환한다.
    * c (confirm) - 만족하는 정규표현식을 찾았을때 치환하기 전에 확인한다.
    * i (ignore-case) - 대소문자를 무시하고 찾는다. 즉, :%s/[aA][bB][cC]/XXX/ 대신에 :%s/abc/XXX/i를 사용할 수 있다.

마지막으로 주의할 점은 치환명령어가 / 문자로 각 부분을 구분하기때문에 "변경전"이나 "변경후"에 / 문자를 사용하려면 \/ 같이 써야합니다. 필요하다면 / 대신 다른 문자를 사용해도 됩니다. 예를 들어, :%s/\/usr\/local\/bin\//\/usr\/bin\//g 대신 :%s#/usr/local/bin/#/usr/bin/#g가 알아보기 더 쉽습니다.
4.2. grep/egrep/fgrep에서

grep은 Global Regular Expression Print(ed 명령어로 :g/re/p)의 준말로 입력에서 원하는 정규표현식을 찾는 명령어입니다. grep에는 egrep과 fgrep이라는 변종이 있습니다. 전통적으로 egrep은 grep 보다 더 다양한 정규표현식을 지원하고, fgrep은 정규표현식을 지원하지 않고 빨리 검색하기 위한 명령어입니다. GNU grep에서 egrep은 grep -E, fgrep은 grep -F와 같습니다.

grep과 egrep 모두 ., *, ?, +, {n,m}, ^, $, |, [], (...), \n, \<...\>, POSIX.2 단축 표현을 지원합니다. 단, grep은 ?, +, {, |, (, )를 일반문자로 보기때문에 특수문자로 사용하려면 앞에 \를 붙여야 합니다.
4.3. sed/awk에서

...


5. Perl 정규표현식

...


6. 정규표현식 응용


7. 정규표현식 프로그래밍

프로그래밍 언어와 관계없이 정규표현식을 프로그래밍하는 방식은 비슷하다. 먼저, 사용할 정규표현식을 "컴파일"한다. 여기서 컴파일한다는 말은 정규표현식을 실행파일로 만든다는 말이 아니라 정규표현식을 처리하기위한 내부 자료구조를 만든다는 뜻이다. 이 자료구조를 사용하여 정규표현식을 빠르게 처리할 수 있다. 컴파일한 후 컴파일된 자료구조를 사용하여 원하는 검색과 치환을 하게된다. 마지막으로 사용이 끝난 자료구조를 반환한다. 프로그래밍 언어에 따라 이 과정이 필요없는 경우도 있다.
7.1. C 언어

glibc(GNU C Library)에 정규표현식을 위한 다음과 같은 함수들이 있다.

#include <regex.h>

int regcomp(regex_t *compiled, const char *pattern, int cflags);
int regexec(regex_t *compiled, char *string, size_t nmatch, regmatch_t matchptr[], int eflags);
void regfree(regex_t *compiled);
size_t regerror(int errcode, regex_t *compiled, char *buffer, size_t length);

먼저 함수와 자료형이 선언된 regex.h를 포함한다. regcomp()는 pattern에 주어진 정규표현식을 컴파일하여 결과를 compiled에 저장한다. cflags 인자는 정규표현식 처리 옵션들을 지정한다. 정상적으로 실행되면 0을 반환하고, 오류가 발생한 경우 0이 아닌 값을 반환한다.

표 1. cflags 인자

REG_EXTENDED  
REG_ICASE  대소문자 구별안함
REG_NOSUB  괄호로 찾은 부분 영역 기억하지 않기
REG_NEWLINE  여러 줄을 처리. 이 옵션이 없다면 .에 행바꿈 문자가 포함되고, (사이에 행바꿈 문자가 있더라도) ^과 $는 찾으려는 문자열의 시작과 끝만을 의미한다.

실제 정규표현식 검색은 regexec()으로 한다. string에 정규표현식으로 검색할 문자열을 주면
7.2. Java
7.3. Python
7.4. PHP



참고 자료

    * grep(1), regex(3), regex(7), fnmatch(3) manpage
    * GNU C Library 문서
    * Learning the vi Editor, 6th ed, Linda Lamb, O'Reilly, 1998
    * sed & awk, 2nd ed, Dale Dougherty & Arnold Robbins, O'Reilly, 1997

from: http://www.whiterabbitpress.com/osp/unix/regex.html
Posted by korcslewis
2009/05/12 17:49

소개 - [UPnP]CyberLink for Java Programming Utility2009/05/12 17:49

UPnP stack을 구현한 많은 구현체, 오픈 소스들이 존재한다.
그 중에 몇 가지를 살펴보았는데, UPnP 구현과 stack을 잘 구현해 놓은 것은 Intel에서 제공하는 툴이 있다. C/C++ 코드 뿐만 아니라, Java 코드도 제공하는 것으로 되어 있는데, 예제 구현도 C/C++ 로 되어 있는 것으로 봐서는 Java 코드는 전체 오픈소스로 제공하지 않는다.
더군다나 생성되는 코드가 Intel.jar 라이브러리에 대해서는 공개되어 있지 않다. 아니면 내가 찾질 못했거나...

그래서 Java로 구현된 UPnP 오픈소스 중에 보게 된 것이다. CyberLink for Java 이다.
많은 곳에서 소개도 하고 있고, 실제 코드를 받아서 실행해보니 Stack과 Contrl Point는 잘 동작한다.

UPnP Java 버전을 고려하는 사람이면 참고할 만한 곳이다.

Posted by korcslewis
Flickr Open API를 각 언어별로 구현해 둔 오픈소스들이 많이 있다. 야후 플리커에서 소개하고 있는 킷 중에 Flickr-j 오픈소스를 이용해서 플리커의 이미지를 검색하는 예제소스를 구현해 보자.

플리커, 피카사, 아마존, 구글 API등을 이용한 새로운 서비스를 만드는 것을 매쉬업이라고 한다. 아마도 대부분은 이에 대한 것을 잘 알고 있으리라 생각하고 생략한다. 그냥 나도 이번 기회를 통해서 매쉬업 서비스를 중점적으로 연구해 볼까? 하는 생각을 해 본다. 물론 언제나 시간적 여건이 마련되어야 하지만...^^;;
암튼, 매쉬업 서비스를 위한 첫 발걸음을 본격적으로 내딛었다고 생각한다. 그럼 다음의 소스를 간략하게 실행해 보자.
컴파일, 실행 전에 반드시 flickrapi.jar 를 classpath에 포함시켜야 한다. 이클립스 환경에서 수행하면 쉽게 수행할 수 있다.

/**
 * FlickrSearch.java
 *
 * Flickr-j를 이용한 Flickr 검색 애플리케이션
 *
 */
package net.wiseant.flickr.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import com.aetrion.flickr.Flickr;
import com.aetrion.flickr.FlickrException;
import com.aetrion.flickr.REST;
import com.aetrion.flickr.RequestContext;
import com.aetrion.flickr.auth.Auth;
import com.aetrion.flickr.auth.Permission;
import com.aetrion.flickr.photos.Photo;
import com.aetrion.flickr.photos.PhotosInterface;
import com.aetrion.flickr.photos.SearchParameters;
import com.aetrion.flickr.util.IOUtilities;

/**
 * @author Sang Hyup Lee
 * @version 1.0
 *
 */
public class FlickrSearch {

    static String apiKey;
    static String sharedSecret;

    Flickr flickr;
    REST rest;
    RequestContext requestContext;
    Properties properties = null;

    public FlickrSearch() throws ParserConfigurationException, IOException {
        InputStream in = null;
        try {
            in = getClass().getResourceAsStream("/setup.properties");
            properties = new Properties();
            properties.load(in);
        } finally {
            IOUtilities.close(in);
        }

        flickr = new Flickr(properties.getProperty("apiKey"), properties.getProperty("secret"), new REST());
        requestContext = RequestContext.getRequestContext();
        requestContext.setSharedSecret(properties.getProperty("secret"));

        Auth auth = new Auth();
        auth.setPermission(Permission.READ);
        auth.setToken(properties.getProperty("token"));

        requestContext.setAuth(auth);
        Flickr.debugRequest = false;
        Flickr.debugStream = false;
    }

    public void search() throws FlickrException, IOException, SAXException {
        Map starredPhotos = new HashMap();
        Map blockedPhotos = new HashMap();

        PhotosInterface photosInterface = flickr.getPhotosInterface();

        SearchParameters params = new SearchParameters();

        // tag setter
        // params.setTags(new String[] {"java", "jsp"});
        params.setText("java");

        int perPage = 10;
        int pageIndex = 0;
        List photoList = photosInterface.search(params, perPage, pageIndex);
        List photos = new ArrayList();

        System.out.println("===== Search result =====");
        for ( Object object : photoList ) {
             Map photoMap = new HashMap();
             Photo photo = (Photo)object;

             System.out.println("Photo id : " + photo.getId());
             System.out.println("Photo title : " + photo.getTitle());
             System.out.println("Photo small square url : " + photo.getSmallSquareUrl());

             photoMap.put("url",    photo.getSmallSquareUrl());

            String id = photo.getId();
             photoMap.put("id", id);

             Boolean starred = (Boolean) starredPhotos.get(id);
             photoMap.put("starred", starred == null ? Boolean.FALSE : starred);
            if (blockedPhotos.get(id) == null) {
                 photos.add(photoMap);
            }
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        FlickrSearch flickrSearch = new FlickrSearch();
        flickrSearch.search();

        System.exit(0);
    }

}

위의 소스를 실행하기 전에 setup.properties를 properties로 설정해 두는 부분이 있다. setup.properties의 양식은 다음과 같다.

apiKey = your api key
secret = your secret
token = your token

setup.properties 에서 value 값으로 사용되는 것들은 플리커 API 키를 신청하면 얻을 수 있다. token은 flickr-j 의 examples 소스의 AuthExample.java 를 통해서 얻어올 수 있다.

특별히 설명이나 어려운 부분없다. 대부분은 flickr-j 에서 제공함으로 API를 호출하기만 하면 된다. example을 통해서 여러 가지 살펴보고 있는데, 블로그를 통해서 소개할 만한 부분들은 언제든지 소개할 것이다.

이것으로 첫 매쉬업 서비스를 런칭해 보았다^^
참, flickrj 는 다음의 주소에서 구할 수 있다.

Posted by korcslewis
현재 지메일은 POP3, IMAP, SMTP를 지원한다. 때문에 POP3를 이용하여 각 메일 클라이언트를 사용하여 지메일의 메일을 읽어올 수도 있고, SMTP를 사용하여 메일을 보낼 수 있기도 하다. POP3, SMPT등을 기본으로 제공하지 않는 국내 포털들의 메일 계정으로는 많은 일을 하지 못하지만 지메일을 이용해서는 다양한 일을 해 볼 수 있다.

그러면 여기에서는 지메일 SMTP를 이용한 자바 메일 API를 사용하여 메일을 발송해 보자. 여기서 사용되는 소스는 "보안 계정으로 설정된(아이디/패스워드가 있는) SMTP 서버를 이용한 자바메일발송" 글과 동일하게 사용된다. 단지 G-Mail SMTP 설정하는 부분이 다를 뿐이다.

자  이제 G-Mail SMTP를 사용한 자바메일 발송 예제를 살펴보자. 간단한 애플리케이션 형태이다.

/**
 * 
 */
package net.wiseant.mail;

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

/**
 * @author Sang Hyup Lee
 * @version 1.0
 *
 */
public class GMailSender {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        String host = "smtp.gmail.com";//smtp 서버
        String subject = "G-Mail을 이용한 메일발송";
        String from = "gmail-id@gmail.com"; //보내는 메일
        String fromName = "이름";
        String to = "receiver-id@daum.net";
        String content = "G-Mail을 이용한 메일 발송 예제입니다. 감사합니다.";

        try{
            // 프로퍼티 값 인스턴스 생성과 기본세션(SMTP 서버 호스트 지정)
            Properties props = new Properties();
            
            // G-Mail SMTP 사용시
            props.put("mail.smtp.starttls.enable","true");
            props.put("mail.transport.protocol", "smtp");
            props.put("mail.smtp.host", host);
            props.setProperty("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
            props.put("mail.smtp.port", "465");
            // props.put("mail.smtp.user", from);
            props.put("mail.smtp.auth", "true");
            
            MyAuthenticator auth = new MyAuthenticator("gmail-id@gmail.com", "gmail-password");
            
            Session mailSession = Session.getDefaultInstance(props, auth);
            
            Message msg = new MimeMessage(mailSession);
            msg.setFrom(new InternetAddress(from, MimeUtility.encodeText(fromName,"UTF-8","B")));//보내는 사람 설정
            InternetAddress[] address = {new InternetAddress(to)};
            
            msg.setRecipients(Message.RecipientType.TO, address);//받는 사람설정
            
            msg.setSubject(subject);// 제목 설정
            msg.setSentDate(new java.util.Date());// 보내는 날짜 설정
            msg.setContent(content,"text/html;charset=euc-kr"); // 내용 설정 (HTML 형식)
            
            Transport.send(msg); // 메일 보내기
            
            System.out.println("메일 발송을 완료하였습니다.");
        } catch ( MessagingException ex ) {
            System.out.println("mail send error : " + ex.getMessage());
        } catch ( Exception e ) {
            System.out.println("error : " + e.getMessage());
        }
        
    }

}


Properties 클래스의 put 메소드에 들어가는 properties 설정을 G-Mail SMTP에 맞추어주면 된다. 그리고 이전 글에서 살펴본 것처럼 보안계정이 설정된 SMTP 서버를 사용할 경우에는 해당 아이디와 패스워드를 셋팅해 주어야 한다. 따라서 MyAuthenticator 클래스에 아이디와 패스워드를 생성자 properties로 인스턴스를 만들어 사용해 주어야 한다.

MyAuthenticator 클래스도 소스를 살펴보자.

package net.wiseant.mail;

import javax.mail.Authenticator;

/**
 * @author Administrator
 *
 */
public class MyAuthenticator extends Authenticator {

    private String id;
    private String pw;

    public MyAuthenticator(String id, String pw) {
        this.id = id;
        this.pw = pw;
    }

    protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
        return new javax.mail.PasswordAuthentication(id, pw);
    }

}
Posted by korcslewis
메일과 관련된 가장 중요한 두 가지의 프로토콜은 POP3와 SMTP이다.
보통 javamail API를 이용하는 경우 보내는 서버인 SMTP만을 이용하는 것이 대부분이다. javamail API를 이용하여 메일을 보내는 것은 다른 세션이나 글을 통해서 알아보면 좋겠다.
여기서는 POP3를 콘솔(윈도우이면 명령 프롬프트) 상에서 커맨드를 통한 간단한 예를 보여주고자 한다.

먼저, POP3의 포트는 110, SMTP 포트는 25라는 것은 기본적으로 알고 있을 필요가 있다. 대부분은 알고 있으리라 생각된다.

다음은 콘솔의 telnet 명령어를 통해서 POP3에 접속하여 로그인 한 후, 메시지의 리스트 갯수를 얻어오는 과정을 설명한 것이다. 참고로 "파란색"은 클라이언트(콘솔)에서 입력하는 것이고 "주황색"은 서버의 응답을 표시한다.

console>telnet mail.wiseant.net 110

그러면 콘솔이 telnet 으로 바뀌면서 다음과 같은 POP3 서버가 보내는 메시지를 볼 수 있다.

+OK Welcome to POP3 Server

즉, 접속은 POP3 서버에 정상적으로 접속을 했다는 말이다. 이제부터 POP3와의 대화를 시도한다고 생각하면 된다.

대화의 첫 시도에서는 사용자 아이디와 패스워드를 입력하여 인증을 받아야 한다.

USER wiseant
+OK
PASS user-password
+OK

해당하는 POP3 서버가 OK 메시지를 보낸 것을 확인할 수 있다. 이제 내 메일 계정의 상태를 알아보자.

STATE
+OK 45 23788250

총 45개의 메시지를 보관하고 있는데, 용량이 23788250 byte 라고 응답한다. 다음은 LIST 명령어를 내려보자.

LIST
+OK 45 messages:
1 134268
2 2522
3 15290277
4 185246
5 64591
6 3425
7 9285
8 1658899
9 4447
10 3107
11 37775
12 11400
13 199801
14 39669
15 4404
16 58860
17 5568
18 6572
19 2532742
20 36726
21 8345
22 485500
23 10774
24 2349
25 919516
26 7470
27 1327001
28 126218
29 19088
30 3028
31 2756
32 3585
33 15647
34 4574
35 2682
36 1383
37 1033
38 2568
39 5214
40 173646
41 158514
42 160119
43 43596
44 13259
45 801

45개의 메시지에 대해서 각각의 용량을 알려준다. 이제 실제로 메시지(메일)의 내용을 살펴보자.

RETR 1
.
..
...
------=_NextPart_000_0003_01C8497C.77820080--

번호에 해당하는 메시지를 쭈욱 보여준다. 그렇다면 삭제는 어떻게 할까? 다음과 같이 해 주면 된다.

DELE 1
+OK message 1 deleted

보통은 메일 클라이언트를 이용하여 POP3 설정을 하여 메일을 읽기 때문에 이와 같은 방법은 잘 사용하지 않는다. command 시대의 전유물이라고 생각하는 경우가 있기도 한데, 메일에 대한 RFC 문서나 메일 솔루션의 개발, 디버깅 시에는 유용한 방법이 될 것이다.
Posted by korcslewis