#read-write #shapefile #typed #dbase #dbf #field-value #memo

typed_shapefile

在Rust中读取和写入 .dbf

3个不稳定版本

0.4.1 2023年4月24日
0.4.0 2023年4月21日
0.3.0 2022年11月24日

1252算法

MIT 协议

320KB
7K SLoC

dbase-rs

Rust库,用于读取和写入 .dbf(dBase / FoxPro)文件。

大多数dBase III和FoxPro类型都可以读取和写入,但Memo类型只能读取(写入将在后续版本中提供)。

如果dbase-rs在读取或写入时失败或执行了不正确的操作,请毫不犹豫地提交问题。


lib.rs:

typed_shapefile是一个旨在读取和写入dBase / FoxPro文件的Rust库。

这些文件现在通常只与shapefile相关联。

读取

Reader 结构是您需要使用以读取dBase文件内容的。

一旦您访问到记录,您将需要对实际的 FieldValue 进行 match

示例

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),
            _ => {}
        }
    }
}

您还可以创建一个 Reader 并遍历记录。

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);
    }
}

反序列化

如果您知道特定文件中的数据类型,可以使用实现 ReadableRecord 特性以“反序列化”记录到您的自定义结构体中。

 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(())
 # }

如果您使用 serde 可选功能和 serde_derive crate,则 ReadableRecord 将为您自动实现。

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");

其他代码页/编码

通过crate yore 提供对Unicode以外的编码的支持。此crate仅支持一些基本的单字节代码页,但其中许多在旧系统中使用,您可能想要打开的dBase数据库可能会使用这些代码页。

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()))));


不接收编码作为参数的函数默认使用 UnicodeLossy,它们尝试将所有数据作为Unicode读取,并用Unicode替换字符替换不可表示的字符。另可选 Unicode,当数据无法表示为Unicode时返回 [Err]

写入

为了获取一个 TableWriter,您需要使用其 TableWriterBuilder 来构建它,并指定构成记录的字段。

至于读取,如果您实现了 WritableRecord,则可以将结构体序列化到 dBase 文件中,前提是它们与构建 TableWriterBuilder 时声明的字段相匹配。

示例

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();

如果您使用 serde 可选功能和 serde_derive crate,则可以为您实现 WritableRecord

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);

依赖项

~5.5MB
~134K SLoC