본문 바로가기
컴퓨터 기초/운영체제 실습

13.grep 명령어 실습

by 인생여희 2020. 7. 4.

#.데이터 검색과 가공, 처리 

 

1.regcomp : regcomp을 통하여 정규식(특정한 규칙을 갖는 문자열)을 정의. regcomp() 함수가 성공하면, 0을 리턴합니다.

그렇지 않으면 regerror() 함수에 대한 호출에서 사용할 수 있는 오류 코드를 리턴하며 preg의 컨텐츠가 정의되지 않습니다.

 

2.regexec : regexec를 통하여 입력된 값이 정규식에 포함되었는지를 확인하고 매칭 정보를 리턴. regexec() 함수는 컴파일된 정규식 preg에 대해 널로 끝나는 string을 비교하여 두 사이의 일치 항목을 찾습니다. 일치 항목을 찾으면 regexec() 함수는 0을 리턴합니다. 일치 항목을 찾지 못하면 regexec() 함수는 REG_NOMATCH를 리턴합니다. 그렇지 않으면 오류를 나타내는 0(영)이 아닌 값을 리턴합니다. 0(영)이 아닌 리턴값은 regerror() 함수에 대한 호출에서 사용될 수 있습니다.

 

3.regerror : regerror는 regcomp와 regexec가 실패할 경우 실패이유를 string으로 변환.

 

4.regfree : regfree는 사용을 다한 정규식 리소스를 반환.

 

 

함수설명

 #.int regcomp(regex_t *preg, const char *regex, int cflags)

 

설명: regexec() 함수에서 사용가능한 형식으로 패턴을 컴파일 해준다.

preg 포인터의 경우 컴파일된 regular 형식의 저장소를 가지고 있다. 간단하게 말하면 저 포인터에 정규식 매칭하는 값들이 저장되어 있다. 즉, regcomp 정규식 컴파일 하면, 입력 값으로 넣었던 preg 포인터에 정규식이 저장되는 것이다.

 

 #.int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)

 설명: 컴파일된 정규식 형식을 가지고 주어진 문자열에서 정규식에 해당하는 값을 찾는다.

 

 #.관련 flag 값 

        REG_EXTENDED : 확장 정규식을 지원합니다.

           

        REG_ICASE : 일치 항목에서 대소문자를 무시합니다.

 

        REG_NOSUB : pattern에 지정된 하위 식의 수를 무시합니다. 컴파일된 패턴과 스트링을 비교하면 (regexec() 사용), 스트링은 전체 패턴과 일치해야 합니다. regexec() 함수는 일치 항목을 찾은 경우에만 표시하는 값을 리턴합니다. 일치 항목이 시작되는 위치 또는 일치하는 스트링이 있는 위치를 표시하지 않습니다.

 

        REG_NEWLINE : 줄 바꾸기 문자를 특수 줄의 끝 문자로 처리합니다. 그런 다음 ] 및 $ 패턴으로 일치되는 행 경계를 설정하며 \n을 사용하여 명백하게 스트링 내에서만 일치될 수 있습니다. (이 플래그를 생략하면 줄 바꾸기 문자는 다른 문자처럼 처리됩니다.)

 

 

예제

//데이터 검색과 가공, 처리
#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);
}
//패턴을 컴파일한다.
//두번째로 인자로 넘어온 정규 표현식 문자열(패턴)을 전용 데이터 형식 regex_t로 변환하고
//변환결과는 첫번째 인자에 기록된다.
//성공시 0리턴
err = regcomp(&pat , argv[1] , REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
//오류발생시
if (err != 0) {
char buf[1024];
//에러 출력
regerror(err, &pat, buf, sizeof buf);
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");
//파일 포인터가 null 이면 오류 발생
if (!f) {
perror(argv[i]);
exit(1);
}
//정규표현식 매칭 처리 후, 파일 닫기
do_grep(&pat,f);
fclose(f);
}
}
//리소스 해제
regfree(&pat);
exit(0);
}
//이 함수가 호출되는 시점에는 정규 표현 패턴 문자열이 regex_t로 변환되어 있다.
//이후는 스트림 src로 부터 한줄씩 읽으면서 패턴에 적합한 경우에만 출력해주면 된다.
static void do_grep(regex_t *pat , FILE *src){
char buf[10];
//버퍼에서 한줄을 읽어 들인다.
while (fgets(buf, sizeof buf, src)) {
//패턴이 일치할 경우 처리할 명령
//인자로 받은 regex_t를 사용하여 실제 문자열과 패턴을 조합하는 함수가 바로 regexec()다.
//인자로 넘긴 문자열이 패턴 pat에 적합할경우 0을 반환한다.
//첫번째 인자를 정규표현식으로 간주한다.
//읽어들인 내용이 정규표현식에 적합한지 확인하는 작업!
if (regexec(pat,buf, 0, NULL, 0) == 0) {
//버퍼의 내용을 출력
fputs(buf, stdout);
}
}
}
view raw grep.c hosted with ❤ by GitHub

결과

 

 

예제파일

test3.zip
0.00MB

 

 

#.homework

1.grep 명령어에 있는 -I 옵션과 -v 옵션을 이 장에서 작성한 grep 명령어에 추가한다.

 

2.정규표현식에 해당하는 줄 전체를 출력하는 것이 아니라, 해당 문자열만을 출력하는 명령어 slice를 작성하라.

이를 위해서는 regexec()의 세번째 인자와 네번째 인자를 사용해야한다.

 

 

 

 

#.grep 명령어에 있는 -I 옵션과 -v 옵션을 이 장에서 작성한 grep 명령어에 추가한다.

풀이

//데이터 검색과 가공, 처리
//grep 명령어에 있는 -i 옵션과 -v 옵션을 이 장에서 작성한 grep 명령어에 추가한다.
// -i : 대/소문자 무시.
// -v : 매칭되는 PATTERN이 존재하지 않는 라인 선택.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <regex.h>
//인자 처리 라이브러리
#define _GUN_SOURCE
#include <getopt.h>
extern char *optarg; //현재 처리 중인 옵션의 파라미터
//현재 처리중인 옵션의 argv 인덱스,에러, 현재 처리 중인 옵션 문자
extern int optind, opterr, optopt;
//커멘드 라인 인자 옵션 구조체
static struct option longopts[] = {
{"ignore" , no_argument, NULL, 'i'}, //대소문자 무시
{"v" , no_argument, NULL, 'v'}, //매칭되는 PATTERN이 존재하지 않는 라인 선택.
{0, 0, 0, 0}
};
int main(int argc, char *argv[]){
FILE *f;
regex_t pat;
int err;
int i;
int opt;
int notInText = 0;
//인자 개수 및 내용 출력
printf("argc : %d \n" , argc);
for (int i =0 ; i < argc; i++) {
printf("argv [%d] : %s \n" , i , argv[i]);
};
//인자의 개수가 맞지 않을때
if (argc != 4) {
fprintf(stdout, "usage:인자의 개수가 잘못되었습니다. \n %s -i 'ab' test.txt 형식입니다. \n" , argv[0]);
exit(1);
}
//커멘드라인 인자 처리
while ((opt = getopt_long(argc, argv, "iv" , longopts, NULL)) != -1) {
switch (opt) {
//대소문자 무시 | REG_ICASE
case 'i':
printf("i\n");
err = regcomp(&pat, argv[2] ,REG_EXTENDED | REG_NOSUB | REG_NEWLINE | REG_ICASE);
break;
//매칭되는 PATTERN이 존재하지 않는 라인 선택.
case 'v':
printf("v\n");
notInText = 1;
//패턴 컴파일
//두번째로 인자로 넘어온 정규 표현식 문자열을 전용 데이터 형식 regex_t로 변환
//변환된 결과는 첫번째 인자에 기록
//성공시 0 리턴
err = regcomp(&pat, argv[2] ,REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
break;
case '?':
fprintf(stdout, "usage: \n %s -i txt test.txt 또는 -v txt test.txt 형식입니다. \n" , argv[0]);
exit(1);
break;
default:
//패턴 컴파일
//두번째로 인자로 넘어온 정규 표현식 문자열을 전용 데이터 형식 regex_t로 변환
//변환된 결과는 첫번째 인자에 기록
//성공시 0 리턴
err = regcomp(&pat, argv[2] ,REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
break;
}
}
//err : 0 이면 성공
printf("err : %d\n" , err);
//0이 아닐때 에러처리
if (err != 0) {
char buf[1024];
//에러 출력
regerror(err, &pat, buf, sizeof buf);
puts(buf);
exit(1);
}
//읽기전용으로 파일 열기
f = fopen(argv[3] , "r");
//파일열기 실패
if (!f) {
perror(argv[3]);
exit(1);
}
char buf[10];
//한줄씩 읽기,
while (fgets(buf, sizeof buf, f)) {
//정규식에 포함되지 않는 행 출력
if (notInText == 1) {
//패턴이 다를경우 명령어 처리
if (regexec(&pat, buf, 0, NULL , 0) != 0) {
//표준출력
fputs(buf, stdout);
}
}else{
//패턴이 같을 경우 명령어 처리
if (regexec(&pat, buf, 0, NULL , 0) == 0) {
//표준출력
fputs(buf, stdout);
}
}
}
printf("\n");
//파일닫기, 정규표현식 컴파일 포인터 닫기
fclose(f);
regfree(&pat);
notInText = 0;
exit(0);
}
view raw grep.c hosted with ❤ by GitHub

결과