diff --git a/README.md b/README.md index 13420b29..9dfc089a 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# javascript-calculator-precourse \ No newline at end of file +# javascript-calculator-precourse + +# 기능 목록 + +1. **입력 및 출력** 기능 + +2. **커스텀 구분자 파싱** 기능 + +3. 구분자(+커스텀)로 문자열의 **숫자 분리** 기능 + +4. 3번의 유효성 검증 실패 시 **에러 핸들링** 기능 + +5. 3번의 유효성 검증 성공 시 구분자로 분리한 **각 숫자의 합**을 구하는 기능 diff --git a/src/App.js b/src/App.js index 091aa0a5..416e2a2b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,14 @@ +import Calculator from './calculator.js'; + class App { - async run() {} + async run() { + const calculator = new Calculator(); + + await calculator.input(); // 입력 + const result = calculator.calculate(); + + calculator.output(result); // 출력 + } } export default App; diff --git a/src/calculator.js b/src/calculator.js new file mode 100644 index 00000000..78cba66e --- /dev/null +++ b/src/calculator.js @@ -0,0 +1,104 @@ +import { Console } from '@woowacourse/mission-utils'; +import { + DEFAULT_DELIMITERS, + ERROR_INVALID_FORMAT, + ERROR_NON_POSITIVE, + INPUT_MESSAGE, + OUTPUT_PREFIX, +} from './constants.js'; +import { isNumberChar } from './utils.js'; + +class Calculator { + constructor() { + this.delimiterList = [...DEFAULT_DELIMITERS]; // 구분자 + this.inputText = ''; // 입력 문자열 + } + + // 문자열 연산 + calculate() { + this.parseCustomDelimiter(); + + const numbers = this.parseNumber(); + return this.sum(numbers); + } + + // 배열의 숫자들을 더한 후 반환 + sum(numbers) { + return numbers.reduce((acc, cur) => acc + cur, 0); + } + + // 문자열 길이 반환 + size() { + return this.inputText.length; + } + + // 커스텀 구분자 파싱 + parseCustomDelimiter() { + if (this.size() < 5) return; + + // 구분자가 없거나 틀린 형식일 시 return + if (this.inputText[0] !== '/') return; + if (this.inputText[1] !== '/') return; + if (this.inputText[3] !== '\\') return; + if (this.inputText[4] !== 'n') return; + + this.delimiterList.push(this.inputText[2]); // 커스텀 구분자 추가 + this.inputText = this.inputText.substring(5); // 커스텀 구분자 문자열 삭제 + } + + // delimiterList(구분자)로 문자열의 숫자를 파싱해서 배열로 반환 + // 예외 발생 시 애플리케이션이 종료된다. + parseNumber() { + const numbers = [0]; // 기본값 : 0 + let chunk = ''; + + for (let i = 0; i < this.size(); i++) { + const c = this.inputText[i]; + + // chunk에 숫자 추가 + if (isNumberChar(c)) { + chunk += c; + + if (i + 1 < this.size()) continue; + } + + // 문자열의 마지막이나 구분자를 만날 경우 + if (i + 1 === this.size() || this.delimiterList.includes(c)) { + const number = Number(chunk); // 형변환 + + // 숫자가 아닐 경우 + if (Number.isNaN(number)) throw new Error(ERROR_INVALID_FORMAT); + + // 양수가 아닐 경우 + if (number <= 0) throw new Error(ERROR_NON_POSITIVE); + + numbers.push(number); + chunk = ''; + continue; + } + + // 숫자, 구분자 모두 아닐 경우 + throw new Error(ERROR_INVALID_FORMAT); + } + + return numbers; // 성공 시 분리된 숫자 배열을 반환 + } + + // 문자열 입력 + async input() { + try { + const input = await Console.readLineAsync(INPUT_MESSAGE); + + this.inputText = input; + } catch { + throw new Error(ERROR_INVALID_FORMAT); + } + } + + // 결과 출력 + output(result) { + Console.print(`${OUTPUT_PREFIX}${result}`); + } +} + +export default Calculator; diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 00000000..55dea8ad --- /dev/null +++ b/src/constants.js @@ -0,0 +1,7 @@ +export const DEFAULT_DELIMITERS = [',', ':']; // 기본 구분자 + +export const INPUT_MESSAGE = '덧셈할 문자열을 입력해 주세요.\n'; +export const OUTPUT_PREFIX = '결과 : '; + +export const ERROR_INVALID_FORMAT = '[ERROR] 입력 형식이 올바르지 않습니다.'; +export const ERROR_NON_POSITIVE = '[ERROR] 양수를 입력해주세요.'; diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 00000000..0e88e1de --- /dev/null +++ b/src/utils.js @@ -0,0 +1,3 @@ +export const isNumberChar = (c) => { + return '0' <= c && c <= '9'; +};