프로그래밍 노트

[c]리스트 구조 파일에 쓰고 읽기 본문

C++ TIP

[c]리스트 구조 파일에 쓰고 읽기

띠리 2008. 6. 2. 10:07

리스트 구조를 파일에 쓰고 읽는 예제이다.
나는 머리가 안좋아서 그런지 리스트 구조가 깨끗하게 이해가 안된다. -.-;;

// 리스트 구조 파일
#define  DEBUG

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

// 키
// --------------------------------------------------
typedef  int  key;

// 키 입력
int input_key(char *szDisp, key *pKey) {
    printf("%s", szDisp);
    if(scanf("%d", pKey) != 1)
        return 0;               // 입력 에러
    return (*pKey > 0);         // 입력 코드 양수 체크
}
                               
// 키 비교
int compare_key(key KeyA, key KeyB) {
    return (KeyA - KeyB);
}

// 키 표시
void print_key(key KeyDisp) {
    printf("%d", KeyDisp);
}

// 데이터
// --------------------------------------------------
typedef struct data {
    key  code;
    char name[20];
    long  Score[2];
} data;

// 데이터 입력
int input_data(char *szDisp, data *pData) {
    printf("%s", szDisp);
    if(input_key("코드 번호 : ", &pData->code) == 0)
        return 0;               // 부정한 코드 입력
    printf("이름 : ");
 if(scanf("%s", pData->name) != 1)
  return 0;
 printf("국어 점수 : ");  // 입력 에러
    if(scanf("%d", &pData->Score[0]) != 1)
        return 0;               // 입력 에러
 printf("산수 점수 : ");
 return (scanf("%d", &pData->Score[1]) == 1);
}

// 데이터 비교
int compare_data(data *pDataA, data *pDataB) {
    return compare_key(pDataA->code, pDataB->code);
}

// 데이터 표시
void print_data(data *pData) {
    print_key(pData->code);
    printf("  %s\t%d\t%d\n", pData->name,
     pData->Score[0], pData->Score[1]);
}

// 노드
// --------------------------------------------------
typedef struct node {
    data  data;
    long  next;
} node;

// 노드 입력
int input_node(char *sDisp, node *pNode) {
    if(input_data(sDisp, &pNode->data) == 0)
        return 0;               // 부정한 입력
    pNode->next = 0;
    return 1;
}

// 노드 비교
int compare_node(node *pNodeA, node *pNodeB) {
    return compare_data(&pNodeA->data, &pNodeB->data);
}

// 노드 표시
void print_node(node *pNode) {
    print_data(&pNode->data);
}

// 노드 읽기
int read_node(FILE *fp, long pos, node *pNode) {
    if(fseek(fp, pos, SEEK_SET) != 0)
        return 0;               // 검색 에러
    return (fread(pNode, sizeof(node), 1, fp) == 1);
}

// 노드 쓰기
int write_node(FILE *fp, long pos, node *pNode) {
    if(fseek(fp, pos, SEEK_SET) != 0)
        return 0;               // 검색 에러
    return (fwrite(pNode, sizeof(node), 1, fp) == 1);
}

// 리스트
// --------------------------------------------------
// 리스트 루트 포인터 읽기
int read_root(FILE *fp, long *p) {
    if(fseek(fp, 0L, SEEK_SET) != 0)
        return 0;               // 검색 에러
    return (fread(p, sizeof(long), 1, fp) == 1);
}

// 리스트 루트 포인터 쓰기
int write_root(FILE *fp, long *p) {
    if(fseek(fp, 0L, SEEK_SET) != 0)
        return 0;               // 검색 에러
    return (fwrite(p, sizeof(long), 1, fp) == 1);
}

// 미사용영역 포인터 읽기
int read_free(FILE *fp, long *p) {
    if(fseek(fp, sizeof(long), SEEK_SET) != 0)
        return 0;               // 검색 에러
    return (fread(p, sizeof(long), 1, fp) == 1);
}

// 미사용영역 포인터 쓰기
int write_free(FILE *fp, long *p) {
    if(fseek(fp, sizeof(long), SEEK_SET) != 0)
        return 0;               // 검색 에러
    return (fwrite(p, sizeof(long), 1, fp) == 1);
}
                               
// 리스트에 새 노드 추가
int make_list(FILE *fp, node *p) {
    long  pos, free_area, here;
    node  x, y;

    // 루트 포인터 읽기
 if(read_root(fp, &pos) == 0) return 0;
    // 미사용영역 포인터 읽기
 if(read_free(fp, &free_area) == 0) return 0;
    // 미사용영역이 없을 때
    if(free_area == 0) {       
        // 파일 마지막에 추가
  if(fseek(fp, 0L, SEEK_END) != 0)   
            return 0;
        here = ftell(fp);       // 파일 마지막 위치 얻기
    }
    else {                      // 미사용영역이 있을 때
        here = free_area;       // 최초의 미사용영역 사용
        // 최초 미사용영역 읽기
  if(read_node(fp, here, &x) == 0)
            return 0;
        free_area = x.next;     // 다음 미사용영역 설정
        // 미사용영역 포인터 쓰기
  if(write_free(fp, &free_area) == 0)
            return 0;
    }

    if(pos == 0) {              // 리스트에 노드가 없을 때
        pos = here;             // 입력 데이터의 노드를 루트로 함
        // 루트 포인터 쓰기
  if(write_root(fp, &pos) == 0)   
            return 0;
        // 로투 노드 등록
  return write_node(fp, here, p);
    }

    // 최초 노드 읽기
 if(read_node(fp, pos, &x) == 0)
        return 0;
    // 루트로서 등록할 때
 if(compare_node(p, &x) < 0) {
        pos = here;             // 루트 지정
        // 루트 포인터 변경
  if(write_root(fp, &pos) == 0)
            return 0;
        // 루트가 다음 노드를 포인터로
  p->next = x.next;      
        // 루트의 노드 등록
  return write_node(fp, here, p); 
    }

    while(x.next != 0) {        // 최후의 노드 검색
        // 다음 노드 읽기
  if(read_node(fp, x.next, &y) == 0)
            return 0;
        // 삽입 위치 조사
  if(compare_node(p, &y) < 0)     
            break;
        pos = x.next;           // 1개씩 뒤로
        x = y;                  // 1개씩 뒤로
    }
    p->next = x.next;           // 등록 노드 삽입
    x.next = here;              // 새 노드 추가
    // (*)포인터 쓰기
 if(write_node(fp, pos, &x) == 0)
        return 0;
    // 새 노드 쓰기
 return write_node(fp, here, p);
}

// 리스트로부터 노드 삭제
int delete_list(FILE *fp, key *key) {
    long  pos, free_area, here;
    node  d, x, y;
    int   ret;

    d.data.code = *key;         // 키 값을 갖는 노드 작성
    // 루트 포인트 읽기
 if(read_root(fp, &pos) == 0)    
        return 0;

    if(pos == 0)                // 리스트에 노드가 없을 때
        return 2;

    // 최초 노드 읽기
 if(read_node(fp, pos, &y) == 0) 
        return 0;
    // 노드가 없을 때
 if((ret = compare_node(&y, &d)) > 0)
        return 2;

    if(ret == 0) {              // 노드가 검색됬을 때
        here = pos;             // 삭제할 노드 어드레스
        pos = y.next;           // 루트에 다음 노드 연결
        // 루트의 포인터 쓰기
  if(write_root(fp, &pos) == 0)   
            return 0;
    }
    else {                      // 노드를 다시 검색
        for( ; ; ) {
            if(y.next == 0)     // 노드가 검색안됨
                return 2;
            x = y;              // 하나 전의 노드를 보존
            here = x.next;
            // 다음 노드 읽기
   if(read_node(fp, here, &y) == 0)
                return 0;
            // 위치 조사
   if((ret = compare_node(&d, &y)) < 0) 
                return 2;
            if(ret == 0)        // 검색됨
                break;
            pos = here;         // 다시 검색
        }
        x.next = y.next;        // 노드 삭제
        // (*)포인터 쓰기
  if(write_node(fp, pos, &x) == 0)
            return 0;
    }

    // 미사용 영역의 포인터 읽기
 if(read_free(fp, &free_area) == 0)
        return 0;

    y.next = free_area;         // 미사용 영역 연결
    // (*)포인터 쓰기
 if(write_node(fp, here, &y) == 0) 
        return 0;
    free_area = here;           // 새로운 미사용 영역 삽입
    // 미사용 영역의 포인터 쓰기
 return write_free(fp, &free_area);
}

// 검색하기
node *search_list(FILE *fp, key *key) {
    static node x;
    node  y;
    long  pos;
    int   ret;

    y.data.code = *key;         // 키 값을 갖는 노드 만들기
    // 루트 포인터 읽기
 if(read_root(fp, &pos) == 0)      
        return NULL;

    while(pos != 0) {           // 노드가 없으면 종료
        // 다음 노드 읽기
  if(read_node(fp, pos, &x) == 0)   
            break;
        // 검색 됨
  if((ret = compare_node(&x, &y)) == 0)
            return &x;
        if(ret >= 0)            // 존재 안함
            break;
        pos = x.next;           // 다음 노드 검색
    }
    return NULL;                // 검색 안됨
}

// 리스트의 노드를 처음부터 순서대로 표시
int print_list(FILE *fp) {
    long  pos;
    node  x;

    // 루트 포인터 읽기
 if(read_root(fp, &pos) == 0)      
        return 0;

    while(pos != 0) {
        // 다음의 노드 읽기
  if(read_node(fp, pos, &x) == 0)  
            return 0;
        print_node(&x);
        pos = x.next;
    }
    return 1;
}

// 리스트 디스크에 쓰기
// --------------------------------------------------
char  *filename;
FILE  *fp;

// 초기화
#pragma argsused
void initialize(int argc, char *argv[]) {
    long  dummy = 0;

#if defined(DEBUG)
    filename = "test.dat";
#else
    if(argc > 2) {
        printf("파라미터가 너무 많아n");
        exit(1);
    }
    if(argc < 2) {
        printf("파라미터가 없네\n");
        exit(2);
    }
    filename = argv[1];
#endif
    if((fp = fopen(filename, "r+b")) == NULL) {
        if((fp = fopen(filename, "w+b")) == NULL) {
            printf("파일 \"%s\" 을 열 수 없네\n", filename);
            exit(3);
        }
        // 노드 포인터 초기화
        fwrite(&dummy, sizeof(long), 1, fp);
        // 미사용영역 포인터 초기화
        fwrite(&dummy, sizeof(long), 1, fp);
    }
}

// 종료
void terminate(void) {
    fclose(fp);
}

// 메뉴
int menu(void) {
    int  k, end_flag = 0;

    while(end_flag == 0) {
        printf("\n"
   "--------------------\n"
   "  (1) 데이터 입력\n"
            "  (2) 데이터 검색\n"
            "  (3) 데이터 삭제\n"
            "  (4) 데이터 표시\n"
            "  (5) 종료\n"
   "--------------------\n"
            "메뉴 선택 : ");
        switch(scanf("%d", &k)+1) {
          case 0:       // EOF 종료 취급
            k = 5;
            end_flag = 1;
            break;
          case 1:       // 부정 데이터 재입력
            rewind(stdin);
            break;
          case 2:
            if(1 <= k && k <= 5)
                end_flag = 1;
            break;
        }
    }
    return k;
}

void process(void) {
    int  end_flag = 0;
    node x, *p;
    key  key;

    while(end_flag == 0) {
        switch(menu()) {
          case 1:       // 입력
     if(input_node("\n입력 취소 >> 0\n",
                     &x) != 0)
                make_list(fp, &x);
            //D(print_list(fp);)
   print_list(fp);
            break;
          case 2:       // 검색
            if(input_key("코드 번호 : ", &key) == 0)
                printf("잘못된 코드 번호\n");
            else {
                if((p = search_list(fp, &key)) == NULL)
                    printf("없음n");
                else
                    print_node(p);
            }
            break;
          case 3:       // 삭제
            if(input_key("코드 번호 : ", &key) == 0)
                printf("잘못된 코드 번호\n");
            else {
                switch(delete_list(fp, &key)) {
                  case 0:
                    printf("파일 입출력 에러\n");
                    break;
                  case 1:
                    print_list(fp);
                    break;
                  case 2:
                    printf("없음\n");
                    break;
                }
            }
            break;
          case 4:       // 표시
            print_list(fp);
            break;
          case 5:       // 종료
            end_flag = 1;
            break;
        }
    }
}

int main(int argc, char *argv[]) {
    initialize(argc, argv);
    process();
    terminate();
    return 0;
}


Comments