프로그래밍 자료/C & C++

[C언어/자료구조] 후위 표기법 계산기

미친사람 2021. 12. 30. 00:35
반응형

리스트 스택 헤더 파일 및 소스 파일

// 후위 표기법 계산 소스 파일 

#include <stdio.h>		// 입출력 관련 
#include <ctype.h>		// 숫자인지, 문자인지 

#include "ListStack.h"
#include "Calculator.h"

Data CalcPostfix(char exp[]) {
	Stack stack;
	S_Init(&stack);
	
	Data opr1, opr2;
	int i, value, k = 0;
	
	for(i = 0; exp[i] != '\0'; i++) {
		if(isdigit(exp[i])) {
		// 문자가 숫자인지 아닌지 확인 ( 0 ~ 9 ) 
			// 공백이 아니라면 k 값 상승 
			while(exp[i + k] != ' ') {
				k++;
			}
			
			// 문자를 정수로 바꿔주는 함수 atoi 사용 
			// 공백이 나온다면 거기까지만 정수 변환 ( "100 20 300" => 100 ) 
			S_Push(&stack, atoi(&exp[i]));
			
			// i 값을 공백이 위치한 장소로 점프 
			i += k;
			// k 의 값은 다시 0으로 만들어준다. 
			k = 0;
		} else {
			// 공백이라면 넘긴다. 
			if(exp[i] == ' ') continue;
			
			// 스택에 있는 숫자를 빼서 각 변수에 넣어줌, 변수 순서에 주의 
			opr2 = S_Pop(&stack);
			opr1 = S_Pop(&stack);
			
			// 연산자에 따른 계산 
			switch(exp[i]) {
				case '+': S_Push(&stack, opr1 + opr2); break;
				case '-': S_Push(&stack, opr1 - opr2); break;
				case '*': S_Push(&stack, opr1 * opr2); break;
				case '/': S_Push(&stack, opr1 / opr2); break;
			}
		}
	}
	
	// 마지막으로 스택에 있는 값을 리턴 
	return S_Pop(&stack);
}
// 리스트 스택 소스 파일 

#include <stdio.h>		// 입출력 관련 
#include <stdlib.h>		// 동적할당 관련 

#include "ListStack.h"

void S_Init(Stack * pstack) {
	pstack->top = NULL;
}

int S_Empty(Stack * pstack) {
	if(pstack->top == NULL) {
		return TRUE;
	} else {
		return FALSE;
	}
}

void S_Push(Stack * pstack, Data data) {
	Node * newStack = (Node*)malloc(sizeof(Node));
	newStack->data = data;
	
	newStack->next = pstack->top;
	pstack->top = newStack;
}

Data S_Pop(Stack * pstack) {
	if(S_Empty(pstack)) {
		printf("\n>> 뽑아낼 데이터가 없습니다.");
		exit(-1);
	}
	
	Node * rnode = pstack->top;			// 삭제할 노드 저장 
	Data rdata	 = rnode->data;			// 반환할 데이터 저장 

	pstack->top	= pstack->top->next;	// 삭제하기전 최상단 위치를 다음으로 변경 
	free(rnode);						// 삭제 
	
	return rdata;						// 데이터 반환 
}

Data S_Peek(Stack * pstack) {
	if(S_Empty(pstack)) {
		printf("\n>> 확인할 데이터가 없습니다.");
		exit(-1);
	}
	
	return pstack->top->data;
}

 


 

계산기 헤더 파일 작성

// 후위 표기법 계산기 헤더 파일 

#ifndef __CALCULATOR_H__
#define __CALCULATOR_H__

// 계산 전 문법 검사 
int SyntaxSearch(char exp[]);

// 후위 표기법 변환 
void ConvToPostfix(char exp[]);

// 후위 표기법 계산 
Data CalcPostfix(char exp[]);

#endif

 

후위 표기법 변환 소스 파일 작성

// 후위 표기법 변환 소스 파일 

#include <stdio.h>		// 입출력 관련 
#include <stdlib.h>		// 동적할당 관련 
#include <string.h>		// 문자열 관련 
#include <ctype.h>		// 숫자인지, 문자인지 

#include "ListStack.h"
#include "Calculator.h"

// 우선순위 
int OptPrec(char opt) {
	switch(opt) {
		case '*': case '/':				return 5;
		case '+': case '-':				return 3;
		case '(': case '{': case '[':	return 1;
	}
	
	return -1;
}

// 후위표기법 
void ConvToPostfix(char exp[]) {
	Stack stack;
	S_Init(&stack);
	
	int expLen = strlen(exp);
	char * ConvExp = (char*)malloc(expLen * 2);
	int i, idx = 0, popOp;

	for(i = 0; exp[i] != '\0'; i++) {
		if(exp[i] == ' ') continue;
		
		if(isdigit(exp[i])) {
		// 문자가 숫자인지 아닌지 확인 ( 0 ~ 9 ) 
			// 숫자라면 문자 배열에 투입 
			ConvExp[idx++] = exp[i];
		} else {
			// 숫자가 아니라면 스택에 투입 
			switch(exp[i]) {
				case '(': case '{': case '[':
					S_Push(&stack, exp[i]);
					break;
				case ')': case '}': case ']':
					while(1) {
						popOp = S_Pop(&stack);
						
						if(popOp == '(' || popOp == '{' || popOp == '[') {
							break;
						}
						
						ConvExp[idx++] = ' ';
						ConvExp[idx++] = popOp;
					}
					break;
				case '+': case '-': case '*': case '/':
					// 값이 비어있지 않고, 기존 값이 현재 값보다 크거나 같으면 
					while(!S_Empty(&stack) && OptPrec(exp[i]) <= OptPrec(S_Peek(&stack))) {
						ConvExp[idx++] = ' ';
						ConvExp[idx++] = S_Pop(&stack);
					}
					ConvExp[idx++] = ' ';
					S_Push(&stack, exp[i]);
					break;
			}
		}
		
	}
	
	// 스택에 있는 연산자 모두 배열 삽입 
	while(!S_Empty(&stack)) {
		ConvExp[idx++] = ' ';
		ConvExp[idx++] = S_Pop(&stack);
	}
	
	// 문자열 끝맺음 
	ConvExp[idx] = '\0';
	
	strcpy(exp, ConvExp);
	free(ConvExp);
}

 

후위 표기법 계산기 소스 파일 작성

// 후위 표기법 계산 소스 파일 

#include <stdio.h>		// 입출력 관련 
#include <ctype.h>		// 숫자인지, 문자인지 

#include "ListStack.h"
#include "Calculator.h"

Data CalcPostfix(char exp[]) {
	Stack stack;
	S_Init(&stack);
	
	Data opr1, opr2;
	int i, value, k = 0;
	
	for(i = 0; exp[i] != '\0'; i++) {
		if(isdigit(exp[i])) {
		// 문자가 숫자인지 아닌지 확인 ( 0 ~ 9 ) 
			// 공백이 아니라면 k 값 상승 
			while(exp[i + k] != ' ') {
				k++;
			}
			
			// 문자를 정수로 바꿔주는 함수 atoi 사용 
			// 공백이 나온다면 거기까지만 정수 변환 ( "100 20 300" => 100 ) 
			S_Push(&stack, atoi(&exp[i]));
			
			// i 값을 공백이 위치한 장소로 점프 
			i += k;
			// k 의 값은 다시 0으로 만들어준다. 
			k = 0;
		} else {
			// 공백이라면 넘긴다. 
			if(exp[i] == ' ') continue;
			
			// 스택에 있는 숫자를 빼서 각 변수에 넣어줌, 변수 순서에 주의 
			opr2 = S_Pop(&stack);
			opr1 = S_Pop(&stack);
			
			// 연산자에 따른 계산 
			switch(exp[i]) {
				case '+': S_Push(&stack, opr1 + opr2); break;
				case '-': S_Push(&stack, opr1 - opr2); break;
				case '*': S_Push(&stack, opr1 * opr2); break;
				case '/': S_Push(&stack, opr1 / opr2); break;
			}
		}
	}
	
	// 마지막으로 스택에 있는 값을 리턴 
	return S_Pop(&stack);
}

 

후위 표기법 만들기 전 문법 검사 소스 파일

// 후위 표기법 변환 전 문법 검사 

#include <stdio.h>		// 입출력 관련  
#include <string.h>		// 문자열 관련 
#include <ctype.h>		// 숫자인지, 문자인지 

#include "ListStack.h"
#include "Calculator.h"

int SyntaxSearch(char exp[]) {
	Stack stack;
	S_Init(&stack);
	
	int i, count;
	char opt, bracket, check;
	
	for(i = 0; exp[i] != '\0'; i++) {
		// 공백이면 넘김 
		if(exp[i] == ' ') continue;
		
		check = exp[i];
		
		switch(check) {
			// 연산자 다음 값이 연산자인 경우 
			case '+': case '-': case '*': case '/':
				opt = exp[i + 1];
				if(opt == '+' || opt == '-' || opt == '*' || opt == '/') {
					printf(">> ' %c ' 연산자 오류!\n", opt);
					return -1;
				} else if(!isdigit(opt) && opt != '(' && opt != '{' && opt != '[' && opt != ' ') {
				// 연산자 다음 값이 괄호가 아니며, 숫자가 아니며, 공백이 아닌 경우 
					printf(">> ' %c ' 연산자 오류!\n", check);
					return -1;
				}
				break;
			
			// 여는 괄호인 경우 스택에 저장 
			case '(': case '{': case '[':
				S_Push(&stack, check);
				break;
			
			// 닫는 괄호인 경우 
			case ')': case '}': case ']':
				bracket = exp[i];
				
				// 스택이 존재 하지 않는 경우  
				if(S_Empty(&stack)) {
					printf(">> ' %c ' 문법 오류!\n", bracket);
					return -1;
				} else {
				// 스택이 존재 하지만 괄호가 다를 경우 
					if(bracket == ')') {
						if(S_Pop(&stack) != '(') {
							printf(">> ' %c ' 문법 오류!\n", bracket);
							return -1;
						}
					} else if(bracket == '}') {
						if(S_Pop(&stack) != '{') {
							printf(">> ' %c ' 문법 오류!\n", bracket);
							return -1;
						}
					} else {
						if(S_Pop(&stack) != '[') {
							printf(">> ' %c ' 문법 오류!\n", bracket);
							return -1;
						}
					}
				}
				break;
			
			default:
				// 숫자가 아닌 경우 , 문자인 경우 
				if(!isdigit(check)) {
					printf(">> 숫자를 입력해주세요 !\n");
					return -1;
				} else {
					bracket = exp[i + 1];
					
					// 숫자 다음이 연산자가 아닌 여는 괄호인 경우 
					if(bracket == '(' || bracket == '{' || bracket == '[' ) {
						printf(">> 괄호 문법 오류!\n");
						return -1;
					}
					
					count++;
				}
				break;
		}
	}
	
	// 하나도 입력되지 않았거나, 하나만 입력된 경우 
	if(!count || count == 1) {
		printf(">> 잘못된 형식입니다.\n");
		return -1;
	}
	
	// 비어 있지 않은 경우 작동 
	while(!S_Empty(&stack)) {
		// 여는 괄호가 많을 경우 
		opt = S_Pop(&stack);
		if(opt == '(' || opt == '{' || opt == '[') {
			printf(">> ' %c ' 문법 오류!\n", opt);
			return -1;
		}
	}
	
	return 0;
}

 


메인 소스 파일

#include <stdio.h>		// 입출력 관련 
#include <string.h>		// 문자열 관련 

#include "ListStack.h"
#include "Calculator.h"

int main(void) {
	
	char exp[101];
	Data result;

	while(1) {
		printf("중위 표기법을 입력해주세요 [종료 : exit]");
		printf("\n\n");
		
		printf("입력 : ");
		gets(exp);
		rewind(stdin);
		
		if(strcmp(exp, "exit") == 0) {
			break;
		}
		
		printf("\n");
		
		if(SyntaxSearch(exp) != -1) {
			printf("> 중위 표기법 : %s\n", exp);
			
			ConvToPostfix(exp);
			printf("> 후위 표기법 변환 : %s\n", exp);
			
			printf("\n");
			result = CalcPostfix(exp);
			printf("> 계산 값 : %.2lf\n", result);
		}
		printf("\n");
		system("pause");
		system("cls");
	}
}

 


 

배운것을 응용하고, 검색해서 내가 필요한 함수를 찾으며 만들어보았다.

처음에는 그냥 계산만 가능했는데, 만들다보니 이런 저런 문제점이 있는거같아서 수정했다.

 

처음에는 공백을 입력하면 계산기가 돌아가지 않아서 공백 관련 소스코드를 작성했고

두번째는 두자릿수 이상의 중위표기법을 작성하면 후위표기법은 제대로 작동을 안해서 두자릿수 이상도 작동되게 했으며

세번째는 중위표기법 자체에 문법이 오류가 있음에도 불구하고 에러메시지가 뜨지 않고, 작동이 되어 이상한 값이 나오기에 문법 검수 관련 소스코드를 작성했다.

 

나름 만들고 보니 보람도 차고 해서 올려둔다.

왜 계산기 만들기가 수업과정에 있는지 알게되는 과정이였다.

간단하게 만들면 간단하지만, 이것저것 손을 대다보면 아는게 많아져서 그런가보다.

반응형