perl에 관하여 논문 형식으로 작성한 것이다.
대표적인 짜집기 논문이다 -0-;; 이러면 안되는데.. 후
----------------------------------------------------------------------------------------
문자열 처리 전문가 Perl
오치환*
*동명정보대학교 컴퓨터공학과
Specialist Perl at String Handling
Chy-Hwan Oh*
*Department of Computer Engineering, Tong-Myong University, Busan , Korea
요 약 : 본 논문에서는 Perl에 관하여 논한다. 특히 Perl은 문자열을 다루는데 최고의 언어라고 할수 있다. Perl에 관하여 알아보고, 사용 되는 분야에 대해서 논할 것이다. 그 중 문자열 처리에 관한 부분에 집중하여 논한다.
핵심용어 : 문자열 처리, perl
ABSTRACT : In this paper, We describe about Perl. Especially perl is the best language for string handling. We inquire out perl and talk about where it using. Among them, We describe about String Handling
KEY WORDS : String handling, perl
1.서론
Perl(Practical Extraction and Reporting Language)은 임의의 형태를 갖춘 텍스트 파일을 읽고, 이 파일에서 의미있는 정보를 추출하여 이 정보를 근거한 레포트를 출력하는 제반 작업에 최적화된 언어이며, 시스템 관리에도 매우 적절한 언어이다.
또 perl은 아름다운 언어보다는 실용적인 언어를 지향한다. perl은 (이 언어의 개발자가 말하기를) C, sed, awk 그리고 sh와 같은 언어나 프로그램들의 가장 좋은점들을 취합한 언어이므로, 이 언어, 프로그램들에 익숙한 사람들이라면 perl 사용에도 역시 큰 어려움을 느끼지 않을 것이다. (컴퓨터언어 사학자라면 perl의 문법이 아마 csh이나 Pascal 혹은 BASIC-PLUS와도 닮았다고 생각할 것 같다.)
문장구조는 C 언어와 상당히 흡사하다. 대개의 Unix용 프로그램들과는 달리, Perl은 우리가 사용할 데이터 크기에 한계를 두지 않는다. 메모리만 충분하다면 한 파일의 내용 전체를 변수하나에 넣을수 있다.. 재귀(Recursion)는 무제한 반복될 수 있다. 해쉬(혹은 조합배열)에서 사용하는 테이블은 성능저하를 일으키지 않으면서 무한대로 커질 수 있다. Perl은 섬세한 패턴일치(pattern matching) 기술을 이용하여 많은 양의 데이터를 빠르게 검색할 수 있다. 비록 텍스트 검색에 최적화되어 있긴하지만 이진데이터도 다룰 수 있으며 해쉬와 같은 형태의 dbm 파일도 만들어낼 수 있다. 또 setuid를 사용하는 스크립트의 경우 C 프로그램에서 사용하는 것보다 안전한데, 데이터 흐름을 추적하는 메커니즘이 말도 않되는 대부분의 보안상의 빈틈을 막아주기 때문이다.
만일 sed나 awk, sh (혹은 bash)를 사용한 프로그램을 만들어 문제를 풀어 나갈 때, 그 문제가 이런 프로그램의 능력으로는 극복하기 어렵다거나, 혹은 프로그램이 좀 더 빨랐으면 하는데, C언어를 사용해서 어리석게도 시간낭비를 하고 싶지 않다면, Perl이 해결책을 줄 것이다. 참고로, sed나 awk 스크립트를 Perl 스크립트로 바꿔주는 변환기들을 (인터넷에서) 찾을 수 있다.
Perl은 더구나 HTML이나 사용자입력과 같은 텍스트를 다루는데 아주 강력한 기능을 가지고 있으므로 문자열 위주의 데이터를 다루기에는 최적의 언어라 할 수 있으며, UN*X, MVS, VMS, MS-DOS, Macintosh, OS/2, Amiga, Windows NT 등 여러 가지 Operating system에 Porting되어 있으므로 소스파일의 이식성이 뛰어나다.
또 Perl은 강력한 사용자층을 가지고 있다. GURU급의 사용자, 개발자로부터 초보자에 이르기까지 Perl의 확장과 사용에 많은 시간과 정렬을 제공하는 사용자들이 CPAN의 많은 모듈을 만들고, 사용하고 고쳐나가면서 Perl의 발전에 힘쓰고 있다. 이 개발자, 사용자층이 있는 이상, Perl은 계속해서 발전할 것이다.
C, C++같은 언어를 써보면 그 언어에서 문자열과 메모리의 관리가 얼마나 짜증나는 일인지 안다. Perl은 그런 복잡한 것이 없다. 즉, 언어의 관리 보다는, 언어의 유용성에 초점을 맞춘 언어이다.
Perl은 무료이다. Perl을 빛내주는 모듈들도 99.9999%가 무료이다. 그리고 많은 프로그램들이 무료로 제공되고 있다.
1993년부터 시작해서 Perl 버젼 5는 거의 완전히 새로 만들어졌고, 그로 인하여 다음과 같은 내용들이 추가되었다.
- 수많은 모듈들을 이용한 모듈화(modularity)와 재사용성(reusability) : 이는 라이브러리를 좀 더 효율적으로 사용할 수 있는 방법이다.
- C 언어의 한부분으로 사용하거나, C 언어를 이용하여 확장 가능.(embeddable and extensible)
- 다른 데이터(DBM같은)를 변수에 통합하여 변수를 조작하는 것으로 원래의 데이터를 조작할 수 있는 기능. (roll-your-own magic variables (including multiple simultaneous DBM implementations))
- 서브루틴으로 내장 함수 재선언(override)하기. 서브루틴의 동적인 선언(autoload). 프로토타이핑.
- 단순한 scalar, 배열, 해쉬이상의 복잡한 데이터 구조와 익명의 함수. (arbitrarily nested data structures and anonymous functions)
- 객체지향적 프로그래밍(object-oriented programming)
- C 코드 또는 Perl 바이트코드로의 변환(compilability into C code or Perl bytecode)
- 쓰레드 지원(support for light-weight processes (threads))
- 국제화, 지역화, 유니코드(support for internationalization, localization, and Unicode)
- 변수의 사전적 범위지정(lexical scoping) - my 연산자를 이용한 변수의 접근범위 제한.
- 향상된 정규식(regular expression enhancements)
- 향상된 디버거와 내장 편집기를 사용하여 사용자와 상호작용 하는 환경 (enhanced debugger and interactive Perl environment with integrated editor support)
- POSIX 1003.1 호환 라이브러리(POSIX 1003.1 compliant library)
2. 사용분야
perl은 한 때 HTTP, HTML이 부상하면서 CGI를 위한 언어로 각광 받았다. 하지만, CGI로서의 역할은 PHP와 같은 언어나, JSP, ASP 등의 등장으로 미미해진다.
그러나, 웹의 부상과 CGI로서의 역할이라는 다소 표면적이고 번지르르한 허명뒤에는 또 다른 역할이 있었다.(물론 아직도 꼭 CGI가 필요한 곳에서는 막강한 힘을 발휘하고 있다. Apache 서버의 mod_perl이 바로 그 중 하나일 것이다.)
그 것이 바로 "관리"를 위한 역할이다. 관리는 시스템 관리만을 위한 것이 아니다. 시스템 관리는 물론이고, 데이터 관리, 웹 관련 관리 등 다양한 관리를 포함한다.
웹 관련 관리에 관해 간단히 말해 보자면, 실제적인 사용자와의 인터페이스는 다른 언어에 맡긴다지만, 가장 쉽게 생각할 수 있는 perl의 또 다른 역할이 웹사이트의 방문기록을 분석하는 것이다. 웹의 방문기록을 counter 프로그램을 이용하여 저장한다면, 그것은 약간 비효율적이다. 그 보다는 Log 파일에 저장된 내용을 추후에 분석하는 것이 대개는 좋다. 이럴 때 perl을 사용하면 일이 매우 쉬워진다. 다른 언어로 하기는 비효율적일 것이다. 왜냐하면 문자열을 다루는 데에는 perl이 최고이기 때문이다. 또, 만일 어떤 페이지의 내용이 30분이나 1시간에 한 번씩 바뀌어야 한다면(날씨관련 사이트처럼), 혹은 하루에 1, 2번 바뀌어야 한다면, perl을 이용하는 것이 가장 좋을 것이다. 주기적으로 어떤 조건에 따라서 새로운 페이지를 생성하여 HTML로 저장한 다음 사용하는 것이, 매번 php나 jsp를 이용하여 생성하는 것보다는 훨씬 경제적이고 속도도 많이 빠를 것이다. 서버가 아닌 클라이언트의 입장이 될 수도 있다. 어떤 페이지를 주기적으로 다운로드 받아서 분석한 후 데이터로 저장해야 한다면, 최고의 문자열처리 언어인 perl을 이용하는 것이 좋을 것이다. 게다가 대상 페이지가 자주 변하는 것이라면 융통성있는 perl이 막강한 힘을 부여해 줄 것이다.
시스템 개발에서도 prototype 언어로 활용할 수도 있고(예를 들어 메일 서버를 구현하기 전에 perl로 실제로 구현해 보고, 이를 C와 같은 언어로 porting 하는 경우), 다른 언어의 code 생성에 template 기반의 tool로 사용할 수도 있는 등 그 쓰임새가 엄청나다.
3. Perl as language
본 장에서는 perl 의 기초문법에 대해 알아본다.
3.1. 펄의 데이터 타입 및 특수변수
3.1.1 Perl
1986년 Unix 프로그래머인 Larry Wall이라는 사람이 자신이 맡은 업무를 보다 쉽게 처리할려고 하는 과정에서, 자신의 일을 해결할수 있는 가장 간결하고 적합한 유틸리티를 만들게 되었다, 그래서 그는 새로운 프로그래밍 언어를 발명하게 되었고, 그후 펄이란 이름으로 빠르게 번져나갔다고 한다.
3.1.2 Date Type
펄의 데이터 타입은 스칼라(scalars), 배열(arrays), 결합형 배열(associative arrays) 이렇게 세가지가 있다.
그리고, 모든 변수명을 대소문자를 구별한다. $temp와 $Temp는 서로 다른 변수가 된다.
3.1.2.1 스칼라(Scalars)
펄은 다른 언어와는 달리 모든 수와 문자열을 이 스칼라로 표현한다.
스칼라 변수명은 '$'표로 시작한다.
$a = 2 ; - 숫자
$b = "test" ; - 문자
$temp = "My name is Soo" ; - 문자열
3.1.2.2 배열(Array)
스칼라의 집합을 배열이라 하며, '@'표시로 시작한다. 다음과 같이 괄호안에 ',(쉽표)'로 각 변수는 구별된다.
@name = ("chanho", "unicorn", "jongbum", "Hongsun") ;
@id = (1234, "unisoo", 0987) ;
또한 배열안에 또 다른 배열이 정의 될수 있다.
@a = (1, 2) ;
@b = (3, 4) ;
@c = (@a, @b) ;
이렇게 하면 @c에는 1, 2, 3, 4의 값을 가지게 된다.
여기서 주의할 것은 배열을 스칼라에 할당 했을 경우이다.
@a = (1, 2) ;
@b = @a ; <-- @b는 당연히 1, 2의 값을 가지게 된다.
$c = @a ; <-- $c는 1, 2 의 값이 아닌 2의 값을 가지게 된다.
$c는 array내의 값을 가지는 것이 아니라, array 내의 값들의 개수를 가진다.
만일 배열내의 임의의 값에 접근하고자 할경우는,
@a = ("name", "school", 4, "Korea") ;
$temp1 = $a[0] <-- name
$temp2 = $a[3] <-- Korea 의 값을 가지게 된다.
배열은 0번째부터 값을 가지게 된다. 그러니깐 위 경우는 값이 4개이므로 $a[0]은 'name'이 되고, $a[1]은 'school'이 되고, $a[2]는 '4'가 되고, $a[3]은 'Korea'의 값을 가지게 된다.
3.1.2.3 결합형 배열(Associative Arrays)
이 결합형 배열은 위의 배열과는 조금 다른 모습을 가진다. 위의 배열은 그냥 값만 가지는것에 비해, 결합형 배열은 키(key)와 이 키에 대응하는 값을 가진다. 결합형 배열은 '%'표시로 시작되며, 다음과 같다.
%animal = ("name", "ppoby", "number", "two", "race", "jabjong") ;
이렇게 표현되어 지며, 배열의 각 요소들에 접근하기 위해서는
$temp = $animal{"name"} ; <-- 'ppoby'
$temp_1 = $animal{"number"} ; <-- 'two'
$temp_2 = $animal{"race"} ; <-- 'jabjong' 의 값이 할당되어 진다.
즉 위의 예에서, 'name은 ppoby를 number는 two를 race는 jabjong' 과 쌍을 이루게 된다. 다시 말해서 위의 배열에서 키는 'name, number, race'가 되고, 각 키의 값으로는 'ppoby, two, jabjong'이 되는 것이다.
3.1.3 특수 변수(Special Variables)
여기서는 펄에서 대단히 많이 쓰이는 변수 몇가지만 소개하기로 한다.
3.1.3.1 환경 변수(Environment Variables) - %ENV
이 변수는 환경 변수를 가지고 있는 결합 배열이다. 이 변수를 이용해서 아주 쉽게 환경 변수의 내용을 얻을 수 있다. 폼으로 부터 보내어진 데이터나, 접속한 사용자의 브라우져 정보, 클라이언트의 IP 주소, 자신의 서버가 사용하고 있는 웹 프로토콜의 버젼 등 쉽게 그 정보를 얻을수 있다.
사용 방법은
$ENV{'원하는 환경변수'} ;
$temp = $ENV{'REMOTE_ADDR'} ; 접속한 사람의 IP주소를 알수 있다.
자주 쓰이는 변수를 정리해 보았다.
CONTENT_LENGTH
폼과 함께 제출된 데이터 내용의 바이트 수
CONTENT_TYPE
폼과 함께 제출된 데이터 내용의 타입
HTTP_USER_AGENT
웹 브라우져 소프트웨의 버젼
PATH_INFO
요청과 함께 웹 서버로 보내어진 경로
QUERY_STRING
폼 제출로 부터 데이터를 담고 있는 문자열
REMOTE_ADDR
클라이언트의 IP주소
REMOTE_HOST
클라이언트의 호스트 이름
REQUEST_METHOD
스크립트를 호출하는 방법(GET, POST)
3.1.3.2 프로그램 인자 - @ARGV
펄 명령 라인 내에서 지정된 인자들은 특수 배열인 @ARGV를 통해 펄 스크립트 내로 전달 되어 진다.
3.1.3.3 현재 라인 - $_
특수 변수 '$_'는 입력의 현재 라인을 저장한다.
3.1.3.4 $(숫자) - $1, $2, $3 .....
특수 변수 $n는 패턴에 의해 매칭된 변수를 저장할 때 사용 되어진다. 첫번째 매칭된 문자열은 $1에, 두번째 매칭된 문자열은 $2에... n 번째 매칭된 문자열은 $n 에 기록 된다.
3.2. 제어문, 파일제어
3.2.1 연산자(Operator)
우선 Perl에서 쓰이는 대표적인 논리적 연산자에 대해서 알아보겠다. 논리적 연산자로는 ||(or) 와 &&(and)가 있다.
이 두개의 연산자는 양쪽에 오는 값에 따라 참, 또는 거짓의 값을 되돌린다.
||(or)
|| 연산자는 '또는' 이란 뜻으로, 'A || B' 일 경우 'A 또는 B 가 ...' 라는 뜻이 되며, A,B 두 값중 하나만 참이 되더라도 전체의 값은 참이 된다. 두개의 값이 모두 거짓일 경우 전체의 값이 거짓이 된다.
&&(and)
&& 연산자는 '그리고' 이란 뜻으로, 'A && B' 일 경우 'A 그리고 B 가 ...' 라는 뜻이 되며, A,B 두 값중 하나만 거짓이 되더라도 전체의 값은 거짓이 된다. 두개의 값이 모두 참일 경우 전체의 값이 참이 된다.
그리고 이 두개의 연산자는 이런 비교의 경우가 아닌, 펄 문장들을 결합시키는 역할 또한 합니다. 이때 '||'연산자는 '그렇지 않다면..' 이 되고 '&&' 연산자는 '그렇다면..' 으로 해석될수 있다.
$value > 5 || print "값이 너무 작습니다.";
$value < 5 && print "값이 너무 작습니다.";
이 두 문장 모두 같은 역할을 하는데,
위의 예는 'value의 값이 5보다 크지 않다면...' 이 되고,
아래의 예는 'value의 값이 5보다 작다면....' 이 된다.
3.2.2 제어문(Flow Control)
Perl 에서 쓰이는 대표적인 제어문은 while, foreach, if등이 있고, 그 외에 until, for, unless등이 있다.
3.2.2.1 while
while()은 ()내의 조건문이 거짓이 될때 까지 문장을 반복 수행한다.
$n=100;
while ($n > 0) {
print "$n\n";
}
3.2.2.2 foreach
foreach 문은 배열을 처리할때, 아주 유용하다. foreach 다음에 오는 배열이 널값(아무런 값도 없을때)일때 까지 문장을 수행한다.
@name = ("Kim", "young", "soo");
foreach $temp(@name) {
print "$temp\n";
}
위 문장에서 $temp 변수는 처음에는 'kim'의 값을 가지며, 두번째는 'young'의 값을, 세번째는 'soo'의 값을 가지며, 네번째에서 $temp는 널값을 가지게 되므로 루프를 종료하게 된다.
@name 배열 값의 개수가 1000개라 하더라도, 위 3줄의 예로써 모든 @name 의 값을 출력할수 있다.
3.2.2.3 if... elsif.... else
if 문은 '만약 조건문이 참이라면' 의 뜻이 된다.
if... elseif.... else 가 모두 쓰인 상태라면, if(만약 이것이 참이라면...) elsif(그렇지 않다면....) else(위의 두 개 모두 아니라면...) 이런 식으로 쓰인다.
$temp = 5;
if ($temp == 5) { print "temp is $temp"; }
elsif ($temp == 3) { print "temp is $temp"; }
else { print "Not found temp"; }
3.2.2.4 조건부 표현 (Conditional Expression)
그럼 여기서 펄에서 쓰이는 관계형 연산자에 대해서 알아보겠다.
같 음 == eq
다 름 != ne
부호를 가지는 다름 <=> cmp
보다 큼 > gt
보다 크거나 같음 >= ge
보다 작음 < lt
보다 작거나 같음 <= le
3.2.3 파일 제어
여기서는 펄에서 파일의 접근에 대한 방법을 알아 보겠다. 펄에서 파일을 열기 위해 쓰는 명령어는 open()이 있다. 물론 사용하신 다음에는 close()로 닫아야한다. 여기선 텍스트 파일에 대한 이야기를 하겠지만, dbm 파일을 이용할수 있는 dbmopen()과 dbmclose()도 있다.
3.2.3.1 open(), close()
open() 함수는 새로운 파일 핸들을 어떤 파일에 연계시키기 위해서 사용한다.
open(FILE, "> /etc/guest.txt");
close(FILE);
open(FILE, ">> /etc/file.log");
close(FILE);
이렇게 사용 될수 있다. 위 예에서 'FILE'은 새로운 파일 핸들이고, '.../filename'은 사용 할려고 하는 파일명이다.
그리고 `` '' 안의 파일명 앞에 여러가지 기호가 쓰이는데, 이것은 파일에 접근 방법을 지정할수 있다.
'<' 읽기를 위한 파일 열기(기본값)
'>' 쓰기를 위한 파일 열기
'>>' 이어쓰기를 위한 파일 열기
'+C<' 읽기와 쓰기를 위한 파일 열기
'+>' 읽기와 쓰기를 위한 파일 열기
'|' (파일 이름 앞) 파일을 명령(펄에서 파일로 전송)처럼 다룬다
'|' (파일 이름 뒤) 파일을 명령(파일에서 펄로 전송)처럼 다룬다
open() 함수로 파일을 열었으면, 반드시 close() 함수로 닫아야 한다. 그리고 open (파일핸들, ``파일제어기호 파일명(경로포함)''); 에서 파일제어기호 와 파일명에는 콤마등의 기호가 없다.
3.3. 정규표현, 패턴매칭
3.3.1 정규 표현(Regular expression)
정규 표현(regular expression)은 일반화된 문자열을 설명하기 위한 규칙의 집합이다.
어떤 문자열이 정규 표현의 규칙을 따르고 있다면, 그 정규 표현은 그 문자열을 매치(match)한다고 한다.
예를 들면, 정규표현 'b.'는 문자열 'observe, body, abc'와는 매치를 이루고, 'b, Bell, Bob' 와는 매치하지 않다. 이 표현 'b.'는 '소문자 b가 문자열 내에 있어야 하며, 다른 문자가 뒤에 한 개 이상 있어야 하기(단,\n는 예외)' 때문이다.
펄에서는 많은 정규 표현들이 있는데, 그것들을 정리해 보았다.
3.3.1.1 정규표현 어써션(Assertions)
기호 매치 영역 매치 매치함 매치안함
---------------------------------------------------------------------------- '^' 문자열의 시작부분 ^fool foolish tomfoolery
'$' 문자열의 끝 부분 fool$ April fool foolish
'\b' 단어 영역 be\bside be side beside
'\B' 비단어 영역 be\Bside beside be side
3.3.1.2 정규표현 아톰 퀀티파이어(Quantifiers)
기호 매치 영역 예 매치함 매치안함
---------------------------------------------------------------------
'*' 없거나 하나 이상 ab*c ac, abc abb
'+' 하나 이상 ab+c abc ac
'?' 없거나 하나 ab?c ac, abc abbc
'{n}' n개 만큼 ab{2}c abbc abbbc
'{n, }' 적어도 n개 만큼 ab{2,}c abbc, abbbc abc
'{nm}' 적어도 n개, 많아야 m개 ab{2,3}c abbc, abbbc abbbbc
3.3.1.4 특수문자(Special Characters)
기호 매치 영역 예 매치함 매치안함
----------------------------------------------------------------------------
'\d' 숫자 b\dd b4d bab
'\D' 비숫자 b\Dd bad b4d
'\n' 새 라인(줄바꿈)
'\r' 캐리지 리턴
'\t' 탭
'\f' 폼 피드
'\s' 공백 문자
'\S' 비공백 문자
'\w' 알파벳이나 숫자 a\wb a2b a^b
'\W' 알파벳이나 숫자를 제외한 문자 a\Wb aa^b aabb
3.3.2 매 칭(Matching)
위의 표현들은 펄 프로그래밍시 매우 많이, 그리고 매우 유용하게 쓰인다. 보통 매치 연산자는 두개의 '/'로 둘러싸인 정규표현으로 구성된다. 그리고 특수연산자 '=~'는 첫번째 값과 두번째 값이 매치하는지에 따라 참과 거짓으로 평가 한다.
예) $filename =~ /dat$/ && die "Can't use. dat files. \n";
이 예의 뜻은 만약 $filename의 끝 문자열이 'dat'로 끝나지 않는다면 'die ~ '해라라는 뜻이 된다.
연산자 '!~'는 '=~'의 반대개념이다.
예) $ENV{'PATH'} !~ /perl/ && warn "Not sure if perl is in your path...";
perl의 경로가 $ENV{'PATH'}와 매치되지 않는다면.... 의 뜻이 된다.
3.3.3 대 치(Substitution)
대치는 쉬우면서 강력한 기능을 제공한다. 대치 연산자는 '/.../.../' 를 사용한다. 첫번째와 두번째 '/'에 대치될 패턴이 오게되고, 두번째와 세번째 '/'에 대치할 패턴이 오게 된다.
예)$name = "Kimyoungsoo";
$name =~ s/Kim/Unicorn/;
$name의 'Kim'을 'Unicorn'으로 대치하게 된다.
4. 문자열 처리에 관한 Perl 사용 예제
본 장에서는 perl을 이용 하여 문자열을 처리하는 방법들에 대해서 자세히 알아 본다.
4.1. 다른 프로그램을 실행시켜 출력내용 읽기
다른 프로그램을 실행시키고 그 출력 내용을 읽는 방법은 여러가지가 있다.
4.1.1. Backtick
역따옴표(`...`)는 그 안에 들어있는 내용을 shell을 통해 실행하고, 표준출력(STDOUT)으로 출력되는 내용을 반환한다. 따라서,
$content = `cat /etc/passwd`;
는 passwd 파일을 간단히 읽어들인다.
물론, 단순한 파일을 읽는데는 직접 open을 해서 읽어들이는 것이 좋다. 역따옴표를 사용하게 되면, 그 내부의 프로그램을 실행하기 위해서 별도의 프로세스가 생성되어야 하기 때문에 그만큼 시스템의 자원을 소모하게된다.
4.1.2. open 함수를 이용하는 방법
open 함수에서 파일이름분에 > 대신 | (파이프)기호를 뒤에 붙이면 해당 파일이름을 실행시키고 그 출력내용을 읽을 수 있다.
#!/usr/bin/perl
# prog.pl
print "$$\n";
warn "Exiting\n";
#!/usr/bin/perl
# opener.pl
$p = open(RUN, "./prog.pl |") || die $!;
@out = <RUN>;
close RUN;
print "opener: $p\n";
print map { "opener: $_" } @out;
opener.pl에서 prog.pl을 "./prog.pl |"와 같이 하여 열었다.(open)
그 후 일반 파일 읽기에서 하는 것처럼 <RUN>를 이용하여 출력내용을 읽는다. 참고로, open 함수가 반환하는 것은, 프로그램이 제대로 실행되었을 때, 새로운 프로세스의 프로세스 아이디이다.
만일 이 프로그램이 오랜시간동안 살아 있는 것(웹서버처럼)이라면 close를 하지 않을 경우 문제가 될 수 있다. 예를 들어, open은 계속해야 하는데, close를 해 주지 않는다면, 해당 프로세스가 죽지않고 계속 남아서 시스템자원을 차지하고 있어서 결국 시스템이 멈추는 상황이 발생할 수도 있게된다.
따라서 반드시 close를 해 주어야 합니다.
만일 표준에러(STDERR)까지 같이 읽어야 한다면
#!/usr/bin/perl
# prog.pl
print "$$\n";
warn "Exiting\n";
#!/usr/bin/perl
# opener.pl
$p = open(RUN, "./prog.pl 2>&1 |") || die $!;
@out = <RUN>;
close RUN; #
print "opener: $p\n";
print map { "opener: $_" } @out;
와 같이 2>&1를 추가합니다.
4.2. 문자열 안에 실행문 넣기
문자열 내에 실행문을 직접 삽입할 수 있다.
보통의 print 문에서는
print "수익대 비용 차액 : " . ($income - $outcome) . "\n";
와 같은 식이다.
만일 print문 안에 이런 계산식이 많이 들어가야 한다면, 조금 귀찮을 수도 있다. 이런 경우 문자열 내에 계산식을 직접 삽입하는 것이 간편하겠다고 생각되면 다음과 같은 형식을 사용한다.
print "수익대 비용 차액 : ${ \($income - $outcome) }\n";
문자열 내에서 ${스칼라변수리퍼런스}는 그 리퍼런스가 가리키는 변수의 값을 나타낸다. 예를 들면 다음과 같은 경우이다.
$var = 365;
$rvar = \$var;
print "var = ${$rvar}\n"; # 이 경우라면 "var = $$rvar\n"도 같은 결과이지만, 설명을 위해 위 ${ \($income - $outcome) }는 ${ }의 안에 \($income - $outcome)을 가지고 있다. 목록(list)을 나타내는 괄호앞에 \(백슬래쉬)를 두면 목록의 각 요소에 대해 리퍼런스를 만드는 것과 같다. 그래서
$what = \( $a, $b, $c )
는
$what = ( \$a, \$b, \$c );
와 같게 되고, 스칼라 문맥에서 목록이 사용되면 맨 뒤의 것만 사용되게 되어 맨 마지막 요소인 $income - $outcome의 결과 값에 대한 리퍼런스가 ${ }와 함께 사용되어 계산값이 출력 되는 것이다. 맨 마지막 값이 사용되는 것을 직접 확인하려면 괄호의 앞부분에 다른 값을 넣고 컴마로 구분해 보면 된다.
print "수익대 비용 비율과 차액 : ${ \($outcome / $income, $income - $outcome) }\n";
이 경우 $outcome / $income 부분은 사라져 알 수 없다.
배열 역시 문자열 안에 넣을 수 있다. 문자열 내에서 배열리퍼런스의 사용은 @{배열의리퍼런스}의 형식이다. 물론 "@$array_ref"와 같은 맥락이다.
print "수익대 비용 비율과 차액 : @{ [ $outcome / $income, $income - $outcome ] }\n";
이 경우에는 \() 대신 []를 사용한다. []는 이름없는 배열(anonymous array)의 레퍼런스를 나타낸다.
4.3. 첫 글자는 대문자로, 나머지는 소문자로 바꾸기
영문 문자열의 첫글자만 대문자로 만들고, 나머지는 소문자로 만드는 법이다. 사용자의 입력을 받았을 때, 다양한 사용자의 입력 습관을 무시하고, 적절히 바꾸는 방법중 하나라고 생각된다.
쌍따옴표를 사용할 때, \n이 개행문자(new line)로, \t가 탭문자로 사용된다는 것을 알 것이다. 이 외에도 다양한 특수문자들이 있다. 그 중에는
\U : 문자열의 끝 또는 \E가 나올 때 까지 모든 문자를 대문자로 바꿈
\L : 문자열의 끝 또는 \E가 나올 때 까지 모든 문자를 소문자로 바꿈
\u : 바로 다음의 문자만을 대문자로 바꿈
\l : 바로 다음의 문자만을 소문자로 바꿈
과 같은 특수문자들이 있다. 이 특수문자들을 이용하면, 원하는 바를 이룰 수 있다.
$str = "what A wAnderful Day!";
$badstr = "\u$str"; # What A wAnderful Day!
$goodstr = "\u\L$str"; # What a wanderful day!
$badstr의 경우는 굳이 설명이 필요없지만, 자칫하면 위와 같이 생각할 수 있다. (사실 프로그래머는 사용자의 입력을 ``예단''하는 수가 상당히 많다)
$goodstr은 일단 \L로 모든 문자들을 소문자로 바꾼후에 \u로 첫 문자만 대문자로 바꿔주었다.
4.4. 문자열의 맨 처음과 맨 뒤의 공백 없애기
파일에서 텍스트를 읽어 들이거나 사용자의 입력을 받을 때 때로 반드시 처리해 주어야 하는 것이 문자열의 맨 앞이나 맨 뒤의 공백문자들을 지우는 것이다. 다음의
$str =~ s/^\s+|\s+$//g;
한 줄이면 맨 앞의 공백과 맨 뒤의 공백을 한 번에 없앨 수 있다. 물론 습관에 따라
$str =~ s/^\s+//;
$str =~ s/\s+$//;
해도 무관하다.
4.5. 한글을 깨끗하게 자르기
어떤 데이터를 받거나 얻은 후, 데이터베이스에 넣거나 웹상에 표출할 때, 그 것이 너무 길어서 데이터베이스에서 받아들이지 않거나, 웹 페이지의 모양이 이상한 경우가 있다.
이 때는 받은 데이터의 일부를 불가피하게 잘라내야 하는 데, 영문이라면 큰 문제가 없지만 한글의 경우에는 문제가 된다.
이유는, 한글 한 글자를 나타내는 데 2바이트를 사용하기 때문이다.
예를 들어 "우리나라 만세"를 10 바이트로 잘라내려면 "우리나라" 8바이트, 공백 1바이트, '만'자의 앞부분 1바이트가 남아서 출력될 때는 깨진 글자가 보여지게 된다.
이 때는 다음과 같은 코드를 사용하시면 된다.
if( length($text) > 60 ) {
$text = substr($text,0,60);
$text =~ s/(([\x80-\xff].)*)[\x80-\xff]?$/$1/;
}
일단 문자열을 원하는 길이만큼만 남기고 잘라낸다(substr).
남은 문자열의 끝부분에 대해서 정규식을 적용하는데, 한글의 첫 바이트는 항상 \x7f 보다 크므로 [\x80-\xff]., 즉 한글의 첫바이트일 수 있는 [\x80-\xff] 한 바이트와 '.' 한 바이트가 합쳐진 것을 '*' 하면 한글 0 또는 그 이상의 글자가 된다. 그리고 한글의 첫바이트인 [\x80-\xff] 하나만 남는다면 온전한 한글 문자열부분인 (([\x80-\xff].)*) 부분이 $1 이라는 변수에 남아있으므로 그것을 일치된 전체 텍스트에 대해서 치환한다.
4.6. 날짜 패턴검사와 후참조(Backreference)
날짜 입력값의 패턴 검사를 해 보겠다.
날짜 입력값이 yyyy-mm-dd나 yyyy/mm/dd는 인정하고 그 외의 패턴은 인정하지 않는다고 한다면 다음과 같이 간단한 정규식을 사용하면 된다.
if( $dateinput =~ m|^\d{4}([-/])\d{1,2}\1\d{1,2}$| ) {
print "Good\n";
} else {
print "Not good\n";
}
여기서 상세한 날짜 범위에 대한 검증은 생략하겠다.
얼핏생각하면 패턴 검사를
$dateinput =~ m|^\d{4}([-/])\d{1,2}[-/]\d{1,2}$|
와 같이 하면 될 것이라고 생각할 수 있다. 하지만, 이 경우는 yyyy-mm/dd나 yyyy/mm-dd도 옳은 것으로 인정해 주기 때문에, 썩 좋은 해법이라고 보기 어렵다. 그래서
$dateinput =~ m|^\d{4}-\d{1,2}-\d{1,2}$| or $dateinput =~ m|^\d{4}/\d{1,2}/\d{1,2}$|
라고 할 수도 있으나, 그 보다는 맨 위의
$dateinput =~ m|^\d{4}([-/])\d{1,2}\1\d{1,2}$|
가 훨씬 간결하다. \1은 후참조(backreference)라고 한다. 정규식 패턴 내에서 앞의 괄호가 일치할 때, 패턴 내부에서 다시 참조할 수 있도록 별도로 저장해둔 내용을 참조하는 것이다.
이 것은 패턴 외부에서 $1, $2, $3...를 사용하는 것과는 달리, 패턴 내부에서 사용한다. 그래서 년과 월의 사이에 사용되는 구분자가 똑같이 월과 일의 사이에서 사용된 경우에만 올바른 패턴으로 결정할 수 있게 된다.
후참조 역시 $1, $2, $3...와 같이 \1, \2, \3...로 사용된다. 만일 날짜 + 시간 패턴을 검사한다면, 다음과 같이 사용할 수 있다.
m|^\d{4}([-/])\d{1,2}\1\d{1,2} \d\d([-:])\d\d\2\d\d$|
5. 결 론
Perl 을 사용 하는 많은 사람들의 인식 속에는 perl 은 그저 인터넷 분야에서 널리 활용되고 있다. 그래서 “오직 CGI 만을 위한 언어다”라고 생각 할지도 모른다. 펄을 창시한 래리 월이 TMTOWTDI, 즉 “There is more than one way to do it" 이라는 슬로건을 내 걸었듯이, 인터넷 프로그래밍은 물론이고 유닉스 시스템 관리, 네트워크, 기타 컴퓨터 분야의 전반에 걸쳐 활용될 수 있는 Perl 의 풍부한 활용성을 알리고자 이렇게 이글을 쓰게 되었다. 특히 문자열 처리에 관한한 최고임을 자부하는 perl 을 사용자 입맛에 맞게 사용 하였으면 한다.
참고문헌
[1] 톰 크리스잔센, 네이던 토킹턴 저, 안계용 역, “Perl Cookbook" 2000.
[2] "http://www.perlmania.or.kr" 문서 모음 게시판
[3] “http://www.perl.or.kr” About perl, Tips 게시판
[4] “http://www.softbrain.co.kr/perl/” Perl 이야기 게시판
[5] “http://www.activestate.com/” 윈도우용 perl 자료
[6] “http://perl.sshel.com/” Docs 게시판
[7] “http://seoul.pm.org/”