728x90
java.nio.file 패키지를 사용한 모던 자바 파일 입출력(I/O) 방법과 사용자 입력을 처리하는 간단한 메모장을 만들어 보겠습니다.
언제 사용하면 좋을까요?
1. 설정 파일 또는 데이터 초안 생성: 사용자가 입력한 특정 키 값(첫 줄)을 파일 이름으로 하고 나머지 내용을 값으로 하는 설정 파일
(.txt, .properties 등)의 초안을 만드는 데 활용할 수 있습니다.
2. 사용자 생성 콘텐츠 저장: 사용자가 게시글 제목과 내용을 입력하면, 제목을 기반으로 파일 이름을 만들어 저장하는 간단한 시스템의 로직을 이해하는 데 도움이 됩니다.
java.nio.file 장점
1. 직관적인 API: Path 객체로 파일/디렉토리 경로를 명확하게 표현하고, Files 유틸리티 클래스로 다양한 파일 관련 작업을 쉽게 수행할 수 있습니다.
2. 개선된 예외 처리: IOException의 구체적인 하위 클래스들을 제공하여 오류 상황을 더 정확하게 파악하고 처리할 수 있습니다.
3. try-with-resources 지원: Files.newBufferedReader, Files.newBufferedWriter 등 NIO.2 API는 AutoCloseable을 구현하므로, try-with-resources 구문을 사용해 리소스를 자동으로 안전하게 닫을 수 있습니다.
코드
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.IntStream;
public class Main {
// 내용이 없거나 첫 줄이 비었을 때 사용할 기본 파일 이름 (확장자 제외)
private static final String DEFAULT_BASENAME = "untitled";
// 파일 확장자
private static final String FILE_EXTENSION = ".txt";
// 입력 종료 명령어 정의
private static final String SAVE_COMMAND = "저장";
public static void main(String[] args) {
Path filePath = null; // 파일 경로는 내용 입력 후 결정되므로 null로 초기화
String finalFilename = ""; // 최종 파일 이름 저장 변수
try (Scanner scanner = new Scanner(System.in)) {
// --- 1 단계: 사용자로부터 초기 내용 입력받기 ---
System.out.println("파일에 저장할 초기 내용을 입력하세요.");
System.out.println("첫 번째 줄 내용이 파일 이름의 기반이 됩니다.");
System.out.println("다 입력했으면 새 줄에 '" + SAVE_COMMAND + "' 라고 입력하세요.");
List<String> initialContent = getContentFromUser(scanner);
// --- 2 & 3 & 4 단계: 파일 이름 결정 ---
String baseName;
if (initialContent.isEmpty() || initialContent.get(0).trim().isEmpty()) {
// 내용이 없거나 첫 줄이 비어있으면 기본 이름 사용
baseName = DEFAULT_BASENAME;
System.out.println("[알림] 첫 줄이 비어있어 기본 파일 이름 '" + baseName + FILE_EXTENSION + "'을(를) 사용합니다.");
} else {
// 첫 줄 내용을 파일 이름으로 사용하기 위해 가공
baseName = sanitizeFilename(initialContent.get(0));
}
finalFilename = baseName + FILE_EXTENSION;
filePath = Paths.get(finalFilename); // 최종 Path 객체 생성
// --- 5 단계: 파일 생성 및 초기 내용 쓰기 ---
if (writeFileContent(filePath, initialContent)) {
System.out.println("\n[성공] 파일 '" + finalFilename + "'이(가) 생성되고 초기 내용이 저장되었습니다.");
System.out.println("파일 위치: " + filePath.toAbsolutePath());
// --- 생성된 파일 내용 확인 ---
System.out.println("\n--- 생성된 '" + finalFilename + "' 파일 내용 확인 ---");
readFileAndDisplay(filePath);
System.out.println("\n 생성된 파일을 수정하시겠습니까?");
System.out.println("\n 1.예 / 2.아니오");
int str = scanner.nextInt();
if (str == 1) {
// 생성된 파일을 수정 할 때
// --- 6 단계: 사용자로부터 수정된 내용 입력받기 ---
System.out.println("\n--- 파일 '" + finalFilename + "' 내용 수정 ---");
System.out.println("아래 내용을 참고하여 수정된 전체 내용을 다시 입력하세요.");
System.out.println("마찬가지로 다 입력했으면 새 줄에 '" + SAVE_COMMAND + "' 라고 입력하세요.");
List<String> modifiedContent = getContentFromUser(scanner);
// --- 7 단계: 수정된 내용으로 파일 덮어쓰기 (동일한 파일 이름 사용) ---
if (writeFileContent(filePath, modifiedContent)) {
System.out.println("\n[성공] 파일 '" + finalFilename + "'이(가) 수정된 내용으로 덮어쓰기 되었습니다.");
// --- 8 단계: 최종 수정된 파일 내용 확인 ---
System.out.println("\n--- 최종 수정된 '" + finalFilename + "' 파일 내용 확인 ---");
readFileAndDisplay(filePath);
} else {
System.out.println("\n[실패] 파일 수정 내용 저장에 실패했습니다.");
}
} else {
// 생성된 파일을 수정 하지 않을 때
System.out.println("\n 수정을 하지 않습니다.");
}
} else {
System.out.println("\n[실패] 초기 파일 생성에 실패했습니다.");
}
} // Scanner는 여기서 자동으로 닫힙니다.
System.out.println("\n프로그램을 종료합니다.");
}
/**
* 사용자로부터 여러 줄의 입력을 받아 리스트로 반환합니다.
* 사용자가 SAVE_COMMAND를 입력하면 입력이 종료됩니다.
*
* @param scanner 사용자 입력을 받을 Scanner 객체
* @return 사용자가 입력한 문자열 라인들의 리스트
*/
private static List<String> getContentFromUser(Scanner scanner) {
List<String> lines = new ArrayList<>();
while (true) {
String line = scanner.nextLine();
if (line.equalsIgnoreCase(SAVE_COMMAND)) {
break;
}
lines.add(line);
}
return lines;
}
/**
* 주어진 경로의 파일을 읽어 각 줄 앞에 번호를 붙여 콘솔에 출력합니다.
*
* @param filePath 읽을 파일의 경로
*/
private static void readFileAndDisplay(Path filePath) {
// 파일이 실제로 존재하는지 먼저 확인하는 것이 안전합니다.
if (!Files.exists(filePath)) {
System.out.println("[오류] 파일 '" + filePath.getFileName() + "'을(를) 찾을 수 없습니다.");
return;
}
try {
List<String> lines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
if (lines.isEmpty()) {
System.out.println("(파일 내용 없음)");
} else {
IntStream.range(0, lines.size())
.forEach(i -> System.out.println((i + 1) + ": " + lines.get(i)));
}
} catch (IOException e) {
System.err.println("[오류] 파일 '" + filePath.getFileName() + "'을(를) 읽는 중 오류 발생: " + e.getMessage());
}
}
/**
* 주어진 내용을 파일에 씁니다. 파일이 이미 존재하면 덮어씁니다.
*
* @param filePath 쓸 파일의 경로
* @param content 파일에 쓸 문자열 라인들의 리스트
* @return 쓰기 성공 여부
*/
private static boolean writeFileContent(Path filePath, List<String> content) {
try {
// 쓰기 전에 상위 디렉토리가 없으면 생성해주는 것이 더 안전할 수 있습니다. (선택 사항)
// Path parentDir = filePath.getParent();
// if (parentDir != null && !Files.exists(parentDir)) {
// Files.createDirectories(parentDir);
// }
Files.write(filePath, content, StandardCharsets.UTF_8);
return true;
} catch (IOException e) {
System.err.println("[오류] 파일 '" + filePath.getFileName() + "'에 쓰는 중 오류 발생: " + e.getMessage());
return false;
} catch (Exception e) { // 예상치 못한 다른 예외 처리
System.err.println("[오류] 파일 '" + filePath.getFileName() + "' 처리 중 예기치 않은 오류 발생: " + e.getMessage());
return false;
}
}
/**
* 문자열을 파일 이름으로 사용하기 안전하게 만듭니다.
* - 앞뒤 공백 제거
* - 파일 시스템에서 허용하지 않는 문자 제거 (예: \ / : * ? " < > |)
* - 공백 문자를 밑줄(_)로 변경
* - 너무 길면 잘라내기 (선택 사항, 여기서는 50자로 제한)
*
* @param input 원본 문자열 (주로 파일의 첫 줄)
* @return 파일 이름으로 사용 가능한 문자열
*/
private static String sanitizeFilename(String input) {
// 1. 앞뒤 공백 제거
String sanitized = input.trim();
// 2. 파일 이름 금지 문자 제거 (정규 표현식 사용)
sanitized = sanitized.replaceAll("[\\\\/:*?\"<>|]", ""); // 금지 문자 제거
// 3. 공백을 밑줄로 변경
sanitized = sanitized.replaceAll("\\s+", "_"); // 하나 이상의 공백을 밑줄 하나로
// 4. 너무 길면 자르기 (예: 최대 50자)
if (sanitized.length() > 50) {
sanitized = sanitized.substring(0, 50);
}
// 5. 혹시 모든 문자가 제거되어 비어있다면 기본 이름 반환
if (sanitized.isEmpty()) {
return DEFAULT_BASENAME;
}
return sanitized;
}
}
시연 영상
'Java' 카테고리의 다른 글
[JAVA] 사용자 정의 예외 만들기 (0) | 2025.04.24 |
---|---|
[Java] HttpClient 사용해서 OpenAI GPT API 호출하기 (0) | 2025.04.23 |
[Java] 옵저버 패턴 활용 예시 구현하기 (0) | 2025.04.19 |
[JAVA] 예외 처리(Exception Handling) (0) | 2025.04.17 |
[Java] Apache POI로 Excel 파일을 .txt로 변환하기 (0) | 2025.04.16 |