FileWriter w = new FileWriter("out.txt");
for (int i = 0; i < 1_000_000; i++) w.write("line " + i + "\n"); // 매번 커널 호출에 가까움
w.close();100만 번의 write 가 각각 인코딩과 (거의) 시스템 콜을 유발합니다. 벤치마크에서 버퍼 유무는 5~10배 차이가 납니다.
✅ Files.newBufferedWriter 또는 new BufferedWriter(new FileWriter(...)).
try (BufferedWriter w = Files.newBufferedWriter(Path.of("out.txt"), StandardCharsets.UTF_8)) {
for (int i = 0; i < 1_000_000; i++) { w.write("line " + i); w.newLine(); }
}new FileReader("members.csv"); // OS 기본 인코딩
Files.readAllLines(path); // JDK 17 이하: OS 기본, 18+: UTF-8개발 PC(Windows, MS949) 에서 잘 되던 코드가 Linux 서버(UTF-8) 에서 한글을 깨뜨립니다. 반대 경우도 마찬가지입니다. 배포 후 발견되면 이미 깨진 파일이 외부로 나간 뒤입니다.
✅ 항상 Charset 을 명시합니다. 파일 형식 문서에 인코딩이 없으면 만든 쪽에 확인합니다.
Files.newBufferedReader(path, StandardCharsets.UTF_8);
Files.newBufferedReader(path, Charset.forName("MS949")); // Excel 저장 CSVlong n = Files.lines(path).filter(l -> l.contains("ERROR")).count(); // 스트림이 닫히지 않음Files.lines 는 파일을 열어 두고 스트림을 돌려줍니다. 닫지 않으면 파일 핸들이 GC 될 때까지 열려 있고, 배치가 수천 개 파일을 처리하면 "Too many open files" 가 납니다. Windows 에서는 열린 파일을 삭제·이동할 수 없어 다음 단계가 실패합니다.
✅ try-with-resources 로 감쌉니다.
long n;
try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
n = lines.filter(l -> l.contains("ERROR")).count();
}String[] f = line.split(",");
long amount = Long.parseLong(f[4]); // memo 에 쉼표가 있으면 f[4] 가 밀림 → NumberFormatException 또는 엉뚱한 값더 나쁜 것은 예외 없이 엉뚱한 값으로 파싱되는 경우입니다. 금액 컬럼이 옆 컬럼 값으로 바뀌어도 숫자면 통과합니다.
✅ 따옴표를 이해하는 파서를 씁니다(예제 2), 또는 검증된 라이브러리. 그리고 필드 수를 검증합니다.
List<String> f = CsvParser.parseLine(line);
if (f.size() != 6) throw new IllegalArgumentException("필드 수 " + f.size() + ": " + line);try (BufferedWriter w = Files.newBufferedWriter(Path.of("settlement.csv"))) {
... // 10분 소요, 5분에 OOM
}
// 반쪽짜리 settlement.csv 가 남고 새벽 4시 전송 배치가 그것을 가져감✅ .tmp 에 쓰고 완료 후 ATOMIC_MOVE. 소비자 쪽에서는 .tmp 를 무시하도록 약속합니다.
Path tmp = dest.resolveSibling(dest.getFileName() + ".tmp");
// ... tmp 에 쓰기 ...
Files.move(tmp, dest, StandardCopyOption.ATOMIC_MOVE);String name = line.substring(20, 30); // 레이아웃: 이름 10바이트MS949 에서 한글은 2바이트입니다. "홍길동" 은 6바이트인데 substring(20, 30) 은 10문자를 자르므로 뒤 필드가 섞여 들어옵니다.
✅ 바이트 배열로 읽어 바이트 오프셋으로 자릅니다.
byte[] rec = line.getBytes(Charset.forName("MS949"));
String name = new String(Arrays.copyOfRange(rec, 20, 30), Charset.forName("MS949")).trim();