실습: 파일을 끝까지 읽고 바이트 세기
Buffer, 파일 I/O, 실패 처리와 메모리 해제를 연결합니다.
Wave Foundation
준비
작업 디렉터리에 input.txt를 만들고 Wave 뒤에 LF 줄바꿈 하나를 저장합니다. 이때 파일은 5바이트입니다. CRLF로 저장하면 6바이트, UTF-8 BOM을 붙이면 추가 바이트가 있으므로 편집기의 저장 형식을 확인하십시오.
프로그램을 main.wave에 저장하고 같은 디렉터리에서 wavec run main.wave로 실행합니다. 상대 경로는 실행하는 작업 디렉터리를 기준으로 합니다.
import("std::buffer::alloc")::{
Buffer, buffer_init, buffer_free
};
import("std::fs::file")::{
read_to_end
};
fun main() -> i32 {
var data: Buffer;
if (buffer_init(&data, 0) < 0) {
return 1;
}
var count: i64 = read_to_end("input.txt", &data);
if (count < 0) {
println("read failed={}", count);
buffer_free(&data);
return 2;
}
var lines: i64 = 0;
for (var i: i64 = 0; i < data.len; i += 1) {
if (deref data.data[i] == 10) {
lines += 1;
}
}
println("bytes={} LF={}", count, lines);
if (buffer_free(&data) < 0) {
return 3;
}
return 0;
}
실행 결과:
bytes=5 LF=1
동작과 책임
read_to_end가 파일을 열고 닫지만 Buffer 해제는 호출자의 책임입니다. 성공과 읽기 실패 경로 모두에서 해제합니다. 바이트 수를 가진 데이터이므로 NUL 종료 문자열이라고 가정하지 않습니다.
LF 개수와 사람이 생각하는 줄 수는 항상 같지 않습니다. 마지막 줄에 LF가 없으면 이 프로그램의 LF 개수에는 포함되지 않습니다. 파일 이름을 바꾸어 읽기 실패도 확인하십시오. 구체적인 오류 숫자는 환경에 따라 다를 수 있습니다.
확장 연습과 해설
매우 큰 파일을 처리하려면 고정 크기 배열과 io_read 반복으로 바꾸십시오. 양수 반환 범위만 처리하고 0에서 종료합니다. 전체 파일을 메모리에 유지하지 않아도 바이트 수와 LF 수를 누적할 수 있습니다. 직접 열었다면 모든 종료 경로에서 디스크립터도 닫습니다.
처리 흐름을 따라가기
- 빈 Buffer를 초기화합니다. 아직 파일 내용은 없습니다.
- read_to_end가 파일을 읽으며 필요한 공간을 늘립니다.
- 읽기에 성공하면 data.len 범위의 바이트를 검사합니다.
- LF의 바이트 값 10을 만날 때마다 lines를 늘립니다.
- 결과를 출력하고 Buffer를 해제합니다.
data.cap은 확보한 저장 공간이고 data.len은 유효한 데이터 길이입니다. 반복 조건을 cap으로 바꾸면 파일에 없던 바이트까지 읽게 되므로 len을 사용합니다. count는 이번 read_to_end 호출에서 추가한 바이트 수입니다. 이 예제는 빈 Buffer에서 시작하므로 count와 data.len이 같습니다.
입력을 바꿔 확인하기
| input.txt 내용 | bytes | LF | 이유 |
|---|---|---|---|
| 빈 파일 | 0 | 0 | 읽을 바이트가 없음 |
Wave |
4 | 0 | 마지막 줄바꿈이 없음 |
Wave + LF |
5 | 1 | 마지막 LF까지 데이터 |
A + LF + B + LF |
4 | 2 | 두 LF를 셈 |
Wave + CRLF |
6 | 1 | CR도 1바이트지만 LF만 셈 |
마지막 줄에 LF가 없는 파일도 한 줄로 세려면, 파일이 비어 있지 않고 마지막 바이트가 10이 아닐 때 줄 수에 1을 더합니다. data.len이 0인지 먼저 확인해야 마지막 원소에 접근할 수 있습니다.
큰 파일로 확장하기
전체 내용을 보관하는 현재 방식은 나중에 데이터를 다시 읽거나 검색할 때 편리합니다. 바이트 수와 LF 수만 필요하다면 고정 크기 버퍼를 재사용하는 방식이 적합합니다.
고정 크기 버퍼로 파일 읽기 예제의 io_read 반복문 안에서, 반환된 바이트 수만큼 LF를 세면 됩니다. 파일 전체 길이가 아니라 버퍼 크기만큼의 메모리로 처리할 수 있습니다. 읽기가 0을 반환하면 끝이고 음수면 오류입니다.