Skip to content
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# java-calculator-precourse
# java-calculator-precourse

1. 쉼표 혹은 콜론을 구분자로 가지는 문자열을 구분자 기준으로 분리한 각 숫자의 합을 반환
2. 커스텀 구분자 지정
3. 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시키기
- 음수를 입력했을 때
- 숫자가 아닌값을 입력했을 때
4. 빈 문자열이나 null을 입력 받았을 때
5. 숫자 하나를 입력 받았을 때 그대로 반환한다
24 changes: 22 additions & 2 deletions src/main/java/calculator/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
package calculator;
import camp.nextstep.edu.missionutils.Console;

import java.util.Scanner;
import static java.lang.Integer.parseInt;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
// 사용자에게 입력을 요청합니다.
System.out.println("덧셈할 문자열을 입력해 주세요.");

String input;
try {
input = Console.readLine();
} catch (Exception e) {
// 빈 입력이나 예외 발생 시 빈 문자열로 처리
input = "";
}

// 계산기 인스턴스 생성하고 덧셈을 수행합니다.
StringCalculator calculator = new StringCalculator();
int result = calculator.add(input);

// 결과를 출력합니다.
System.out.println("결과 : " + result);
}
}
}
51 changes: 51 additions & 0 deletions src/main/java/calculator/StringCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package calculator;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringCalculator {

private static final Pattern CUSTOM_DELIMITER_PATTERN = Pattern.compile("//(.)\\\\n(.*)");

public int add(final String text) {
// 1. 입력값이 null 이거나 비어있으면 0을 반환합니다.
if(text == null || text.trim().isEmpty()){
return 0;
}

// Matcher를 사용하여 커스텀 구분자 패턴과 일치하는지 확인합니다.
Matcher matcher = CUSTOM_DELIMITER_PATTERN.matcher(text);
if (matcher.find()) {
String customDelimiter = matcher.group(1); // 커스텀 구분자
String numbersPart = matcher.group(2); // 숫자 구분
return splitAndSum(numbersPart, Pattern.quote(customDelimiter));
}
// 커스텀 구분자가 없으면 기본 구분자를 사용하여 계산
return splitAndSum(text, "[,:]");
}

private int splitAndSum(final String text, final String delimiter) {
// 구분자를 기준으로 문자열을 분리
String[] numbers = text.split(delimiter);

// 분리된 각 문자열을 숫자로 변환하고 합계를 구함
return Arrays.stream(numbers)
.mapToInt(this::parseAndValidate)
.sum();
}

private int parseAndValidate(final String numberStr) {
try {
int number = Integer.parseInt(numberStr.trim());
// 음수인지 확인하여 예외를 발생시킵니다.
if(number < 0){
throw new IllegalArgumentException("음수는 입력할 수 없습니다.");
}
return number;
} catch (NumberFormatException e) {
// 숫자로 변환할 수 없는 경우 예외를 발생시킵니다.
throw new IllegalArgumentException("숫자가 아닌 값이 포함되어 있습니다.");
}
}
}
70 changes: 64 additions & 6 deletions src/test/java/calculator/ApplicationTest.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,84 @@
package calculator;

import camp.nextstep.edu.missionutils.test.NsTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class ApplicationTest extends NsTest {

@Test
@DisplayName("기본 구분자(쉼표)를 사용한 덧셈")
void 기본_구분자_쉼표() {
assertSimpleTest(() -> {
run("1,2,3");
assertThat(output()).contains("결과 : 6");
});
}

@Test
@DisplayName("기본 구분자(콜론)를 사용한 덧셈")
void 기본_구분자_콜론() {
assertSimpleTest(() -> {
run("1:2:3");
assertThat(output()).contains("결과 : 6");
});
}

@Test
@DisplayName("기본 구분자(쉼표와 콜론 혼용)를 사용한 덧셈")
void 기본_구분자_혼용() {
assertSimpleTest(() -> {
run("1,2:3");
assertThat(output()).contains("결과 : 6");
});
}

@Test
void 커스텀_구분자_사용() {
@DisplayName("커스텀 구분자를 사용한 덧셈")
void 커스텀_구분자_덧셈() {
assertSimpleTest(() -> {
run("//;\\n1");
assertThat(output()).contains("결과 : 1");
run("//;\n1;2;3");
assertThat(output()).contains("결과 : 6");
});
}

@Test
void 예외_테스트() {
@DisplayName("단일 숫자 입력 시 해당 숫자 반환")
void 단일_숫자() {
assertSimpleTest(() -> {
run("5");
assertThat(output()).contains("결과 : 5");
});
}

@Test
@DisplayName("빈 문자열 또는 null 입력 시 0 반환")
void 빈_문자열_또는_null() {
assertSimpleTest(() -> {
run("");
assertThat(output()).contains("결과 : 0");
});
}

@Test
@DisplayName("음수 입력 시 IllegalArgumentException 발생")
void 음수_예외() {
assertSimpleTest(() ->
assertThatThrownBy(() -> runException("-1,2,3"))
.isInstanceOf(IllegalArgumentException.class)
);
}

@Test
@DisplayName("숫자가 아닌 문자 입력 시 IllegalArgumentException 발생")
void 숫자가_아닌_문자_예외() {
assertSimpleTest(() ->
assertThatThrownBy(() -> runException("-1,2,3"))
.isInstanceOf(IllegalArgumentException.class)
assertThatThrownBy(() -> runException("1,a,2"))
.isInstanceOf(IllegalArgumentException.class)
);
}

Expand Down