tmontaigu / dbase-rs

Rust library to read & write dBase files.
MIT License
29 stars 30 forks source link

Is it possible to open an existing .dbf file for writing? #28

Closed aj3n closed 1 year ago

aj3n commented 2 years ago

I've checked the api docs, it seems the only way to write into a .dbf file is using TableWriter, which can only be built from TableWriterBuilder. but I didn't find a way to use TableWriterBuilder to open an existing .dbf file for writing.

Is there a way to open an existing .dbf file for writing? Do remind me if i missed something in the docs , thanks.

aj3n commented 2 years ago

I found the way to open an existing .dbf for writing by exploring the code and docs, my code looks like this:

fn get_writer<P: AsRef<std::path::Path>>(path: P) -> TableWriter<BufWriter<File>> {
  let reader = dbase::Reader::from_path(&path).unwrap();
  let tb = TableWriterBuilder::from_reader(reader);
  let mut fd = OpenOptions::new().write(true).open(path).unwrap();
  fd.seek(SeekFrom::End(0)).unwrap();
  tb.build_with_dest(BufWriter::new(fd))
}

But something went wrong when I use the TableWriter created in this way. I found it's because every time close the writer, it writes a terminating 0x1a to the end of file, so if I append to the end, the offset of single row is wrong. My temporary workaround for this situation is changing from:

fd.seek(SeekFrom::End(0)).unwrap();

to

fd.seek(SeekFrom::End(-1)).unwrap();

But I found my original .dbf file (not created/written by this crate) didn't have the terminating 0x1a. Is there a better solution for my situation?

tmontaigu commented 2 years ago

Opening a dbf in a sort of read+write mode to modify exisiting records and/or append new records is not something that is supported right. Tho, with how the file format is, it could be, the feature needs to be added.

They way to do things currently is to read all of the records in memory, modify/add records then fully rewrite the file.

let reader = dbase::Reader::from_path(&path).unwrap();
let records = reader.read()?;
let builder = TableWriterBuilder::from_reader(reader);

modify_and_add_records(&mut records);

let mut writer = builder.build_with_file_dest(&path).unwrap();
writer.write_records(&records).unwrap();
aj3n commented 2 years ago

Opening a dbf in a sort of read+write mode to modify exisiting records and/or append new records is not something that is supported right. Tho, with how the file format is, it could be, the feature needs to be added.

They way to do things currently is to read all of the records in memory, modify/add records then fully rewrite the file.

let reader = dbase::Reader::from_path(&path).unwrap();
let records = reader.read()?;
let builder = TableWriterBuilder::from_reader(reader);

modify_and_add_records(&mut records);

let mut writer = builder.build_with_file_dest(&path).unwrap();
writer.write_records(&records).unwrap();

Thanks for replying, I temporarily fix this problem by manually calculating the next writable offset when opening the file. Looking forward to a better solution in the future