3 unstable releases
0.4.1 | Apr 24, 2023 |
---|---|
0.4.0 | Apr 21, 2023 |
0.3.0 | Nov 24, 2022 |
#1371 in Algorithms
320KB
7K
SLoC
dbase-rs
Rust library to read and write .dbf (dBase / FoxPro) files.
Most of the dBase III and FoxPro types can be read and written, with the exception of the Memo which can only be read (writing will come in a later release).
If dbase-rs fails to read or write or does something incorrectly, don't hesitate to open an issue.
lib.rs
:
typed_shapefile is rust library meant to read and write dBase / FoxPro files.
Theses files are nowadays generally only found in association with shapefiles.
Reading
The Reader is the struct that you'll need to use in order to read the content of a dBase file.
Once you have access to the records, you will have to match
against the real
FieldValue
Examples
use typed_shapefile::FieldValue;
let records = typed_shapefile::read("tests/data/line.dbf")?;
for record in records {
for (name, value) in record {
println!("{} -> {:?}", name, value);
match value {
FieldValue::Character(Some(string)) => println!("Got string: {}", string),
FieldValue::Numeric(value) => println!("Got numeric value of {:?}", value),
_ => {}
}
}
}
You can also create a Reader and iterate over the records.
let mut reader = typed_shapefile::Reader::from_path("tests/data/line.dbf")?;
for record_result in reader.iter_records() {
let record = record_result?;
for (name, value) in record {
println!("name: {}, value: {:?}", name, value);
}
}
Deserialisation
If you know what kind of data to expect from a particular file you can use implement the ReadbableRecord trait to "deserialize" the record into your custom struct:
use std::io::{Read, Seek};
use typed_shapefile::Encoding;
struct StationRecord {
name: String,
marker_col: String,
marker_sym: String,
line: String,
}
impl typed_shapefile::ReadableRecord for StationRecord {
fn read_using<T>(field_iterator: &mut typed_shapefile::FieldIterator<T>) -> Result<Self, typed_shapefile::FieldIOError>
where T: Read + Seek
{
use typed_shapefile::Encoding;Ok(Self {
name: field_iterator.read_next_field_as()?.value,
marker_col: field_iterator.read_next_field_as()?.value,
marker_sym: field_iterator.read_next_field_as()?.value,
line: field_iterator.read_next_field_as()?.value,
})
}
}
# fn main() -> Result<(), typed_shapefile::Error> {
let mut reader = typed_shapefile::Reader::from_path("tests/data/stations.dbf")?;
let stations = reader.read_as::<StationRecord>()?;
assert_eq!(stations[0].name, "Van Dorn Street");
assert_eq!(stations[0].marker_col, "#0000ff");
assert_eq!(stations[0].marker_sym, "rail-metro");
assert_eq!(stations[0].line, "blue");
# Ok(())
# }
If you use the serde
optional feature and serde_derive crate you can have the
ReadbableRecord impletemented for you
extern crate serde_derive;
use std::io::{Read, Seek};
use serde_derive::Deserialize;
#[derive(Deserialize)]
struct StationRecord {
name: String,
marker_col: String,
marker_sym: String,
line: String,
}
let mut reader = typed_shapefile::Reader::from_path("tests/data/stations.dbf")?;
let stations = reader.read_as::<StationRecord>()?;
assert_eq!(stations[0].name, "Van Dorn Street");
assert_eq!(stations[0].marker_col, "#0000ff");
assert_eq!(stations[0].marker_sym, "rail-metro");
assert_eq!(stations[0].line, "blue");
Other Codepages / Encodings
Support for encodings other than Unicode is provided via the crate yore
.
This crate only supports some basic single-byte codepages, but many of those
were used by older systems, which dBase databases you may want to open might use.
use yore::code_pages::CP850;
let mut reader = typed_shapefile::Reader::from_path_with_encoding("tests/data/cp850.dbf", CP850)?;
let records = reader.read()?;
assert_eq!(records[0].get("TEXT"), Some(&typed_shapefile::FieldValue::Character(Some("Äöü!§$%&/".to_string()))));
The functions that do not take an encoding as parameter, use UnicodeLossy
by default,
they try to read all data as Unicode and replace unrepresentable characters with the unicode
replacement character. Alternatively Unicode
is available, to return an [Err
] when data
can't be represented as Unicode.
Writing
In order to get a TableWriter you will need to build it using its TableWriterBuilder to specify the fields that constitute a record.
As for reading, you can serialize structs into a dBase file, given that they match the declared fields in when building the TableWriterBuilder by implementing the WritableRecord.
Examples
let mut reader = typed_shapefile::Reader::from_path("tests/data/stations.dbf")?;
let mut stations = reader.read()?;
let mut writer = typed_shapefile::TableWriterBuilder::from_reader(reader)
.build_with_file_dest("stations.dbf").unwrap();
stations[0].get_mut("line").and_then(|_old| Some("Red".to_string()));
writer.write_records(&stations)?;
use typed_shapefile::{TableWriterBuilder, FieldName, WritableRecord, FieldWriter, FieldIOError, Encoding};
use std::convert::TryFrom;
use std::io::{Cursor, Write};
struct User {
nick_name: String,
age: f64
}
impl WritableRecord for User {
fn write_using<'a, W>(&self, field_writer: &mut FieldWriter<'a, W>) -> Result<(), FieldIOError>
where W: Write
{
field_writer.write_next_field_value(&self.nick_name)?;
field_writer.write_next_field_value(&self.age)?;
Ok(())
}
}
let mut writer = TableWriterBuilder::new()
.add_character_field(FieldName::try_from("Nick Name").unwrap(), 50)
.add_numeric_field(FieldName::try_from("Age").unwrap(), 20, 10)
.build_with_dest(Cursor::new(Vec::<u8>::new()));
let records = User{
nick_name: "Yoshi".to_string(),
age: 32.0,
};
writer.write_record(&records).unwrap();
If you use the serde optional feature and serde_derive crate you can have the WritableRecord implemented for you.
extern crate serde_derive;
use serde_derive::Serialize;
use typed_shapefile::{TableWriterBuilder, FieldName, WritableRecord, FieldWriter};
use std::convert::TryFrom;
use std::io::{Cursor, Write};
#[derive(Serialize)]
struct User {
nick_name: String,
age: f64
}
let writer = TableWriterBuilder::new()
.add_character_field(FieldName::try_from("Nick Name").unwrap(), 50)
.add_numeric_field(FieldName::try_from("Age").unwrap(), 20, 10)
.build_with_dest(Cursor::new(Vec::<u8>::new()));
let records = vec![User{
nick_name: "Yoshi".to_string(),
age: 32.0,
}];
writer.write_records(&records);
Dependencies
~5.5MB
~128K SLoC