post list

2013년 12월 19일

[Data Structure] #1-7 연결리스트 응용 (라인 에디터)


연결리스트 응용 라인 에디터


이 블로그의 코드들은 모두 'C언어로 쉽게 풀어쓴 자료구조'라는 책에서 가져왔다. 혼자 공부하기 위해서 써 놓는 혼자말과 코드들이니 자세한 것을 알고 싶다면 책을 사시길 바란다... 

연결리스트를 응용한 라인 에디터다. 핵심적인 메서드의 경우에는 이미 앞서 모두 구현된 것을 가져왔다.

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


#define MAX_CHAR_PER_LINE 1000
#define MAX_NAME 256
void warning(char*);
void error(char*);

#define FALSE 0
#define TRUE 1

typedef struct {
    char a[MAX_CHAR_PER_LINE];
}element;

typedef struct {
    element data;
    struct ListNode* link;
}ListNode;

typedef struct {
    ListNode* head;
    int length;
}LinkedListType;

void insert_node(ListNode **phead, ListNode *p, ListNode *new_node){
    if (*phead==NULL) {
        *phead = new_node;
        (*phead)->link = NULL;
    } else if (p == NULL) {
        new_node->link = (*phead)->link;
        (*phead)->link = new_node;
    } else {
        new_node->link = p->link;
        p->link = new_node;
    }
}

void remove_node(ListNode **phead, ListNode *p, ListNode *removed){
    if (*phead == NULL)
        return;
    else if (p==NULL){
        (*phead) = (ListNode*) (*phead)->link;
    }else{
        p->link = removed->link;
    }
    free(removed);
}

void init(LinkedListType *list){
    if (list == NULL)
        return;

    list->length = 0;
    list->head = NULL;
}

ListNode* get_node_at(LinkedListType *list, int pos){
    if (pos < 0 || pos > list->length)
        return NULL;

    int i;
    ListNode *p = list->head;
    for (i=0; i<pos; i++)
        p = p->link;
    
    return p;
}

int get_length(LinkedListType *list){
    return list->length;
}

void add(LinkedListType *list, int pos, element data){
    if (pos < 0 || pos > list->length)
        return;
    
    ListNode *p = get_node_at(list, pos);
        
    ListNode *new_node = (ListNode*) malloc(sizeof(ListNode));
    new_node->data = data;
    new_node->link = NULL;
    
    insert_node(&(list->head), p, new_node);
    list->length++;
}

void add_last(LinkedListType *list, element data){
    add(list, get_length(list), data);
}

void add_first(LinkedListType *list, element data){
    add(list, 0, data);
}

int is_empty(LinkedListType *list){
    return list->length == 0;
}

void delete(LinkedListType *list, int pos){
    //LinkedListType은 여기서 검사한다
    if (is_empty(list) || pos > list->length)
        return;
    
    //ListNode는 다음 메서드에서 검사한다
    ListNode *p = get_node_at(list, pos-1);
    remove_node(&(list->head), p, p != NULL ? (ListNode*) p->link : NULL);
    list->length--;
}

element get_entry(LinkedListType *list, int pos){
//    if (is_empty(list) || pos > list->length)
//        return NULL;
    ListNode *p = get_node_at(list, pos);
    return p->data;
}

void clear(LinkedListType *list){
    int i;
    for (i=0; i<list->length; i++)  delete(list, i);
}

void display(LinkedListType *list){
    int i;
    ListNode *p = list->head;
    
    printf("**********\n");
    for (i=0; i<list->length; i++) {
        printf("%s",p->data.a);
        p = (ListNode*) p->link;
    }
    printf("**********\n");
}

void warning(char *message){
    fprintf(stderr, message);
}

void error(char *message){
    fprintf(stderr, message);
    exit(1);
}

void help(){
    printf("**********\n");
    printf("i: 입력\n");
    printf("d: 삭제\n");
    printf("r: 파일읽기\n");
    printf("w: 파일쓰기\n");
    printf("q: 종료\n");
    printf("**********\n");
}

void read_file(LinkedListType *buffer){
    char fname[MAX_NAME];
    FILE *fd;
    element p;
    
    if (!is_empty(buffer)) {
        clear(buffer);
    }
    init(buffer);
    
    printf("파일 이름 : ");
    scanf("%s",fname);
    if ((fd=fopen(fname, "r"))==NULL) {
        warning("error");
        return;
    }
    while (fgets(p.a, MAX_CHAR_PER_LINE, fd)) {
        add_last(buffer, p);
    }
    
    fclose(fd);
    display(buffer);
}

void write_file(LinkedListType *buffer){
    char fname[MAX_NAME];
    FILE *fd;
    element p;
    int i;
    
    printf("파일 이름 : ");
    scanf("%s",fname);
    if ((fd=fopen(fname, "w"))==NULL) {
        warning("error");
        return;
    }
    for (i=0; i<get_length(buffer); i++) {
        p = get_entry(buffer, i);
        fputs(p.a, fd);
    }
    fclose(fd);
    display(buffer);
}

void delete_line(LinkedListType *buffer){
    int position;
    
    if (is_empty(buffer)) {
        printf("못지움\n");
    }else{
        printf("지우고 싶은 라인번호를 입력 : \n");
        scanf("%d",&position);
        delete(buffer, position);
    }
    display(buffer);
}

void insert_line(LinkedListType *buffer){
    int position;
    char line[MAX_CHAR_PER_LINE];
    element p;
    
    printf("입력행 번호 입력 : \n");
    scanf("%d\n",&position);
    
    printf("내용을 입력 : ");
    fflush(stdin); // 남아있는 standard input을 비워줌
    fgets(line, MAX_CHAR_PER_LINE, stdin);
    strcpy(p.a, line); // line에 있는 것을 p.a로 copy
    add(buffer, position, p);
    display(buffer);
}

void do_command(LinkedListType *buffer, char command){
    switch (command) {
        case 'd':
            delete_line(buffer);
            break;
        case 'i':
            insert_line(buffer);
            break;
        case 'r':
            read_file(buffer);
            break;
        case 'w':
            write_file(buffer);
            break;
        case 'q':
            exit(1);
            break;
    }
}

int main(){
    char command;
    LinkedListType buffer;
    
    init(&buffer);
    do{
        help();
        command = getchar();
        do_command(&buffer, command);
        fflush(stdin);
    }while (command != 'q');
    
    return 0;
}


코드를 실행시키면 잘 돌아갈 것... 이라고 생각했으나. stdin 이 mac 환경에서 먹히질 않는다. 예를 들어 다음의 코드를 보자..
fgets(line, MAX_CHAR_PER_LINE, stdin);
..
이 때  fgets 는 stdin을 \n으로 먹어버린다. 왜냐하면 stdin에 무언가 남아있기 때문이다. 무엇이 남아있을까. 그것은 바로 \n이다. fflush(stdin)이 \n까지 비우질 못하고 있다. 이것은 다음의 코드로 확인할 수있다.

if ((c = getchar()) == '\n')
        printf("bingo!\n");

위의 코드를 fflush(stdin) 아래에 두고 돌려보면 걸리는 것(bingo!가 출력)을 알 수 있다. 즉, stdin이 flush가 되질 않고 \n이 남아있어서 input값이 존재하는 것으로 이해되어 코드가 실행된다. 참고로 맥환경에서만 그런 듯하니 윈도우계열이신분들은 그냥 넘어가시면 됩니다.

위의 코드를 집어넣으면 fflush가 되질 않던 \n이 getchar()로 먹어들어가서 잘 돌아간다. 한방에 해결하는 방법은 없는건지 ..

댓글 없음:

댓글 쓰기