diff --git a/README.md b/README.md index bd90ef0247..0d4f38f6b7 100644 --- a/README.md +++ b/README.md @@ -1 +1,107 @@ -# java-calculator-precourse \ No newline at end of file +# java-calculator-precourse + +## 과제 진행 요구 사항 +- 문자열 덧셈 계산기 저장소를 포크하고 클론 하는 것으로 시작 +- 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가 +- Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가 + +## 기능 요구 사항 +> 입력한 문자열에서 숫자를 추출하여 더하는 계산기 구현 +- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다. + +`ex) "" => 0, "1,2" => 3, "1,2,3" => 6, "1,2:3" => 6` +- 앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 있다. +- 커스텀 구분자는 문자열 앞부분의 "//"와 "\n" 사이에 위치하는 문자를 커스텀 구분자로 사용한다. + +`ex) "//;\n1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론이며, 결과 값은 6이 반환되어야 한다.` + +- 사용자가 잘못된 값을 입력할 경우 `IllegalArguemntException`을 발생시킨 후 애플리케이션은 종료되어야 한다. + +## 입출력 요구 사항 +### 입력 +- 구분자와 양수로 구성된 문자열 +### 출력 +- 덧셈 결과 + +`결과 : 6` + +### 실행 결과 예시 + +`덧셈할 문자열을 입력해 주세요.` + +`1,2:3` + +`결과 : 6` + +## 프로그래밍 요구 사항 +- JDK 21 버전에서 실행 가능해야 한다. +- 프로그램 실행의 시작점은 Application의 main() 이다. +- build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다. +- 프로그램 종료 시 `System.exit()`를 호출하지 않는다. +- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. +- 자바 코드 컨벤션을 지키면서 프로그래밍한다. + +### 라이브러리 +- `camp.nextstep.edu.missionutils`에서 제공하는 Console API를 사용하여 구현해야 한다. +- 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다. + +## 기능 목록 +- 사용자가 문자열을 입력한다. +- 잘못된 값인지 확인한다. + - 잘못된 값(음수, 숫자가 아닌 문자)이라면 IllegalArgumentException 발생 후 종료 +- 커스텀 구분자가 있는지 확인한다. + - 있을 경우 커스텀 구분자를 기준으로 숫자 분리 + - 없을 경우 기본 구분자로 분리 +- 분리된 문자열을 숫자로 변환한다. +- 변환된 숫자를 합한다. +- 합을 반환한다. + +## 베운 것 +### regex 패키지 클래스 +- Pattern 클래스 + - 문자열을 정규표현식 패턴 객체로 변환해주는 역할 + - 문자열을 정규식 문법에 알맞게 구성해주지 않으면 예외 발생 + - 일반 클래스처럼 공개된 생성자를 제공하지 않아서 정규식 패턴 객체를 생성하려면 compile() 정적 메서드를 호출해야 함 +- Matcher 클래스 + - 대상 문자열의 패턴을 해석하고 주어진 패턴과 일치하는지 판별하고 반환된 필터링된 결과값들을 지니고 있음 + - Patter 클래스와 마찬가지로 공개된 생성자가 없으며, Pattern 객체의 matcher() 메서드를 호출해서 얻음 +### StringTokenizer vs split +- StringTokenizer + - java.util에 포함되어 있는 클래스 + - 문자 또는 문자열로 문자열 구분 + - 빈 문자열을 토큰으로 인식하지 않음 + - 결과 값이 문자열 + - 전체 토큰을 보고싶다면 반복문으로 하나씩 뽑아야 함 +- split + - String 클래스에 속해있는 메서드 + - 정규표현식으로 구분 + - 빈 문자열을 토큰으로 인식 + - 결과값이 문자열 배열 +### Character.isDigit() +- 주어진 문자가 숫자인지 판별 +- 문자가 숫자면 true +### 문자열을 숫자로 변환 +- java.lang.Integer 클래스의 parseInt()와 valueOf() 메서드 사용 가능 + +**Integer.parseInt()** +- 파라미터로 숫자로 변환할 문자열을 입력받고, 입력받은 문자열을 integer로 변환한 int 값을 리턴 +- private type인 int 리턴 + +**integer.valueOf()** +- 문자열을 변환하여 integer Object를 리턴 + +### replace vs replaceAll +- replace: 단순 문자열 `replace(".", "#");` +- replaceAll: 정규식 `replaceAll("\\.", "#");` + - 정규식이어서 이스케이프를 사용하지 않으면 전체로 인식해서 모든 문자가 #으로 대체 + +### IllgalArgumentException +- 메서드에 인수가 잘못된 경우 발생하는 예외 +- 메서드에 전달된 인수가 예상된 형식이나 범위를 벗어나는 경우에 발생 +- 주로 메서드의 파라미터 유효성을 검사하는데 사용 + +**발생 이유** +- 메서드에 전달된 인수의 형식이나 타입이 예상과 다를때 +- 메서드에 전달된 인수가 null이지만 null을 허용하지 않는 경우 +- 메서드에 전달된 인수가 허용되는 범위를 벗어날때 +- 기타 인수의 유효성을 검사하는 조건에 위반될 때 \ No newline at end of file diff --git a/src/main/java/calculator/Application.java b/src/main/java/calculator/Application.java index 573580fb40..b14d754015 100644 --- a/src/main/java/calculator/Application.java +++ b/src/main/java/calculator/Application.java @@ -3,5 +3,15 @@ public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + try { + String input = InputView.readInput(); + String[] splitInput = StringSplitter.splitInput(input); + int[] numbers = NumberParser.changeNumber(splitInput); + int result = SumCalculator.sum(numbers); + OutputView.printResult(result); + } catch (IllegalArgumentException e) { + System.out.println("Error " + e.getMessage()); + throw e; + } } } diff --git a/src/main/java/calculator/InputView.java b/src/main/java/calculator/InputView.java new file mode 100644 index 0000000000..a284735275 --- /dev/null +++ b/src/main/java/calculator/InputView.java @@ -0,0 +1,12 @@ +package calculator; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + + public static String readInput() { + System.out.println("덧셈할 문자열을 입력해 주세요."); + String line = Console.readLine(); + return line; + } +} diff --git a/src/main/java/calculator/NumberParser.java b/src/main/java/calculator/NumberParser.java new file mode 100644 index 0000000000..e2eaa7e992 --- /dev/null +++ b/src/main/java/calculator/NumberParser.java @@ -0,0 +1,14 @@ +package calculator; + +public class NumberParser { + public static int[] changeNumber(String[] splitInput) { + int[] numbers = new int[splitInput.length]; + for (int i = 0; i < splitInput.length; i++) { + if (!splitInput[i].matches("\\d+")) { + throw new IllegalArgumentException("형식을 벗어남"); + } + numbers[i] = Integer.valueOf(splitInput[i]); + } + return numbers; + } +} diff --git a/src/main/java/calculator/OutputView.java b/src/main/java/calculator/OutputView.java new file mode 100644 index 0000000000..833953b69e --- /dev/null +++ b/src/main/java/calculator/OutputView.java @@ -0,0 +1,7 @@ +package calculator; + +public class OutputView { + public static void printResult(int result) { + System.out.println("결과 : " + result); + } +} diff --git a/src/main/java/calculator/StringSplitter.java b/src/main/java/calculator/StringSplitter.java new file mode 100644 index 0000000000..ceda5d8399 --- /dev/null +++ b/src/main/java/calculator/StringSplitter.java @@ -0,0 +1,14 @@ +package calculator; + +public class StringSplitter { + public static String[] splitInput(String input) { + if (!Character.isDigit(input.charAt(0))) { + String delimiter; + delimiter = String.valueOf(input.charAt(2)); + input = input.replaceAll("^.*\\\\n", ""); + String customRegex = "[" + delimiter + ",:]"; + return input.split(customRegex); + } + return input.split("[,:]"); + } +} diff --git a/src/main/java/calculator/SumCalculator.java b/src/main/java/calculator/SumCalculator.java new file mode 100644 index 0000000000..c489161cdb --- /dev/null +++ b/src/main/java/calculator/SumCalculator.java @@ -0,0 +1,13 @@ +package calculator; + +public class SumCalculator { + public static int sum (int[] numbers) { + int total = 0; + + for (int number : numbers) { + total += number; + } + + return total; + } +}