Java에서 문자열을 분할하는 가장 기본적이면서도 강력한 메서드인 split()
함수는 정규식을 활용해 다양한 패턴으로 문자열을 나눌 수 있습니다.
이 글에서는 Java split 함수의 모든 기능을 상세히 다루어, 실무에서 마주할 수 있는 모든 상황에 대응할 수 있도록 도와드리겠습니다.
split() 함수 기본 구조와 원리
Java String split는 두 가지 오버로딩된 메서드를 제공합니다.
public String[] split(String regex)
public String[] split(String regex, int limit)
첫 번째 메서드는 정규식 패턴만 사용하며, 두 번째 메서드는 limit 매개변수를 추가로 받아 분할 결과의 개수를 제어합니다.
기본 사용 예제
String text = "apple,banana,orange";
String[] fruits = text.split(",");
// 결과: ["apple", "banana", "orange"]
System.out.println(Arrays.toString(fruits));
이처럼 간단한 구분자를 사용한 Java 문자열 자르기는 매우 직관적입니다.
하지만 실제 개발에서는 더 복잡한 상황들이 발생하죠.
regex 구분자 활용법과 특수문자 처리
Java String split regex를 사용할 때 가장 주의해야 할 점은 정규식의 특수문자들입니다.
정규식 특수문자와 이스케이프 처리
다음 문자들은 정규식에서 특별한 의미를 갖습니다. ^ $ * + ? { } [ ] \ | ( )
// 잘못된 예시 - 점(.)은 정규식에서 "모든 문자"를 의미
String ip = "192.168.1.1";
String[] parts = ip.split("."); // 빈 배열 반환!
// 올바른 예시 - 이스케이프 처리
String[] correctParts = ip.split("\\.");
// 결과: ["192", "168", "1", "1"]
여러 구분자를 한번에 처리
여러 구분자를 동시에 사용하려면 정규식의 OR 연산자 |
를 활용합니다:
String mixed = "apple,banana;orange:grape";
String[] fruits = mixed.split("[,;:]");
// 또는 String[] fruits = mixed.split(",|;|:");
System.out.println(Arrays.toString(fruits));
// 결과: ["apple", "banana", "orange", "grape"]
공백 문자 처리
공백 관련 분할은 자주 사용되는 패턴입니다:
String text = " apple banana orange ";
// 하나 이상의 공백으로 분할
String[] words = text.trim().split("\\s+");
// 결과: ["apple", "banana", "orange"]
// 탭, 공백, 개행문자 모두 처리
String multiSpace = "apple\tbanana\ngrape orange";
String[] allWords = multiSpace.split("\\s+");
정규식 구분자 split에 대한 더 자세한 정보는 Oracle Java Pattern 클래스 문서에서 확인할 수 있습니다.
limit 매개변수 완전 분석
split(String regex, int limit)
메서드의 limit 매개변수는 결과 배열의 크기를 제어하는 핵심 요소입니다.
limit 값에 따른 동작 차이
limit 값 | 동작 방식 | 후행 빈 문자열 |
---|---|---|
양수 (>0) | 최대 limit개만큼 분할 | 보존됨 |
0 | 제한 없이 분할 | 제거됨 |
음수 (<0) | 제한 없이 분할 | 보존됨 |
limit >0인 경우 - 배열 길이 제한
String data = "a,b,c,d,e";
// limit = 3: 최대 3개 요소로 제한
String[] limited = data.split(",", 3);
// 결과: ["a", "b", "c,d,e"] - 나머지는 마지막 요소에 포함
System.out.println("Length: " + limited.length); // 3
이는 배열 길이 제한이 필요한 상황에서 매우 유용합니다.
limit = 0인 경우 - 후행 빈 문자열 제거
String trailing = "a,b,c,,,";
String[] withLimit0 = trailing.split(",", 0);
// 결과: ["a", "b", "c"] - 후행 빈 문자열 제거
String[] noLimit = trailing.split(",");
// 결과: ["a", "b", "c"] - 기본값이 limit=0
limit 0 처리는 후행 공백을 자동으로 정리해주는 편리한 기능입니다.
limit 음수인 경우 - 모든 빈 문자열 보존
String trailing = "a,b,c,,,";
String[] withNegative = trailing.split(",", -1);
// 결과: ["a", "b", "c", "", "", ""] - 모든 빈 문자열 보존
System.out.println("Length: " + withNegative.length); // 6
limit 음수 의미는 원본 데이터의 구조를 완전히 보존하고 싶을 때 사용합니다.
빈 문자열 처리 전략
실무에서는 빈 문자열 제외 split 처리가 자주 필요합니다.
연속된 구분자 처리
String messy = "apple,,banana,,,orange";
// 기본 split: 빈 문자열 포함
String[] withEmpty = messy.split(",");
// 결과: ["apple", "", "banana", "", "", "orange"]
// 빈 문자열 제거 방법 1: 정규식 활용
String[] cleaned1 = messy.split(",+"); // 1개 이상의 쉼표
// 결과: ["apple", "banana", "orange"]
// 빈 문자열 제거 방법 2: Stream API 활용
String[] cleaned2 = Arrays.stream(messy.split(","))
.filter(s -> !s.isEmpty())
.toArray(String[]::new);
CSV 파일 처리 예제
CSV 파일 처리에서 후행 빈 문자열 제거는 매우 중요합니다
public class CsvProcessor {
public static String[] processCsvLine(String line) {
// limit = -1로 모든 필드 보존
String[] fields = line.split(",", -1);
// 각 필드의 따옴표 제거 및 트림
for (int i = 0; i < fields.length; i++) {
fields[i] = fields[i].trim().replaceAll("^\"|\"$", "");
}
return fields;
}
public static void main(String[] args) {
String csvLine = "\"John\",\"Doe\",\"30\",\"Developer\",";
String[] processed = processCsvLine(csvLine);
System.out.println(Arrays.toString(processed));
// 결과: ["John", "Doe", "30", "Developer", ""]
}
}
실전 예제와 성능 최적화
로그 파일 파싱
실제 로그 파일을 파싱하는 문자열 분할 실전 예제입니다
public class LogParser {
private static final Pattern LOG_PATTERN =
Pattern.compile("\\s*\\|\\s*"); // 파이프(|) 구분자, 공백 포함
public static LogEntry parseLogLine(String logLine) {
try {
// 정규식 패턴 재사용으로 성능 최적화
String[] parts = LOG_PATTERN.split(logLine, 5); // 최대 5개 필드
if (parts.length < 4) {
throw new IllegalArgumentException("Invalid log format");
}
return new LogEntry(
parts[0], // timestamp
parts[1], // level
parts[2], // component
parts[3], // message
parts.length > 4 ? parts[4] : "" // additional info
);
} catch (PatternSyntaxException e) {
System.err.println("Regex pattern error: " + e.getMessage());
return null;
}
}
}
class LogEntry {
private String timestamp, level, component, message, additionalInfo;
public LogEntry(String timestamp, String level, String component,
String message, String additionalInfo) {
this.timestamp = timestamp;
this.level = level;
this.component = component;
this.message = message;
this.additionalInfo = additionalInfo;
}
// getters...
}
URL 파라미터 파싱
웹 개발에서 자주 사용되는 URL 파라미터 파싱
public class UrlParameterParser {
public static Map<String, String> parseParameters(String queryString) {
Map<String, String> params = new HashMap<>();
if (queryString == null || queryString.isEmpty()) {
return params;
}
// '&'로 파라미터 분리
String[] pairs = queryString.split("&");
for (String pair : pairs) {
// '='로 키-값 분리 (limit=2로 값에 '='가 있어도 안전)
String[] keyValue = pair.split("=", 2);
if (keyValue.length == 2) {
try {
String key = URLDecoder.decode(keyValue[0], "UTF-8");
String value = URLDecoder.decode(keyValue[1], "UTF-8");
params.put(key, value);
} catch (UnsupportedEncodingException e) {
// UTF-8은 항상 지원되므로 실제로는 발생하지 않음
}
}
}
return params;
}
public static void main(String[] args) {
String query = "name=John&age=30&city=Seoul&description=Hello%20World";
Map<String, String> params = parseParameters(query);
params.forEach((k, v) -> System.out.println(k + " = " + v));
}
}
split vs StringTokenizer 성능 비교
split vs StringTokenizer 중 어떤 것을 선택해야 할까요?
성능 테스트 결과
항목 | split() | StringTokenizer |
---|---|---|
단순 구분자 | 느림 (정규식 오버헤드) | 빠름 |
복잡한 패턴 | 빠름 (정규식 활용) | 불가능 |
메모리 사용량 | 높음 (배열 생성) | 낮음 (Iterator 방식) |
편의성 | 높음 | 낮음 |
사용 권장사항
// 단순한 구분자, 대용량 데이터: StringTokenizer
public List<String> tokenizeSimple(String data, String delimiter) {
List<String> result = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(data, delimiter);
while (tokenizer.hasMoreTokens()) {
result.add(tokenizer.nextToken().trim());
}
return result;
}
// 복잡한 패턴, 소규모 데이터: split()
public String[] splitComplex(String data) {
return data.split("\\s*[,;:]\\s*"); // 구분자 앞뒤 공백 제거
}
더 자세한 성능 비교는 Java Performance Tuning 가이드에서 확인할 수 있습니다.
고급 활용 기법과 주의사항
Pattern 클래스 활용한 최적화
반복적으로 같은 정규식을 사용한다면 Pattern
클래스를 미리 컴파일해두는 것이 효율적입니다
public class OptimizedSplitter {
// 정규식 패턴 미리 컴파일 (성능 최적화)
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
public static String[] splitByComma(String text) {
return COMMA_PATTERN.split(text);
}
public static String[] splitByWhitespace(String text) {
return WHITESPACE_PATTERN.split(text.trim());
}
}
PatternSyntaxException 처리
PatternSyntaxException은 잘못된 정규식 패턴을 사용할 때 발생합니다
public class SafeSplitter {
public static String[] safeSplit(String text, String regex) {
try {
return text.split(regex);
} catch (PatternSyntaxException e) {
System.err.println("Invalid regex pattern: " + regex);
System.err.println("Error: " + e.getDescription());
// 기본 구분자로 폴백
return text.split(Pattern.quote(regex)); // 리터럴 문자로 처리
}
}
public static void main(String[] args) {
String text = "a.b.c";
// 잘못된 정규식 (점은 특수문자)
String[] wrong = safeSplit(text, ".");
// 올바른 처리
String[] correct = safeSplit(text, "\\.");
System.out.println("Wrong: " + Arrays.toString(wrong));
System.out.println("Correct: " + Arrays.toString(correct));
}
}
대용량 데이터 처리
대용량 파일을 처리할 때는 메모리 효율을 고려해야 합니다:
public class LargeDataProcessor {
public static void processLargeFile(String filename) {
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) {
reader.lines()
.map(line -> line.split(",", -1)) // CSV 라인 분할
.filter(fields -> fields.length >= 3) // 최소 필드 수 검증
.forEach(fields -> {
// 각 라인을 스트림으로 처리 (메모리 효율적)
processFields(fields);
});
} catch (IOException e) {
System.err.println("File processing error: " + e.getMessage());
}
}
private static void processFields(String[] fields) {
// 필드 처리 로직
System.out.println("Processing: " + Arrays.toString(fields));
}
}
마무리
Java split 함수는 단순해 보이지만 실제로는 매우 강력하고 복잡한 기능을 제공합니다.
정규식 구분자를 활용하면 복잡한 패턴의 문자열도 효과적으로 분할할 수 있고, limit 매개변수를 통해 결과를 정밀하게 제어할 수 있습니다.
특히 빈 문자열 처리와 특수문자 이스케이프 처리는 실무에서 자주 마주치는 중요한 포인트입니다.
이 글에서 다룬 split 함수 예제들을 참고하여, 여러분의 프로젝트에서도 효율적인 문자열 처리를 구현해보시기 바랍니다.
더 자세한 Java 문자열 처리 방법은 Oracle Java String 클래스 문서에서 확인할 수 있습니다.
같이 보면 좋은 글
Comparable vs Comparator 한방에 끝내기 - 자바 정렬·비교 원리와 실무 패턴 총정리
자바에서 Comparable과 Comparator 인터페이스는 객체 정렬의 핵심으로, 자연 정렬과 커스텀 정렬 로직을 구현하여 Collections.sort()와 Arrays.sort() 메서드를 통해 효율적인 데이터 정렬을 가능하게 하는
notavoid.tistory.com
Scanner 클래스 한방 정리 - next() vs nextLine() 차이, 입력패턴, 실수 방지 팁
Java Scanner 클래스는 System.in을 통한 사용자 입력 처리에 필수적인 도구로, next()와 nextLine()의 차이점을 정확히 이해하고 공백 구분자 처리 방법을 숙지하면 입력 관련 오류를 크게 줄일 수 있습니
notavoid.tistory.com
웹 개발의 핵심: AJP와 HTTP를 활용한 WEB-WAS 연동 전략(feat. 아파치, 톰캣)
AJP와 HTTP 프로토콜을 활용한 Apache-Tomcat 연동으로 50% 이상의 성능 향상과 운영 비용 절감을 달성하는 실무 가이드를 제공합니다.현대 웹 애플리케이션 아키텍처에서 Web Server와 WAS 간의 효율적인
notavoid.tistory.com
JVM , 아파치, 아파치 톰캣 튜닝
추석 시즌 200% 트래픽 증가로 겪은 실제 장애 경험을 바탕으로, 서버 성능을 즉시 개선할 수 있는 실무 중심의 JVM 및 웹서버 튜닝 전략을 제시합니다. 추석 복지몰 운영 중 사용자가 급증하면서
notavoid.tistory.com
Java 멀티스레딩 성능 최적화 완벽 가이드 (2025): 동시성 제어부터 실무 트러블슈팅까지
Java 멀티스레딩 환경에서 발생하는 동시성 문제를 해결하고 성능을 최적화하는 실무 중심의 완전 가이드로,실제 운영 환경 사례와 구체적인 해결책을 제시합니다.현대 애플리케이션에서 멀티
notavoid.tistory.com
Java 21부터 달라진 주요 기능 요약: 실무 개발자가 알아야 할 핵심 변화점
Java 21은 2023년 9월에 출시된 차세대 LTS(Long Term Support) 버전으로, 자바 개발자들에게 혁신적인 기능들을 제공합니다.이전 LTS 버전인 Java 17과 비교했을 때 상당한 성능 향상과 개발 편의성 개선이
notavoid.tistory.com
'자바(Java) 실무와 이론' 카테고리의 다른 글
Comparable vs Comparator 한방에 끝내기 - 자바 정렬·비교 원리와 실무 패턴 총정리 (0) | 2025.09.17 |
---|---|
Scanner 클래스 한방 정리 - next() vs nextLine() 차이, 입력패턴, 실수 방지 팁 (0) | 2025.09.17 |
Java 리액티브 프로그래밍 실무 완전 가이드: 네이버 쇼핑 10배 성능 개선 사례로 배우는 Project Reactor (0) | 2025.05.28 |
Java로 메모리 캐시 직접 구현해보기: 성능 최적화를 위한 실무 가이드 (0) | 2025.05.28 |
Java 패턴 매칭 기능 완벽 가이드: 모던 자바 개발자를 위한 실무 활용법 (0) | 2025.05.28 |