2009년 6월 29일 월요일

단상 - 뒤돌아보기

생각하는 사람이 진실을 발견한다. 하지만 진실을 즐기고 그 거룩한 선물을 이용하는 사람은 행동하는 사람이다.
- 베니토 페레스 갈도스 -

오늘 아침 위의 짧은 문장을 통해서 올 한 해를 뒤돌아볼 수 있는 시간을 가져야 겠다는 생각을 해 본다.
마음이 평안하고 조금은 여유가 있을 때 시간을 내어볼 필요가 있다라는 생각이 든다.

지금 내게 필요한 것은 '생각의 정리', '평안', 그리고 '행동'이다.

2009년 6월 24일 수요일

지금 나는 어디로 가고 있는가? - 무릎팍도사, 안철수편을 보고

지난 주 "무릎팍도사 - 안철수편"이 엄청난 사회적 이슈를 불러 일으켰다.
TV를 보았던 많은 사람들은 대부분 안철수 교수님의 삶을 통해서 많은 반성과 배움을 얻은 듯 하다.

평소에도 관심이 많았던 나로서도 기사를 통해서 얻는 것보다는 직접 보는게 좋을 거 같아서 생애 최초로 iMBC.com 을 통해서 유료로 다시보기를 통해서 "무릎팍도사 - 안철수편"을 보았다(집에서는 TV가 나오지 않는 관계로ㅋㅋ).

일전에 데브멘토 개발자 컨퍼런스에서 "개발자여! 꿈을 가져라!"는 기조 연설을 직접 들었던 적이 있었다.

사용자 삽입 이미지

당시에 기억도 작은 체구지만 몸에 벤 겸손함과 작지만 분명한 목소리로 자신의 소신을 너무나도 적절하게 잘 전달해 주었던 것이 기억난다.

그리고 "무릎팍도사 - 안철수편"에서도 마찬가지로 순수한 마음과 겸손함, 경영에 대한 올바른 철학, 자신의 소신을 묵묵히 걸어가시는 모습등을 너무나도 평온하게 전달하는 느낌이었다.
예능프로여서 많이 웃고 즐거웠지만 진지하게 내 자신을 돌아보는 계기가 되었다.

그리고 내겐 중간 중간에 계속해서 뇌리를 두드리는 것이 '나는 지금 어디로 가고 있는가?'하는 질문이었다.
태그로 적어본다면 '의지', '기회', '소신' 등.

스스로를 돌아보는 질문에 지근 내게 올바른 길로 걸어가고 있는가? 하는 질문이 계속해서 던져졌다.

"경영자로서의 꿈"
늘상 소망하고 준비한다고 하지만 실제로는 행동하지 않는 듯한 내 모습

'기회는 모든 사람에게 주어진다. 하지만 준비된 자만이 그 기회를 잡을 수 있다'는 말을 되뇌이면서 지금 내가 잘 하고 있는가? 하는 고민이 끊이질 않는다.

'CEO!' 안철수 교수님에겐 성공과 부의 모습이 아닌 사회에 공헌하고 행복한 자리였다.
그러면 내게 'CEO'란 무엇인가?

다시금 많은 고민을 하게 된다.
지금 여기서 내가 걸어가는 길이 안철수 교수님과 같은 경영자로서 걸어갈 수 있는 길일까?

2009년 6월 18일 목요일

사진에서 숨은 아들은 찾아보세요!

경품은 없습니다ㅋㅋ
얼마전 사진을 정리하다가 개구장이 큰 아들이 장난을 치고 있는 틈을 타서 엄마 찍어둔 모양입니다.
혼자 보기엔 너무 아까워서 올려 봅니다^^


자~~ 아래의 사진에서 숨은 아들을 찾아보세요!
사용자 삽입 이미지

사용자 삽입 이미지


정답을 모르시는 분을 위해서 큰 사진으로 보여드리죠...
히야... 찰나의 순간으로 두 개의 사진을 찍었나 봅니다...ㅋㅋ




사용자 삽입 이미지


참 개구장이 녀석이죠? 장난감 통엔 왜 들어갔을까요?ㅎㅎ

2009년 6월 17일 수요일

KT, "7월초 아이폰 공급 사실 무근"

국내 아이폰 출시에 대한 관심이 예상처럼 뜨거운 분위기이다.

이전기사 - [단독]아이폰 내달 상륙... KT서 독점공급 - 의 입력시간 2009.06.17 17:40:11 인데 머니투데이에서 다른 기사를 내 놓았다.


머니투데이의 기사 입력 시간을 살펴보니 2009.06.17 18:57
이전 기사 시간보다 확실히 이후이긴 하나 시간적으로 기자들이 사실을 확인하고 기사를 쓰기에 촉박한 시간을 다투고 있는 느낌을 받을 수 있다.

이래저래 아이폰 출시에 대한 국내의 관심이 뜨거운 것을 한눈에 알 수 있다.
아무튼 국내에도 올 해 안에는 아이폰이 출시될 예정은 확실한 분위기이다.
가격 면에서도 그리 높은 단가로 출시되는 것은 아닌 듯 하다.

흠... 시간이 나면 아이폰 프로그래밍을 해 보아야 할텐데...
당분간은 안드로이드 개발에 매진을 해야겠지?

드디어 아이폰 내달 국내상륙

사용자 삽입 이미지


이르면 다음달에 애플 아이폰이 국내 상륙할 예정이라고 한다.

예상한대로 KT를 통해서 독점으로 공급될 예정...
이로써 KTF와의 합병이후 단말 시장과 이통시장에 큰 변혁이 예상된다.

우리나라에서 아이폰의 상륙이 얼마나 큰 영향을 미칠지는 올해 판가름 할 수 있게 되었다.

흐흐... 올해 연말이면 안드로이드폰도 나올 예정이니 재밌는 시간들이 다가오고 있다.



2009년 6월 16일 화요일

[링크]1.5_r1 자바 소스코드 이클립스에서 보기

Android 1.5_r1 자바 소스코드 이클립스에서 보기

안드로이드 개발에 필수적인 작업일 수도 있다. 최근 단순히 안드로이드 Application 개발 이슈가 아닌 이미 잘 만들어진 안드로이드 애플리케이션 소스를 보아야 할 일이 많아졌다.

이전에 찾아놓았던 자료인데 이클립스에 연결만 해 두고 포스팅을 해 두지 않아서 연결해 둔다.

http://anddev.tistory.com/50


2009년 6월 15일 월요일

Android 전체 소스코드 빌드

안드로이드 전체 소스를 git에서 다운받아서 빌드하고 테스트 해보자.

개발환경
- OS : Ubuntu 8.10 desktop

구글도 우분투를 권장하고 있을 뿐만 아니라 안드로이드 개발에 있어서는 우분투에서 개발하는 것이 좋은 점이 많다. 특히 에뮬레이터 네트워크 이슈등으로 인해서.

안드로이드 전체 소스코드 빌드 방법에 대해서는 다음의 글을 참고하여 직접 해 본 것이다.
http://www.kandroid.org/board/board.php?board=androidsource&command=body&no=4

1. 환경설정

위의 kandroid.org 의 글에서처럼 필요한 패키지들을 설치해 준다.
다음의 예제들은 우분투 데스트탑 터미널이나 원격 터미널을 통해서 실행할 수 있다.

sudo apt-get install flex bison gperf libsdl-dev libesd0-dev libwxgtk2.6-dev build-essential zip curl
sudo apt-get install valgrind

해당 패키지들을 설치하는 중에 의존성 에러등이 발생할 경우에는 해당 의존 패키지들을 설치해 준다. 대부분은 'yes'를 통해서 설치만 해 주면 된다.

다음으로는 Java SDK가 설치되어 있지 않다면 Java SDK를 설치해 주어야 한다. kandroid.org 글에서는 우분투 apt-get을 통한 SUN SDK를 설치하도록 되어 있는데 여기서는 아래의 링크를 통해서 SUN Java SDK 버전을 다운받아서 설치하도록 한다.

우분투에서 Java 설치하기

여기서 잠시 살펴볼 것은 현재 버전이 JDK1.6.0_14 임으로 update-alternatives를 다음과 같이 실행해 준다.

sudo update-alternatives --install "/usr/bin/java" "java" "/usr/local/jdk1.6.0_14/bin/java" 1
sudo update-alternatives --config java

기존에 기설치된 JDK가 존재할 경우 -config 옵션을 통해서 선택하게 해 준다. 방금 설치한 JDK1.6.0_14 버전을 선택해 준다.

java -version
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) Client VM (build 14.0-b16, mixed mode, sharing)

"java -version" 커맨드를 통해서 자바 버전을 확인해 본다.

다음은 repo 명령어를 실행하기 위해서 계정 내에서 bin 디렉토리를 만들어 준다.

mkdir bin

지금까지 설정한 환경에 대한 셋팅을 마무리한다.

vi ~/.bashrc


해당 파일에 다음을 추가한다.

export LANG=c
export PATH=/home/<user_home>/bin:$PATH:.
export JAVA_HOME=/usr/local/jdk1.6.0_14
export ANDROID_JAVA_HOME=$JAVA_HOME

시스템을 재로그인 하거나 원격일 경우 로그아웃 한 후 재접속해 준다.


2. git 설치

git는 최근의 연구대상이다. 인터넷을 통해서 많은 자료를 찾을 수 있다. 다음의 자료를 통해서 대략적인 것을 이해할 수 있다.

git 사용자 설명서

우분투 터미널에서 다음과 같은 명령어를 통해서 git 를 설치하고, android.git의 repo를 로컬로 옮겨온다.

sudo apt-get install git-core gnupg
curl http://android.git.kernel.org/repo >/home/<user_home>/bin/repo
repo 커맨드를 실행할 수 있는 파일로 변경해 준다.

chmod a+x ~/bin/repo


3. Android 소스 다운로드 하기

안드로이드 소스를 다운로드하기 위한 로컬의 저장소를 만들어주고, 해당 디렉토리로 이동한다.

mkdir mydroid & cd mydroid

android 소스가 존재하는 git 주소로 초기화 해 준다.

repo init -u git://android.git.kernel.org/platform/manifest.git

원격 git 주소와 로컬의 저장소를 동기화 해 준다. 이 과정이 안드로이드 소스를 다운로드 받는 것이다.

repo sync

이 과정을 통해서 안드로이드 소스를 다운받게 되는데 다운로드를 받아보니 현재(2009.06.16) 기존으로 1.4GB 이다. 따라서 식사시간 전에 sync를 걸어주는 게 좋다.
간혹 네트워크 상태가 좋치 않거나 서버와의 접속이 끊어지는 경우가 있다. repo sync 명령어를 계속해서 입력해 주면 된다.

사용자 삽입 이미지

4. Android 소스 빌드

안드로이드 소스 코드를 다 받게 되면 make 명령어를 통해서 빌드하게 된다.

cd mydroid
make


현재 테스트해 본 결과로는 별도의 에러가 발생하지 않는다. 하지만 Java 관련 명령어의 링크가 제대로 동작하지 않아 에러를 발생하는 경우를 볼 수 있었다. 예를 들면 다음의 에러와 같다.

build/core/product_config.mk:261: WARNING: adding test OTA key
============================================
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=
============================================
build/core/copy_headers.mk:15: warning: overriding commands for target `out/target/product/generic/obj/include/libpv/getactualaacconfig.h'
build/core/copy_headers.mk:15: warning: ignoring old commands for target `out/target/product/generic/obj/include/libpv/getactualaacconfig.h'
make: execvp: /bin/bash: Argument list too long
host Java: clearsilver (out/host/common/obj/JAVA_LIBRARIES/clearsilver_intermediates/classes)
/bin/bash: javac: command not found
make: *** [out/host/common/obj/JAVA_LIBRARIES/clearsilver_intermediates/javalib.jar] Error 41


javac 커맨드를 찾을 수 없다는 말이니깐 "sudo update-alternatives --install "/usr/bin/javac" "javac" "/usr/local/jdk1.6.0_14/bin/javac 1" 명령어를 통해서 해결할 수 있다.

javah, javadoc, jar 에 대해서도 동일한 에러가 발생하였음으로 위의 명령어를 통해서 해결할 수 있다.

빌드 시간이 2시간 남짓 걸렸다...ㅡㅡ;; 암튼 많은 시간을 투자해야 하니 처음부터 긴 시간을 각오하는게 좋다^^


5. 빌드 테스트

전체 소스 빌드를 성공하였다면 img 파일이 정상적으로 동작하는 지 살펴보아야 한다. "mydroid/out/target/product/generic" 디렉토리를 살펴보자. 아래의 그림과 같이 system.img, ramdisk.img, userdata.img 파일을 포함한 다양한 파일, 폴더가 생성되어져 있어야 한다.

사용자 삽입 이미지


이제 ramdisk.img, system.img, userdata.img 파일들을 이용하여 실제로 SDK의 에뮬레이터를 구동해 보면 된다.
이를 위해서 Android SDK1.5_r2 를 설치해 주어야 한다.

http://developer.android.com/sdk/1.5_r2/index.html

각 플랫폼에 맞는 SDK를 설치해 주면 된다. 만약 SDK1.5 버전을 최초로 설치하였다면 AVD(Android Virtual Device)를 생성해 주어야 한다. 다음과 같이 해 준다. 이 때 Android SDK 설치 디렉토리의 "ANDROID_HOME/tools" 디렉토리에서 해 주어야 한다.

./android create avd -n <device_name> -t 2

위의 명령어 옵션 중에 -n 다음에 붙는 것은 이미지의 이름이고, -t 다음에 붙는 것은 SDK 버전을 뜻한다. 인터넷 상에 Android SDK 1.5 설치 관련된 자료가 많으니 참고하길 바란다.

이제 모든 준비가 되었다. 에뮬레이터를 먼저 구동해 보고 Android SDK 1.5가 정상적으로 설치되었는지 확인해 본 다음 위에서 빌드한 파일로 테스트해 보자.

./emulator -avd <device_name>


위의 명령어를 통해서 에뮬레이터를 실행하면 SDK로 설치한 img 파일로 실행된다. App 들이 정상적으로 동작하는지 확인해 보자.

다음은 에뮬레이터를 종료시키고 빌드한 img 파일을 이용하여 에뮬레이터를 동작시켜 보자.

./emulator -adv <device_name> -ramdisk <file> -system <file> -initdata <file>


형태로 실행하면 되는데 <file> 에 해당하는 img 파일들을 절대경로로 지정해 주면 된다.
-ramdisk 다음의 파일을 "ramdisk.img", -system 다음의 파일은 "system.img", -initdata 다음의 파일은 "userdata.img" 이다.

Android SDK 1.x 에서는 -system 옵션이 -image 였다. 이전의 자료들을 보면 -image 옵션으로 설명한 곳들이 있으니 주의하기 바란다.

에뮬레이터가 정상적으로 구동하고 application 들이 정상적으로 구동되면 안드로이드 전체 소스 빌드를 성공한 것이다.

2009년 6월 14일 일요일

부서이동

약 한달 가량 지루한 고민의 연속이었다.
육체적으로는 하나도 피곤한 게 없었으나(거의 일을 하지 않은 관계로), 정신적인 데미지로 따지면 정말 피곤함에 쓰러질 지경이었다.

일단 어느 정도는 마무리를 지어야 할 듯 하다.
회사 내에선 부서를 이동하여 안드로이드를 개발및 PM 역할을 하게 되고,
개인적으론 마음을 일단락짓고 업무에 집중을 해야 할 듯 하다.

아직 많은 이야기를 담기는 버거운 듯 하다.
재밌는 일과 즐거운 일이 많아졌으면 하는 소망을 가져본다.

2009년 6월 8일 월요일

원점에 서다 : Back to the Basics

사용자 삽입 이미지
이 책에서는 단도직입적으로 저자의 커리어 등을 통해서 알 수 있듯이 대부분 원가절감 또는 문제해결에 있어서의 원칙을 말하고 있다.

원칙 또는 문제해결의 시작은 어디서부터일까? 하는 질문이 있다면 그 해답이 '목적'에 있다라고 말하고 있는 책이다.
좀더 정확하게 말하면 '잘못되거나 분명치 않는 목적이 아닌 분명하고 올바른 목적'에 근거한 경영을 말하고 있다.

분명 '목적'이란 것은 인간이 살아가는 데 필수적인 요소이다. 어느 누가 목적없이 삶을 살아가고 있을까? 그리고 이런 유기체적인 인간의 모습 그대로 경영에서도 목적이 필요함을 인식시켜 준다.
특히, 책의 대부분에서 저자가 컨설팅하며 보았던 문제, 그리고 해결했던 문제들을 가지고 다루고 있기 때문에 현장감을 높이고 있다.

그리고, 저자의 직업의 특성상(?) - 저자가 자주 이야기하는 대목이다 - 하나의 제품 또는 사물을 볼 때에도 곧바로 문제점 그리고 그에 대한 해결책들을 찾는 것을 보면 창조적인 목적도 동시에 다루고 있다.
그러고보면 대부분의 문제해결에서 드러나는 건 '창조적인 발상 + 제품 또는 서비스의 정확한 목적이해'에서 오는 것 같다.

결국, 기업의 입장에서는 소비자(고객)이 필요로 하는 것을 극대화 시켜서 좋은 제품 또는 서비스를 만들고 다시금 불편하거나 필요한 것을 소비자로부터 피드백을 받고 더 나은 제품과 서비스를 제공함으로 이루어지는 선순환 구조를 가질 필요가 있다. 사회의 한 부분으로서의 기업과 소비자의 올바른 관계가 위와 같아야 하지 않을까?

그런면에서 원칙에 입각한 창조적인 목적 달성에 주안점을 두어야 할 기업의 입장에 있는 사람들이라면 한 번쯤 읽어 볼 만한 책이다.

서블릿을 이용한 간단한 Thread 프로그램 구현해 보기

일반적인 웹 애플리케이션을 개발할 때 서블릿을 이용할 경우 자바 Thread를 이용할 경우가 별로 없는 듯 하다. 왜냐하면 서블릿을 기존의 HTML을 렌더링하는 일반적인 용도로 사용할 경우에는 Thread를 이용할 경우가 확실히 작아진다.

하지만, 타 기종간의 애플리케이션을 기반으로 한 서버 사이드의 영역으로서 J2EE 프로젝트에서 서블릿을 사용할 경우 실행 파일과 같은 역할을 수행하는 경우가 많다. 실제로 서블릿은 CGI 형태의 코딩에 어울리는 코딩 기법이기도 하다. 서버 사이드의 실행 파일이라고 할 수 있겠다.

사실 서블릿은 그 용도가 아주 다양한데, 웹 애플리케이션을 공부하는데 있어 J2EE를 시작한다면 JSP를 공부하기 보다는 먼저 서블릿을 공부하는게 올바른 순서라고 할 수 있다.
서두가 길어졌다. 제목대로 간단하게 서블릿 코드 상에 Thread를 구동시켜 보자.

먼저, 간단한 Thread 코드를 살펴보자. [CountRunable.java]
/**
 * CountRunable.java
 *
 * Runnable 인터페이스를 구현한 간단한 Count Thread 프로그램
 */
package net.wiseant.test.thread;

/**
 * @author SangHyup LEE
 * @version 1.0
 *
 */
public class CountRunable implements Runnable {

    private int count = 0;
    
    public CountRunable(int count) {
        this.count = count;
    }
    
    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("COUNT : " + count);
    }

}


[CountRunable.java]는 Runable를 implements 하여 구현한 간단한 Thread 코드이다. 그럼 CountRunable 클래스를 호출하는 서블릿 코드를 살펴보자.

[CountThreadServlet]
/**
 * CountThreadServlet.java
 *
 * CountRunable 쓰레드를 수행하기 위한 servlet 프로그램
 */
package net.wiseant.test.web.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.wiseant.test.thread.CountRunable;

public class CountThreadServlet extends HttpServlet {

    private static int count = 0;
    
    /**
     * The doGet method of the servlet. <br>
     *
     * This method is called when a form has its tag value method equals to get.
     *
     * @param request the request send by the client to the server
     * @param response the response send by the server to the client
     * @throws ServletException if an error occurred
     * @throws IOException if an error occurred
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        while ( count <= 20 ) {
            CountRunable countRunable = new CountRunable(count);
            Thread countThread = new Thread(countRunable);
            countThread.start();
            count++;
        }
        
    }

}


doGet 메소드에 간단하게 Runable를 implements하여 구현한 CountRunable를 실행하고 있는 코드가 보인다. 일반적으로 자바 Thread를 수행하는 코드와 다를 것이 없다.
코드를 실제로 수행해보면 count를 static 변수로 선언하였기 때문에 각 Thread가 수행되는 순서에 따라서 증가하대 그 순서가 serial  하지 않고, Thread가 수행되는 순서에 따라서 실행되는 것을 알 수 있다. 다음은 예제로 돌려본 실행하면이다.

COUNT : 0
COUNT : 1
COUNT : 2
COUNT : 6
COUNT : 4
COUNT : 3
COUNT : 8
COUNT : 13
COUNT : 11
COUNT : 10
COUNT : 5
COUNT : 9
COUNT : 7
COUNT : 14
COUNT : 12
COUNT : 15
COUNT : 16
COUNT : 18
COUNT : 19
COUNT : 20
COUNT : 17

중복제거 알고리즘 구현- 자바 샘플코드 포함

"데이터베이스 테이블을 클래스 형태로 구현시 이슈" 글에서 잠시 언급했던 것 중에 '중복제거'와 관련된 알고리즘을 소개한다. 물론 자료구조나 알고리즘 수업을 들어본 사람이 있으면 쉽게 구현이 가능하다. 사실 현업에 오래있는 동안 이러한 로직을 잘 사용하지 않으면 기억이 잘 나지 않는다.

나 역시 그런 면이 있었는데, 그냥 혼자서 곰곰히 생각해 본 대로 구현해 보았다.
이전 글에서 실제로 예제를 들을 수 있었으면 좋겠다고 언급한 적이 있는데, 이번 예제에서는 테이터 베이스의 클래스를 예로 들진 않았다. 하지만 샘플 코드를 통해서 알 수 있는 것은 테이블을 자바 클래스로 매핑을 하였다면 필시 테이블의 PK에 해당하는 키를 하나의 properties로 표현하였을 것이다. 즉, 중복제거에 필요한 column을 표현한 것인데 이를 샘플예제의 array index 라고 생각하면 된다. 이를 통해서 응용을 하여 실제 업무에 사용해도 무방하리라 생각된다.

본 샘플예제에서 한 가지 개선할 점은 결과 배열의 크기를 미리 예측하기 힘들어 원본 배열과 동일한 크기를 할당한다는 것이다. 자바는 동적 배열을 지원함으로 크기를 미리 알 수 있다면 불필요한 메모리 크기를 사용하지 않아도 된다. 이에 대해서는 좋은 아이디어가 있으면 함께 해 주었으면 좋겠다.

그리고 참고적으로 실무에선 데이터베이스의 테이블을 클래스 매핑시켜 이러한 중복제거를 사용시에는 배열을 이용할 경우가 없을 것이다. 대부분 List 형태로의 Collection을 이용함으로 이러한 처리는 오히려 배열을 사용하는 것보다는 쉽게 구현이 가능할 뿐만 아니라 메모리 할당에 대해서도 고민할 필요가 없을 것이다.

자, 이제 실제로 테스트 코드를 살펴보자.

/**
 * DuplicationRemoveTest.java
 */
package net.wiseant.test.algorithm;

import java.util.Random;

import junit.framework.TestCase;

/**
 * @author Sang Hyup Lee
 * @version 1.0
 *
 */
public class DuplicationRemoveTest extends TestCase {

    private Random random;
    private int min = 1;
    private int max = 10;
    
    private static int ARRAY_LENGTH = 20;
    
    private int getRandomNumber()
    {
        return random.nextInt((max - min) + 1) + min;
    }
    
    int[] array = new int[ARRAY_LENGTH];
    
    /* (non-Javadoc)
     * @see junit.framework.TestCase#setUp()
     */
    protected void setUp() throws Exception {
        super.setUp();
        random = new Random();
        
        for ( int i=0; i < array.length; i++ ) {
            array[i] = getRandomNumber();
            System.out.println("array[" + i + "] : " + array[i]);
        }
    }
    
    public void testDuplicationRemove() {
        int[] duplicationRemoveArray = new int[ARRAY_LENGTH];
        int index = 0;
        
        System.out.println("============ PROCESS ============");
        
        for ( int k=0; k < array.length; k++ ) {
            if ( index == 0 ) {
                duplicationRemoveArray[index] = array[k];
                index++;
            } else {
                boolean isDuplication = false;
                for ( int m=0; m < duplicationRemoveArray.length; m++ ) {
                    if ( array[k] == duplicationRemoveArray[m] ) {
                        isDuplication = true;
                        break;
                    }
                }
                
                if ( isDuplication ) {
                    System.out.println("Duplication number : " + array[k]);
                } else {
                    duplicationRemoveArray[index] = array[k];
                    index++;
                }
            }
        }
        
        System.out.println("============ RESULT ============");
        
        for ( int j=0; j < duplicationRemoveArray.length; j++ ) {
            if ( duplicationRemoveArray[j] == 0 ) break;
            System.out.println("duplicationRemoveArray[" + j + "] : " + duplicationRemoveArray[j]);
        }
    }

}



위의 소스 코드는 TDD 형태로 구현되어져 있다. TDD에 대해서는 다음에 기회가 있으면 언급을 하기로 하고 그냥 이렇게도 TDD를 사용할 수 있다는 것도 Tip 이라고 할 수 있겠다.

위의 소스 코드의 한 예를 살펴보면 다음과 같다.

array[0] : 2
array[1] : 3
array[2] : 2
array[3] : 2
array[4] : 1
array[5] : 2
array[6] : 8
array[7] : 7
array[8] : 9
array[9] : 4
array[10] : 1
array[11] : 3
array[12] : 1
array[13] : 3
array[14] : 1
array[15] : 4
array[16] : 7
array[17] : 1
array[18] : 8
array[19] : 9
============ PROCESS ============
Duplication number : 2
Duplication number : 2
Duplication number : 2
Duplication number : 1
Duplication number : 3
Duplication number : 1
Duplication number : 3
Duplication number : 1
Duplication number : 4
Duplication number : 7
Duplication number : 1
Duplication number : 8
Duplication number : 9
============ RESULT ============
duplicationRemoveArray[0] : 2
duplicationRemoveArray[1] : 3
duplicationRemoveArray[2] : 1
duplicationRemoveArray[3] : 8
duplicationRemoveArray[4] : 7
duplicationRemoveArray[5] : 9
duplicationRemoveArray[6] : 4


결과를 가만히 살펴보면 중복된 숫자를 디스플레이하고 최종 중복이 제거된 배열의 결과를 디스플레이 하고 있다. 실제로 동작하는 코드에서는 크기가 정해진 배열에 랜덤으로 1~10에 해당하는 숫자를 발생시켜 원본 배열 - array - 에 저장해 두고 이를 결과 배열 - duplicationRemoveArray - 에 중복이 제거된 숫자만을 저장한다.

로직은 그렇게 어려운 것이 아니니 더 이상의 설명은 필요가 없으리라 본다. 끝으로 실제업무에선 List와 같은 Collection 객체를 사용하게 될 경우가 많다고 했는데, 필요시에 이와 같이 테스트 후에 적용해 보는 것이 좋다는 생각이 든다.

데이터베이스 테이블을 클래스 형태로 구현시 이슈

현재 개발 방법중에는 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에서 바로 얻어오는 것이나 다름없기 때문이다.

정규표현식 기초

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


정규표현식 기초

저자 전정호 (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

Ubuntu(우분투) 8.04 IP 설정

우분투 데스크탑은 레드햇과는 많이 다르더군요...ㅡㅡ;;
그래서 이리저리 찾아가면서 다음과 같이 했더니 되었습니다^^
다음은 우분투 8.04 기준입니다.

1. sudo vi /etc/network/interfaces
파일을 열어 다음과 같이 본인의 네트워크에 맞게 셋팅해 준다.

auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
address 192.168.0.107
netmask 255.255.255.0
network 192.168.0.0
broadcast 192.168.0.255
gateway 192.168.0.1



2. sudo /etc/init.d/networking restart

확인도 해 볼겸 다음과 같이 명령어를 입력해 보는 것도 좋다.

3.ifconfig -a

eth0가 정상적으로 잡혀 있는지 확인해 보면 된다.

2009년 6월 4일 목요일

고민의 연속

여러가지 문제로 고민 중에 있다.

고민 중에 드는 생각은

"마음을 움직이게 할 수 있는 사람"

"지금 필요한 것은 의지와 열정"

"선택"


2009년 6월 2일 화요일

[소개]스타플, http://www.starpl.com

재미있는 사이트 하나를 소개하고자 한다.


굳이 카테고리를 적용해 보자면(태그 형태로) 다음과 같다.

SNS, 싸이월드, 블로그, 위젯

어쩌면 웹2.0을 표방하는 또 하나의 사이트라고 할 수 있는데, 이 사이트를 알게된 경로가 나에겐 특이하다.
스타플을 개발한 업체는 "위콘, weconize"라는 곳이다.


특이하게도 회사를 먼저 알게 되고 스타플을 알게 된 것이다.
회사 홈페이지를 블로그를 통해서 보여주고 있는데, 여러가지로 재밌는 회사란 느낌을 많이 받을 수 있다.
이런 회사에서 일하는 것을 좋아한다. 그래서 관심을 가지고 있던 회사인데 스타플이란 서비스를 하고 있어서 가입도 해보고 살펴본 것이다.

국내에선 싸이월드, 블로그와 같은 사이트이 성격을 띄고 있지만 접근하는 방법이 우주를 컨셉으로 하고 있다는 것이 차이점이다.
꾸미기 좋아하는 사람들은 재미있는 서비스가 될 거 같다.



스타플