GREP 명령어 만들기 및 정규 표현식
libc
에서 제공하는 정규 표현식 API는 다음과 같다.
#include <sys/types.h>
#include <regex.h>
int recomp(regex_t *reg, const char *pattern, int flags);
void regfree(regex_t *reg);
int regexec(const regex_t *reg, const char *string, size_t nmatch, regmatch_t pmatch[], int flags);
size_t regerror(int errcode, const regex_t *reg, char *msgbuf, size_t msgbuf_size);
-
regcomp()
는 두번째 인자로 넘어온 정규 표현식 문자열을 전용 데이터 형식regex_t
로 변환한다. 변환한 결과는 첫번째 인자reg
에 기록된다. -
첫번째 인자
reg
의 메모리 영역은 호출하기 전에 할당하여 그 포인터를 전달해야 하는데, 그 외에도regcomp()
가 독자적으로 메모리를 확보하게 된다. 그것을 해제하는API
가 바로regfree()
이다. -
regcomp()
와regfree()
는open()
,close()
처럼 늘 쌍으로 사용되는 함수이다. -
regcomp()
는 성공하면 0을 반환하고 실패하면, 에러 코드를 반환하는데, 이 에러 코드를 에러 메시지로 변환하는 함수가regerror()
이다.
grep 소스 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <regex.h>
static void do_grep(regex_t *pat, FILE *f);
int main(int argc, char *argv[]) {
regex_t pat;
int err;
int i;
if (argc < 2) {
fputs("no pattern\n", stderr);
exit(1);
}
err = regcomp(&pat, argv[1], REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
if (err != 0) {
char buf[1024];
puts(buf);
exit(1);
}
if (argc == 2) {
do_grep(&pat, stdin);
} else {
for (i = 2; i < argc; i++) {
FILE *f;
f = fopen(argv[i], "r");
if (!f) {
perror(argv[i]);
exit(1);
}
do_grep(&pat, f);
fclose(f);
}
}
regfree(&pat);
exit(0);
}
static void do_grep(regex_t *pat, FILE *src) {
char buf[4096];
while (fgets(buf, sizeof buf, src)) {
if (regexec(pat, buf, 0, NULL, 0) == 0) {
fputs(buf, stdout);
}
}
}
한글 문자열 처리와 국제화
-
위의
grep
명령어를 한글로 된 텍스트 파일에 적용할 수가 없다. 이유는 한글 문자열을 고려하지 않았기 때문이다. -
C언어에서 문자는 사실 숫자이며, 문자열은 숫자의 배열이다.
유니코드
-
유니코드는 기존의 수많은 언어의 문자 코드를 대부분 포함해 문자 코드가 난립하는 상황을 해결하기 위해서 고안되었다. 그러나, 현실은 유니코드에 포함되지 않은 문자도 존재하며 특히 한자와 관련된 골치아픈 문제들이 남아있다.
-
그래도 기존의 문자 코드를 여러개 사용하는 것보다는 훨씬 낫고, 달리 대체할 것이 없어 현재는 유니코드가 사실상 표준이 되었다.
문자 코드의 구체적인 의미
- ‘문자 코드’ 라는 모호한 개념은 구체적으로 다음과 같이 두 가지로 분해할 수 있다.
- 부호화 문자집합
- 인코딩
부호화 문자 집합
- 문자집합 또는 문자 셋은 글자들의 집합이며, 집합 안의 문자들에 음수가 아닌 정수들을 배정한 것을 부호화된 문자 집합(coded character set) 이라고 한다.
인코딩
-
부호화 문자 집합에 속하는 각 문자는 하나의 숫자와 대응된다. 그 번호를 실제 바이트 열로 적용할 때의 계산식이 인코딩이다.
-
인코딩은 크게 두 가지 종류가 있는데 아래와 같다.
- 모든 문자에 대해서 같은 바이트 수를 사용하는 인코딩
- 문자의 종류에 따라 사용하는 바이트 수를 바꾸는 인코딩
- 전자를 와이드 문자, 후자를 멀티 바이트 문자라고 한다. 둘 다 실제로 사용되고 있는데, 문자열의 저장 및 전송은 멀티 파이트 문자를 사용하고, 프로세스 내에서 데이터를 처리할 때는 와이드 문자도 자주 사용된다. 와이드 문자가 처리하기 쉽기 때문이다.
문자열 처리 방법
-
앞으로 유니코드가 우세할 것은 틀림이 없다. 그리고 특히 데이터 저장 및 전송에 사용되는 인코딩은
UTF-8
로 결정되었다. -
따라서 지금까지 작성된 유니코등 이외의 데이터를 어떻게 처리할 것인가 하는 문제가 있는데 아래 방법중에 하나를 선택해야한다.
- 프로그램에서 사용할 문자 코드를 미리 정한다 (혹은 사용자가 정하도록 한다)
- 문자 코드를 추출한다.
- 문자열을 주고 받을 때, 문자 코드의 이름도 넘기도록 한다.
-
첫 번째 방법은 정해진 문자 코드를 사용하기로 사용자와 합의하는 것이다.
-
두 번째 방법은 유니코드가 등장하기 전까지는 효과적일지 몰라도, 유니코드가 포함되면서 실패할 확률이 늘었다.
-
세번재 방법은 문자 코드의 이름을 명시적으로 전달하는 것도 좋은 방법이지만, 이름을 잘못 전달하거나 모르는 경우가 있다.
결로넉으로 완벽한 대처는 존재하기 힘들고, 가장 합리적인 방법은 사용할 문자 코드를 미리 정하고, 그 이외의 문자 코드는 이름을 넘겨줘서 처리할 수 있도록 대응하는 방법일 것이다.
국제화와 다중언어화
-
지금까지 설명한 것은 하나의 프로그램에서 여러 언어를 처리할 수 있도록 하기 위한 개념들이었고, 이를 다중 언어화라고 한다.
-
이와 비슷한 개념으로 프로그램으 여러 요소를 지역의 관습에 맞추는 작업도 한다. 이것을 지역화(
localiszation
)라고 한다. 예를 들어서, 메시지를 사용자가 원하는 언어로 표시하거나 날짜와 시간을 지역의 관습에 맞춰서 표시하는 경우이다. -
또한 실행시에 해당 지역을 전환할 수 있게 하는 것을 국제화(
internationalization
)라고 한다. -
C 언어에선 국제화의 기본 구조는
로케일(locale)
이다. 국가, 언어, 문자의 조합이 로케일이며, 예를 들면 한글UTF-8
의 경우ko_KR.UTF-8
로케일을 사용한다.ko
가 한글,KR
이 한국이라는 지역,UTF-8
이 유니코드를 의미한다.
다중언어 처리와 국제화를 위한 라이브러리
국제화와 다중 언어 문자열 처리에 사용할 수 있는 라이브러리 몇 가지를 소개한다.
libc
로케일 매커니즘
setlocale()
이 로케일의 기본 API이다.
libc
와이드 문자 관련 루틴(wchar)
libc
에서는 ANSIC
로 규정되어 있는, 와이드 문자용API
가 있다.
iconv
iconv
문자 코드 간 상호 변환을 위해 사용하는 라이브러리이다. 예를 들면,EUC-KR
과UTF-8
등의 변화를 수행할 수 있다.
PCRE(Perl Compatible Regular Expression)
PCRE(Perl Compatible Regular Expression)
는UTF-8
에 대응하는 정규 표현 라이브러리이다. 이장에서 소개한libc
정규 표현에 더하여Perl5
확장 정규 표현에도 대응한다.