202121006 - reverse 프로젝트#
개요#
reverse는 역순 프로그램으로 지정된 입력 파일에서 데이터를 읽어 역순으로 만드는 것입니다. 다양하게 문자열, 숫자, 데이터 구조 등이 있습니다.
주요기능#
입력 : 프로그램이 데이터를 읽어들이는 것
출력 : 프로그램이 데이터를 출력하는 것
반전 : 주어진 데이터를 반전시키는 것
요류 : 오류 메세지를 출력하는 것
리디렉션 : 프로그램의 입력 또는 출력을 파일이나 다른 프로그램으로 재지정하는 것
연결 리스트 : 각 요소가 다음 요소에 대한 참조를 포함하는 선형 데이터 구조
역순: 항목의 순서를 거꾸로 바꾸는 것
사용기술#
fopen(), getline(), fclose() : 입력 파일을 읽고 출력하는 코드를 작성하는 언어
fprintf() : 화면에 출력하기 위한 언어
fgets() : 파일에서 한 줄씩 읽어드리는 언어
append() : 연결 리스트에 추가하는 언어
printReverse() : 재귀를 사용하여 연결 리스트에 역순으로 출력하는 언어
진행 계획#
“Reverse”를 구현하기 위한 진행 계획은 다음과 같이 할 수 있습니다
요구 사항 분석: 먼저, 어떤 종류의 데이터를 역순으로 처리할지, 어떤 방식으로 구현할지 등을 분석합니다. 예를 들어, 문자열, 숫자, 배열 등을 역순으로 만들어야 하는지를 확인합니다.
구현 환경 설정: 필요한 프로그래밍 언어나 도구를 선택하고 설정합니다. 언어에 따라 문자열 처리 함수, 배열 또는 리스트 조작 메서드 등을 활용할 수 있습니다.
알고리즘 설계: 선택한 데이터 구조나 문제의 특성에 맞는 알고리즘을 설계합니다. 예를 들어, 문자열을 반복하여 각 문자를 역순으로 배열하는 방식 등을 선택할 수 있습니다.
코드 구현: 설계한 알고리즘을 바탕으로 코드를 구현합니다. 문자열, 숫자, 배열 등을 역순으로 처리하는 코드를 작성합니다.
테스트: 작성한 코드를 테스트하여 정확성과 성능을 확인합니다. 다양한 입력값에 대해 코드가 제대로 동작하는지를 검증합니다.
최적화(Optional): 필요한 경우 코드의 성능을 향상시키기 위해 최적화 작업을 수행합니다. 예를 들어, 반복문의 개수를 줄이거나 특정한 데이터 구조를 사용하여 처리 속도를 향상시킬 수 있습니다.
프로젝트 결과#
이 프로젝트는 파일로부터 데이터를 읽어 연결 리스트에 저장한 후, 리스트를 역순으로 출력하는 프로그램입니다.
code#
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 노드 구조체 정의
typedef struct Node {
char *data;
struct Node *next;
} Node;
// 연결 리스트에 노드를 추가하는 함수
void append(Node **head, char *data) {
Node *new_node = (Node *)malloc(sizeof(Node));
new_node->data = strdup(data);
new_node->next = NULL;
if (*head == NULL) {
*head = new_node;
return;
}
Node *last = *head;
while (last->next != NULL) {
last = last->next;
}
last->next = new_node;
}
// 연결 리스트를 역순으로 출력하는 재귀 함수
void printReverse(Node *node) {
if (node == NULL) {
return;
}
printReverse(node->next);
printf("%s", node->data);
}
// Windows에서 getline 함수 대체
ssize_t get_line(char **lineptr, size_t *n, FILE *stream) {
size_t len = 0;
if (feof(stream)) return -1;
if (!*lineptr) {
*lineptr = malloc(128);
if (!*lineptr) return -1;
*n = 128;
}
for (int c; (c = fgetc(stream)) != EOF;) {
if (len + 1 >= *n) {
size_t new_size = *n + 128;
char *new_ptr = realloc(*lineptr, new_size);
if (!new_ptr) return -1;
*lineptr = new_ptr;
*n = new_size;
}
(*lineptr)[len++] = c;
if (c == '\n') break;
}
(*lineptr)[len] = '\0';
return len;
}
// 메인 함수
int main() {
FILE *file;
char *line = NULL;
size_t len = 0;
ssize_t read;
Node *head = NULL;
file = fopen("C:\\c code\\text\\input.txt", "r");
if (file == NULL) {
fprintf(stderr, "파일을 열 수 없습니다.\n");
exit(EXIT_FAILURE);
}
while ((read = get_line(&line, &len, file)) != -1) {
append(&head, line);
}
fclose(file);
if (line) {
free(line);
}
printf("역순 출력:\n");
printReverse(head);
// 메모리 해제
Node *temp;
while (head != NULL) {
temp = head;
head = head->next;
free(temp->data);
free(temp);
}
return 0;
}
code 결과#
주어진 파일 ‘input.txt’의 내용이 역순으로 출력되는 것입니다.
input.txt
hello
this
is
a file
good
day
pes
202121006
프로그램을 실행하면 출력 결과는 다음과 같습니다.
결과
202121006
pes
day
good
a file
is
this
hello
동작 과정 요약#
파일 열기: main 함수에서 input.txt 파일을 엽니다. 파일을 열 수 없으면 오류 메시지를 출력하고 종료합니다.
파일 읽기: get_line 함수를 사용하여 파일에서 한 줄씩 읽어옵니다.
노드 추가: 읽어온 각 줄을 append 함수를 사용하여 연결 리스트에 추가합니다.
역순 출력: printReverse 함수를 사용하여 연결 리스트의 데이터를 역순으로 출력합니다.
메모리 해제: 연결 리스트의 노드와 데이터를 해제하여 메모리 누수를 방지합니다.
코드 설명#
‘Node’ 구조체: 연결 리스트의 각 노드를 정의합니다. 각 노드는 문자열 데이터를 저장하고, 다음 노드를 가리키는 포인터를 가집니다.
‘append’ 함수: 새로운 노드를 생성하여 연결 리스트의 끝에 추가합니다.
‘printReverse’ 함수: 재귀적으로 호출되어 연결 리스트의 데이터를 역순으로 출력합니다.
‘get_line’ 함수: 파일에서 한 줄씩 읽어오는 기능을 구현합니다. getline 함수를 사용할 수 없는 환경을 고려한 대체 함수입니다.
‘main’ 함수: 파일을 열고 데이터를 읽어 연결 리스트에 저장한 후, 역순으로 출력하고 메모리를 해제합니다.
주의사항#
파일 경로는 코드에 하드코딩되어 있으므로, 실행 환경에 맞게 파일 경로를 수정해야 합니다.
프로그램이 성공적으로 실행되기 위해서는 input.txt 파일이 존재하고, 읽기 권한이 있어야 합니다.
이 프로젝트는 연결 리스트의 기본적인 사용법과 재귀 호출을 이용한 역순 출력 방법을 학습하는 데 유용합니다.
개선 방향#
이 프로젝트를 개선하기 위해 다음과 같은 방향을 고려할 수 있습니다
파일 경로의 유연성:
현재 파일 경로가 코드에 하드코딩되어 있어, 다른 파일을 읽으려면 코드 수정이 필요합니다. 사용자로부터 파일 경로를 입력받도록 수정하면 유연성이 증가합니다.
에러 처리 개선:
파일이 존재하지 않거나 읽기 권한이 없는 경우에 대한 에러 처리를 개선합니다. 예외 처리를 추가하여 오류 메시지를 사용자에게 명확히 전달합니다.
메모리 관리:
현재 코드에서는 연결 리스트의 메모리를 수동으로 해제하고 있습니다. 이를 자동으로 처리할 수 있는 구조를 도입하여 메모리 누수 가능성을 줄입니다.
코드 구조 개선:
함수들을 모듈화하고, 파일 입출력, 리스트 조작, 출력 등을 별도의 함수로 분리하여 가독성과 유지보수성을 높입니다.
테스트 케이스 추가:
다양한 입력에 대해 프로그램이 정상적으로 동작하는지 확인하는 테스트 케이스를 작성합니다. 이를 통해 예기치 않은 입력 상황에 대한 처리가 제대로 되는지 확인할 수 있습니다.
입력 데이터 검증:
입력 데이터가 올바른 형식인지 확인하고, 부적절한 데이터에 대해 경고하거나 처리를 중지하는 로직을 추가합니다.
다국어 지원:
출력 메시지와 오류 메시지를 다국어로 지원하여, 다양한 언어를 사용하는 사용자가 편리하게 사용할 수 있도록 합니다.
유닛 테스트 추가:
각 함수에 대해 유닛 테스트를 작성하여 코드의 안정성을 확보합니다.
메모리 사용 최적화:
연결 리스트의 데이터 복사 및 메모리 할당 방식을 최적화하여 성능을 개선할 수 있습니다.
개선 사항 요약#
사용자로부터 파일 경로를 입력받아 유연성을 높임
메모리 할당 실패 시 오류 처리 추가
메모리 해제 함수를 분리하여 코드의 가독성 및 재사용성 향상
프로그램 시작 시 파일 경로 입력을 받도록 개선하여 하드코딩된 파일 경로 문제 해결
이와 같은 개선을 통해 프로그램의 유연성, 안정성, 유지보수성을 향상시킬 수 있습니다.