← Java 강의 목록으로
📂
예외 · 입출력
예외 · 입출력 · 선수: 이전 단원

17. 문자열 처리

문자열(`String`) 은 어떤 프로그램에서도 빠지지 않는 자료형입니다. Java 의 `String` 은 **불변(immutable)** 이며, 변경이 잦은 경우엔 `StringBuilder` 를 함께 사용합니다. 검색·치환에는 정규식(`Pattern`/`Matcher`) 이 강력한 도구입니다.

Java예외I/O문자열 처리
소요 시간
약 1~1.5시간
난이도
📊 중급
선수 조건
🎯 이전 단원 또는 동등 지식
결과물
문자열(`String`) 은 어떤 프로그램에서도 빠지지 않는 자료형입니다. Java 의 `String` 은 **불변(immutable)** 이며, 변경이 잦은 경우엔 `StringBuilder` 를 함께 사용합니다. 검색·치환에는 정규식(`Pattern`/`Matcher`) 이 강력한 도구입니다.

이 강의에서 배우는 것

  • 1`String` 의 주요 메서드 (`length`, `substring`, `indexOf`, `split`, `trim` 등) 를 안다
  • 2`String` 이 불변이라는 의미와 결합 시 비용을 안다
  • 3`StringBuilder` 로 반복 결합을 최적화한다
  • 4`String.format` 으로 보기 좋은 문자열을 만든다
  • 5정규식 기본 사용법을 본다 (`Pattern.compile`, `matcher.find`)

소개

문자열(`String`) 은 어떤 프로그램에서도 빠지지 않는 자료형입니다. Java 의 `String` 은 **불변(immutable)** 이며, 변경이 잦은 경우엔 `StringBuilder` 를 함께 사용합니다. 검색·치환에는 정규식(`Pattern`/`Matcher`) 이 강력한 도구입니다.

핵심 개념

1) 불변성

java
String a = "Hello";
String b = a.replace("e", "a");
System.out.println(a);   // Hello  (a 는 안 바뀜)
System.out.println(b);   // Hallo

`String` 메서드는 **새 문자열** 을 반환하지 원본을 바꾸지 않습니다.

2) 주요 메서드

java
"Java".length();              // 4
"Java".substring(1, 3);        // av
"Hello, World".indexOf(",");   // 5
"  hi ".trim();                 // "hi"
"a,b,c".split(",");             // ["a","b","c"]
String.join("-", "a","b","c"); // "a-b-c"

3) `StringBuilder`

java
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) sb.append(i).append(",");
System.out.println(sb.toString());   // 0,1,2,3,4,

루프 안에서의 문자열 결합은 `StringBuilder` 가 훨씬 빠릅니다.

4) `String.format` / text block

java
String row = String.format("이름=%s, 점수=%d", "지수", 95);

String json = """
    {
        "name": "지수",
        "score": 95
    }
    """;

5) 정규식

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

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("주문번호: 1234, 가격: 5678");
while (m.find()) {
    System.out.println(m.group());
}

핵심 예제

예제 1 — `StringMethods.java` : 주요 메서드

java
public class StringMethods {
    public static void main(String[] args) {
        String s = " Hello, Java World ";
        System.out.println("length=" + s.length());
        System.out.println("trim=" + s.trim() + "|");
        System.out.println("upper=" + s.toUpperCase());
        System.out.println("startsWith Hello? " + s.trim().startsWith("Hello"));
        System.out.println("sub=" + s.trim().substring(7, 11));
        System.out.println("rep=" + s.trim().replace("Java", "Spring"));
        for (String w : s.trim().split(",\\s*")) {
            System.out.println("token=" + w);
        }
    }
}

**실행 결과**

text
length=19
trim=Hello, Java World|
upper= HELLO, JAVA WORLD 
startsWith Hello? true
sub=Java
rep=Hello, Spring World
token=Hello
token=Java World

**메모:** `substring(begin, end)` 의 `end` 는 **exclusive** 입니다.

예제 2 — `Immutability.java` : 불변성과 비용

java
public class Immutability {
    public static void main(String[] args) {
        String s = "X";
        long start = System.nanoTime();
        for (int i = 0; i < 1000; i++) s += "Y";
        long t1 = System.nanoTime() - start;
        System.out.println("String += 길이=" + s.length() + ", 시간=" + t1 + "ns");

        StringBuilder sb = new StringBuilder("X");
        long start2 = System.nanoTime();
        for (int i = 0; i < 1000; i++) sb.append("Y");
        long t2 = System.nanoTime() - start2;
        System.out.println("StringBuilder 길이=" + sb.length() + ", 시간=" + t2 + "ns");
    }
}

**실행 결과 (시간 값은 실행마다 다름)**

text
String += 길이=1001, 시간=...ns
StringBuilder 길이=1001, 시간=...ns

**메모:** 반복 결합은 거의 항상 `StringBuilder` 가 훨씬 빠릅니다.

예제 3 — `FormatAndTextBlock.java` : 포맷팅

java
public class FormatAndTextBlock {
    public static void main(String[] args) {
        String row = String.format("이름=%s, 점수=%d, 평균=%.2f", "지수", 95, 82.345);
        System.out.println(row);

        String json = """
            {
                "name": "지수",
                "score": 95
            }
            """;
        System.out.println(json);
    }
}

**실행 결과**

text
이름=지수, 점수=95, 평균=82.35
{
    "name": "지수",
    "score": 95
}

**메모:** Text block 의 들여쓰기는 가장 짧은 줄의 공백 길이가 기준입니다.

예제 4 — `RegexDemo.java` : 정규식

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

public class RegexDemo {
    public static void main(String[] args) {
        String text = "주문번호: 1234, 가격: 5678원, 수량: 3개";
        Pattern p = Pattern.compile("\\d+");
        Matcher m = p.matcher(text);
        while (m.find()) {
            System.out.println("매치=" + m.group());
        }

        Pattern email = Pattern.compile("[\\w.]+@[\\w.]+");
        System.out.println(email.matcher("hi jisu@example.com bye").find());
    }
}

**실행 결과**

text
매치=1234
매치=5678
매치=3
true

**메모:** 자바 문자열 안의 `\d` 는 `\\d` 로 두 번 백슬래시 해야 합니다.

전체 예제 코드 (src/)

src/FormatAndTextBlock.java

java
public class FormatAndTextBlock {
    public static void main(String[] args) {
        String row = String.format("이름=%s, 점수=%d, 평균=%.2f", "지수", 95, 82.345);
        System.out.println(row);

        String json = """
            {
                "name": "지수",
                "score": 95
            }
            """;
        System.out.println(json);
    }
}

src/Immutability.java

java
public class Immutability {
    public static void main(String[] args) {
        String s = "X";
        long start = System.nanoTime();
        for (int i = 0; i < 1000; i++) s += "Y";
        long t1 = System.nanoTime() - start;
        System.out.println("String += 길이=" + s.length() + ", 시간=" + t1 + "ns");

        StringBuilder sb = new StringBuilder("X");
        long start2 = System.nanoTime();
        for (int i = 0; i < 1000; i++) sb.append("Y");
        long t2 = System.nanoTime() - start2;
        System.out.println("StringBuilder 길이=" + sb.length() + ", 시간=" + t2 + "ns");
    }
}

src/RegexDemo.java

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

public class RegexDemo {
    public static void main(String[] args) {
        String text = "주문번호: 1234, 가격: 5678원, 수량: 3개";
        Pattern p = Pattern.compile("\\d+");
        Matcher m = p.matcher(text);
        while (m.find()) {
            System.out.println("매치=" + m.group());
        }

        Pattern email = Pattern.compile("[\\w.]+@[\\w.]+");
        System.out.println(email.matcher("hi jisu@example.com bye").find());
    }
}

src/StringMethods.java

java
public class StringMethods {
    public static void main(String[] args) {
        String s = " Hello, Java World ";
        System.out.println("length=" + s.length());
        System.out.println("trim=" + s.trim() + "|");
        System.out.println("upper=" + s.toUpperCase());
        System.out.println("startsWith Hello? " + s.trim().startsWith("Hello"));
        System.out.println("sub=" + s.trim().substring(7, 11));
        System.out.println("rep=" + s.trim().replace("Java", "Spring"));
        for (String w : s.trim().split(",\\s*")) {
            System.out.println("token=" + w);
        }
    }
}

자주 하는 실수

  1. `String` 비교에 `==` 사용 → `.equals()` 또는 `.equalsIgnoreCase()`
  2. 루프 안에서 `+` 결합 (성능) → `StringBuilder`
  3. `substring(begin, end)` 의 `end` 가 inclusive 라고 착각
  4. 정규식 백슬래시 한 개로 작성 (Java 문자열은 `\\` 필요)
  5. `format` 에 `%n` 대신 `\n` 만 사용 (대부분 동작하지만 OS 호환은 `%n`)

정리

  • `String` 은 불변, 반복 결합은 `StringBuilder`
  • `String.format` 과 text block 으로 가독성을 챙기자
  • 정규식은 강력하지만 가독성·디버깅 어려움 — 필요할 때만
  • 빈 문자열·null 체크는 항상 의식

과제

# 과제 - 17. 문자열 처리

## 문제 1 — 회문 판별

  • 파일명: `Homework01.java`
  • 핵심 개념: `String` 기본 메서드

요구사항

  • `static boolean isPalindrome(String s)` — 공백 무시, 대소문자 무시.
  • 시험 입력: `"level"`, `"Hello"`, `"A man a plan a canal Panama"`

예상 출력

text
level -> true
Hello -> false
A man a plan a canal Panama -> true

## 문제 2 — 이메일 추출

  • 파일명: `Homework02.java`
  • 핵심 개념: 정규식

요구사항

  • `"문의: a@x.com 또는 b@y.co.kr 까지"` 에서 이메일을 모두 추출해 출력.

예상 출력

text
a@x.com
b@y.co.kr

## 정답 확인 직접 풀어 본 후 [`answer/`](./answer/) 폴더의 정답과 비교해 보세요.

정답 코드 (homework/answer/)

answer/Homework01.java

java
/** 회문 판별. */
public class Homework01 {
    public static void main(String[] args) {
        String[] cases = { "level", "Hello", "A man a plan a canal Panama" };
        for (String s : cases) {
            System.out.println(s + " -> " + isPalindrome(s));
        }
    }

    static boolean isPalindrome(String s) {
        String t = s.replace(" ", "").toLowerCase();
        return t.equals(new StringBuilder(t).reverse().toString());
    }
}

answer/Homework02.java

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

/** 이메일 추출. */
public class Homework02 {
    public static void main(String[] args) {
        String text = "문의: a@x.com 또는 b@y.co.kr 까지";
        Pattern p = Pattern.compile("[\\w.]+@[\\w.]+");
        Matcher m = p.matcher(text);
        while (m.find()) {
            System.out.println(m.group());
        }
    }
}

직접 해 보기

bash
cd 04_예외_입출력/17_문자열_처리/src
javac StringMethods.java
java StringMethods

다음 단원

[18_람다와_함수형](../../05_모던_자바/18_람다와_함수형/) — 람다·메서드 참조·함수형 인터페이스를 본격적으로 다룹니다.

예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗