And Brain said,
엔디언(Endian) 행진곡. 4 - 제2장: File 본문
엔디언(Endian) 행진곡
0. 멀티바이트의 질서있는 행진
1. 빅이냐 리틀이냐 그것이 문제로다
2. Rust로 다시 쓰여지다
3. 제1장: Network
4. 제2장: FIle
5. 행진이 끝나고
예제 코드
엔디언은 또한 파일 시스템에서도 중요한데, 바이너리 데이터를 저장하고 읽어올 때는 엔디언을 정확히 알고 있어야 합니다. 엔디언이 다른 시스템에서 파일을 읽거나 쓰려고 할 때 엔디언 변환을 올바르게 하지 않는다면, 데이터가 올바르게 해석되지 않을 수 있습니다
먼저 텍스트 파일안의 숫자 데이터를 읽어서 빅엔디언 형식으로 변환한 후, 다른 파일에 빅엔디언 형식의 바이트를 포함하는 바이너리 파일을 만드는 작업을 수행해봅시다.
use std::fs::File;
use std::io::{self, BufRead, Read, Result, Write};
fn read_from_input() -> Result<()> {
let file = File::open("input.txt")?;
let reader = io::BufReader::new(file);
let mut writer = File::create("output.bin")?;
for line in reader.lines() {
let line = line?;
let num: u16 = line.parse().unwrap();
writer.write_all(&num.to_be_bytes())?;
}
Ok(())
}
fn main() -> Result<()> {
read_from_input()?;
Ok(())
}
최상위 디렉토리의 input.txt 파일안의 숫자데이터를 읽고 이를 바탕으로 ouput.bin 이라는 빅엔디언 형식의 바이너리 파일을 생성합니다.
다음은, 이 만들어진 output.bin 파일을 다시 읽어와서 출력까지 해보도록 하겠습니다.
use std::fs::File;
use std::io::{self, BufRead, Read, Result, Write};
fn read_from_input() -> Result<()> {
let file = File::open("input.txt")?;
let reader = io::BufReader::new(file);
let mut writer = File::create("output.bin")?;
for line in reader.lines() {
let line = line?;
let num: u16 = line.parse().unwrap();
writer.write_all(&num.to_be_bytes())?;
}
Ok(())
}
fn read_from_output() -> Result<()> {
let mut file = File::open("output.bin")?;
let mut buffer = [0; 2];
while let Ok(_) = file.read_exact(&mut buffer) {
let num = u16::from_be_bytes(buffer);
println!("{:?}", num);
}
Ok(())
}
fn main() -> Result<()> {
read_from_input()?;
read_from_output()?;
Ok(())
}
이번에는, Rust의 파일 I/O API와 byteorder 크레이트를 사용하여 정수와 부동소수점 값을 little-endian 형식으로 파일에 쓰고 읽는 예제를 작성해보겠습니다.
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fs::File;
use std::io::{BufWriter, Result};
#[derive(Debug)]
struct MyStruct {
a: i32,
b: f64,
c: u16,
}
impl MyStruct {
fn write_to_file(&self, writer: &mut BufWriter<File>) -> Result<()> {
writer.write_i32::<LittleEndian>(self.a)?;
writer.write_f64::<LittleEndian>(self.b)?;
writer.write_u16::<LittleEndian>(self.c)?;
Ok(())
}
fn read_from_file(reader: &mut File) -> Result<Self> {
let a = reader.read_i32::<LittleEndian>()?;
let b = reader.read_f64::<LittleEndian>()?;
let c = reader.read_u16::<LittleEndian>()?;
Ok(MyStruct { a, b, c })
}
}
fn main() -> Result<()> {
let data = vec![
MyStruct {
a: 42,
b: 3.14159,
c: 255,
},
MyStruct {
a: 53,
b: 2.71828,
c: 512,
},
MyStruct {
a: 64,
b: 1.41421,
c: 1024,
},
];
{
let file = File::create("data.bin")?;
let mut writer = BufWriter::new(file);
for struct_instance in &data {
struct_instance.write_to_file(&mut writer)?;
}
}
{
let file = File::open("data.bin")?;
let mut reader = file;
for _ in 0..data.len() {
let loaded_data = MyStruct::read_from_file(&mut reader)?;
println!("{:?}", loaded_data);
}
}
Ok(())
}
위 예제는 여러 개의 구조체를 한 파일에 저장하고, 이를 다시 읽어도록 하였습니다.
BufWriter를 사용하여 파일에 데이터를 효율적으로 기록하며, 한 개의 파일에 여러개의 MyStruct 인스턴스를 기록할 수 있습니다. 모든 데이터를 기록한 후 파일을 닫도록 BufWriter 를 스코프 밖으로 빼내 자동으로 BufWriter가 소멸됩니다. 그리고, 모든 데이터를 읽어온 후에는 마찬가지로 파일을 닫도록 File을 스코프 밖으로 뺍니다. 이렇게 하면 File이 자동적으로 소멸되면서 파일이 닫히게 됩니다.
행진이 끝나고
이렇게 멀티바이트 데이터들의 행진이 끝났습니다. 우리는 엔디언이 무엇인지, 어디서 사용되는지 알아보았습니다. 엔디언의 중요성을 강조했지만 사실은, 실제로 여러분들이 앞으로 엔디언 변환을 직접하는 경우는 거의 없을 것입니다. 왜냐면 대부분의 경우에 우리는 네트워킹과 파일 시스템을 직접 작업하지 않을테니까요. 단지 이런게 있다 정도만 아시고 넘어가셔도 충분합니다.
다음은 아마 CI/CD Argonautica 시리즈 작업을 진행할 것입니다. 그럼 이만. 언제나,
Thanks for watching, Have a nice day.
<- 이전으로
'IT > 엔디언 행진곡' 카테고리의 다른 글
엔디언(Endian) 행진곡. 3 - 제1장: Network (0) | 2023.07.05 |
---|---|
엔디언(Endian) 행진곡. 2 - Rust로 다시 쓰여지다 (0) | 2023.06.28 |
엔디언(Endian) 행진곡. 1 - 빅이냐 리틀이냐 그것이 문제로다 (0) | 2023.06.18 |
엔디언(Endian) 행진곡. 0 - 멀티바이트의 질서있는 행진 (0) | 2023.06.18 |