ostep-reverse : reverse 명령어 구현

2022. 4. 28. 22:54컴퓨터 공학/운영체제

반응형

본 티스토리 블로그는 PC 환경에 최적화되어 있습니다.

모바일 유저분들은 아래 네이버 블로그를 이용해 주세요.

 

 

ostep-reverse : reverse 명령어 구현

본 네이버 블로그는 모바일 환경에 최적화되어 있습니다. PC 유저분들은 아래 티스토리 블로그를 이용해 ...

blog.naver.com

 

 

운영체제 기능 중 하나인 reverse 명령어를 구현해 보도록 하겠습니다.

명령어의 기능은 입력된 파일을 열어서 역순으로 출력해 주는 것입니다.

단, 개행문자 기준으로 역순 출력해야 하며, 모든 내응을 반전시켜서는 안됩니다.

 

input file
output file
asdf0
asdf1
asdf2
asdf3
asdf3
asdf2
asdf1
asdf0

이 기능을 구현하는 방식은 제가 생각하기론 스택, 파일 포인터, 전체 동적할당을 진행하는 방법이 존재하는 것 같습니다.

대부분 원리는 같으나 부연 설명을 진행하자면 아래와 같습니다.

 

 

1) 스택

파일을 읽다가 '\n'을 확인하면 해당 위치를 스택에 집어넣습니다.

이 과정을 반복하다가 EOF를 만나면 스택에서 하나씩 주소 정보를 pop 하고, 문자열을 출력해 주면 역순으로 한 줄씩 출력이 가능합니다.

다만, 문자열 출력과 포인터를 관리하기 위한 별도의 코드를 작성해야 하며, C언어는 직접 자료구조를 만들어야 하기 때문에 조금 귀찮을 수 있습니다.

 

2) 파일 포인터 이용

파일을 역순으로 읽어 드리고 '\n'을 확인하면 getline 함수를 호출하여 한 줄 출력하고, 파일 포인터를 개행문자 이전으로 옮기는 방식입니다.

포인터와 인덱스 관리도 쉽고, 메모리 사용량도 상당히 적지만 파일 포인터를 움직여야 하는 경우가 많기 때문에 경우에 따라서는 상당히 느려질 수 있습니다.

 

아래는 해당 아이디어 기반으로 작성된 코드입니다.

물론, ostep 테스트를 통과했다는 것이지 일반 파일에도 정상 동작하지 않을 수 있습니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void reversContents(FILE* input, FILE* output);

int main(int argc, char* argv[]){
	FILE *fp, *fp2;
    char c,d;
	
    if (argc > 3){ //인자 개수가 3을 초과한 경우
        fprintf(stderr,"usage: reverse <input> <output>\n");
        exit(1);
    }
	
    else if(argc == 1){ 
        fp = stdin;
        fp2 = stdout;
    }
	
    else{

        fp = fopen(argv[1],"r");  
        if(fp == NULL){       
            fprintf(stderr, "reverse: cannot open file '%s'\n",argv[1]);
            exit(1);
        }

        fp2 = fopen(argv[2],"w");
        if(fp2 == NULL){           
            fprintf(stderr, "reverse: cannot open file '%s'\n",argv[2]);
            exit(1);
        }
	    

        while(1){
            if (feof(fp) == 0 && feof(fp2) == 0){
                c = fgetc(fp);
                d = fgetc(fp2);

                if (c != d) // 읽어드린 정보가 하나라도 다르면 다른 파일이다.
                    break;
            }
            else{
                fprintf(stderr, "reverse: input and output file must differ\n");
                exit(1);
            }  
        }
    }
    reversContents(fp, fp2);
    return 0;
}

void reversContents(FILE* input, FILE* output){
    int head, tail;
    char *buffer;
    size_t size=0;
    char c;
	
    FILE *fp = input;
    FILE *fp2 = output;

	fseek(fp,0,SEEK_END);

    while(ftell(fp)>0){
        head = ftell(fp);
        c =fgetc(fp);
        

        if(c == '\n'){
            getline(&buffer, &size, fp);
            fprintf(fp2, "%s", buffer);
            tail = ftell(fp);

            fseek(fp, (tail - head)*(-1), SEEK_CUR);
        }
        fseek(fp, -2, SEEK_CUR);
    }

    fseek(fp, 0, SEEK_SET);
    getline(&buffer, &size, fp);
    fprintf(fp2, "%s", buffer);
    free(buffer);
}
 

 

3) 파일 전체를 동적할당하기

가장 쉬운 방법이며, 파일 전체를 힙 영역에 받아와서 읽고 strtok를 통해서 '\n'을 분리, 역순으로 배열을 순회하면 풀립니다.

이 방식은 경우에 따라서 작동이 불가능할 수 있습니다.

예를 들어 파일 크기가 20GB 정도 되는 걸 힙 영역에 넣는 건 무리겠죠?

 

아래는 해당 아이디어 기반으로 작성된 코드입니다.

실제 파일 크기가 커지거나 할 경우 코드가 정상 동작하지 않을 가능성이 매우 높으므로 주의 바랍니다!!!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char* argv[]){
	FILE *FD, *FD2;
	int sizeFile;
    char* tmp;
    char checker_FD,checker_FD2;
	
    if (argc > 3){ //인자 개수가 3을 초과한 경우
        fprintf(stderr,"usage: reverse <input> <output>\n");
        exit(1);
    }
	
    if(argc == 1 ){ 
        FD = stdin;
        FD2 = stdout;
    }
	
    else{

        FD = fopen(argv[1],"r");  
        if(FD == NULL){       
            fprintf(stderr, "reverse: cannot open file '%s'\n",argv[1]);
            exit(1);
        }

        FD2 = fopen(argv[2],"w");
        if(FD2 == NULL){           
            fprintf(stderr, "reverse: cannot open file '%s'\n",argv[2]);
            exit(1);
        }
	    
	if(!strcmp(argv[1], argv[2])){ // 입력된 경로가 일치하면, 동일 파일로 간주함. 
            fprintf(stderr,"reverse: input and output file must differ\n");
            exit(1);
        }

        while(1){
            if (feof(FD) == 0 && feof(FD2) == 0){
                checker_FD = fgetc(FD);
                checker_FD2 = fgetc(FD2);

                if (checker_FD != checker_FD2) // 읽어드린 정보가 하나라도 다르면 다른 파일이다.
                    break;
            }
            else{
                fprintf(stderr, "reverse: input and output file must differ\n");
                exit(1);
            }  
        }
        
    }

    fseek(FD,0,SEEK_END);     // 파일의 맨 끝으로 이동
    sizeFile = ftell(FD);     // 파일 포인터의 현재 위치를 얻는다.
    char buffer[sizeFile+1];  // 확인된 파일 사이즈+1 만큼 버퍼를 할당. c언어 표준이 일부 바뀌면서 사이즈 고정이 아니여도 사용가능.
    char *result[sizeFile+1];  // 반환 역순 데이터를 저장.
	
    fseek(FD, 0, SEEK_SET);
    fread(buffer, sizeFile , 1 , FD); // 실제 파일 데이터를 읽음
    tmp = strtok(buffer, "\n");
	
    int i;
	for(i=0;; i++){
		if(tmp == NULL)				  // 자를 문자열이 없을때까지 반복함
			break;
		result[i] = tmp;
        tmp = strtok(NULL, "\n");     // 다음 문자열을 잘라서 포인터를 반환
	}

           
    for(int j=i-1; j>-1; j--){
        if(result[j] != NULL)
            fprintf(FD2,"%s\n",result[j]);
    }
            
    fclose(FD);
    fclose(FD2);
    return 0;
}
 

 

아래는 테스트 성공 화면입니다.

 
반응형