5.2.2) StringBuffer 클래스
StringBuffer 클래스는 문자열을 다루기 쉽도록 필자가 고안한 클래스다. StringBuffer 클래스의 인스턴스는 다음의 행위를 수행할 수 있다.
- init: 버퍼를 인자로 받은 문자열로 초기화한다
- getc: 버퍼로부터 문자를 하나 가져온다
- ungetc: 버퍼에서 읽었던 값을 되돌린다
- add: 버퍼의 끝에 문자 또는 문자열을 추가한다
- is_empty: 버퍼가 비어있는지 확인한다
다음은 StringBuffer 클래스를 구현한 것이다.
StringBuffer.h |
#ifndef __HANDY_STRINGBUFFER_H__ #define __HANDY_STRINGBUFFER_H__
#include <string> class StringBuffer { std::string str; unsigned len; unsigned idx;
public: StringBuffer(const char *s = ""); StringBuffer(const std::string &str); ~StringBuffer();
// 버퍼를 문자열로 초기화합니다. void init(const char *str); void init(const std::string &str);
// 버퍼로부터 문자를 하나 읽습니다. 포인터가 이동합니다. char getc(); // 버퍼의 포인터가 가리키는 문자를 가져옵니다. 포인터는 이동하지 않습니다. char peekc() const; // 버퍼에서 읽었던 값을 되돌립니다. 되돌릴 수 없으면 false를 반환합니다. bool ungetc();
// 버퍼의 끝에 문자 또는 문자열을 추가합니다. void add(char c); void add(const char *s); void add(const std::string &str);
// 버퍼가 비어있다면 true, 값을 더 읽을 수 있다면 false를 반환합니다. bool is_empty() const; };
#endif |
StringBuffer.cpp |
#include "StringBuffer.h" #include <string> typedef std::string Exception; StringBuffer::StringBuffer(const char *s) : str(s), idx(0) { this->len = this->str.length(); } StringBuffer::StringBuffer(const std::string &str) : str(str), idx(0) { this->len = this->str.length(); } StringBuffer::~StringBuffer() {} void StringBuffer::init(const char *str) { this->str = str; this->idx = 0; this->len = this->str.length(); } void StringBuffer::init(const std::string &str) { this->str = str; this->idx = 0; this->len = this->str.length(); } char StringBuffer::getc() { if (idx >= len) { throw Exception("Buffer is empty"); } return str[idx++]; } char StringBuffer::peekc() const { if (idx >= len) { throw Exception("Buffer is empty"); } return str[idx]; } bool StringBuffer::ungetc() { if (idx > 0) { --idx; return true; } else { return false; } } void StringBuffer::add(char c) { this->str += c; } void StringBuffer::add(const char *s) { this->str += s; } void StringBuffer::add(const std::string &str) { this->str += str; } bool StringBuffer::is_empty() const { return (idx >= len); } |
다음은 StringBuffer 클래스를 사용하는 예제이다.
13_StringBufferMain.cpp |
#include <iostream> #include "StringBuffer.h" typedef std::string Exception; int main(void) { try { StringBuffer buffer; buffer.init("Hello, world!"); while (buffer.is_empty() == false) { std::cout << buffer.getc() << std::endl; } return 0; } catch (Exception &ex) { std::cerr << ex.c_str() << std::endl; return 1; } } |
그리고 이를 이용하여 사칙 연산 계산기를 다시 작성할 수 있다.
14_basic4_StringBuffer.cpp |
int ascii_to_int(StringBuffer &buffer) { // 문자열을 정수로 변환하고 그 값을 반환 int digit = 0; int value = 0; char ch; while (buffer.is_empty() == false) { if (is_digit(ch = buffer.getc()) == false) { buffer.ungetc(); // 아직 읽지 않은 값이므로 되돌린 후 탈출한다 break; } digit = ch - '0'; value = 10 * value + digit; } return value; } int calculate(const char *expr) { // 넘겨받은 식을 계산하여 값을 반환한다 StringBuffer buffer(expr); if (is_digit(buffer.peekc()) == false) { // 입력의 처음이 숫자가 아니라면 예외 throw Exception("타당하지 않은 입력입니다."); } int left = ascii_to_int(buffer); // 왼쪽에 나타나는 수 획득 if (buffer.is_empty()) { // 입력이 끝났다면 획득한 정수를 반환하고 종료 return left; } // 연산자 획득: 사칙 연산에 대해서만 다루므로 연산자 길이는 반드시 1 char op = buffer.getc(); // 문자열 포인터가 가리키는 연산자를 획득 후 포인터 이동 int right = ascii_to_int(buffer); // 오른쪽에 나타나는 수 획득 if (buffer.is_empty() == false) { // 입력이 아직 끝나지 않았다면 예외 발생 throw Exception("두 개의 피연산자로만 연산할 수 있습니다."); } // 획득한 값과 연산자를 이용하여 연산 int retVal = 0; switch (op) { case '+': retVal = left + right; break; case '-': retVal = left - right; break; case '*': retVal = left * right; break; case '/': retVal = left / right; break; default: throw Exception("올바른 연산자가 아닙니다."); } return retVal; } |
독자 중엔 StringBuffer 클래스가 이전과 크게 편의성에서 차이가 나지 않는다고 생각하는 사람도 있을 것이다. 그 경우에는 자신이 원하는 방법으로 진행하면 된다. 사실 StringBuffer 클래스는 완성된 클래스가 아니고, 프로젝트를 진행하면서 기능을 추가하여, 이 클래스가 없이 작업하는 것을 상상할 수 없을 정도로 만들 것이다. 이와 같이StringBuffer 클래스를 활용하고 이해할 수 있었다.