実現したいこと
- Rustのdbaseクレートで国土地理院(例として、消防署データ)でダウンロードできるzipファイルの中にあるdbfファイルを読み込むと、文字化けしてしまうので、それを文字化けせずに読み込めるようにしたいです。
発生している問題・分からないこと
- 国土地理院でダウンロードできるdbfファイルの文字コードはCP932になります。
- dbaseクレート内のReaderで読み込む際の関数として、エンコーディングを指定する関数が
ありますが、第二引数がdbaseクレート内のEncodingトレイトになっており、
がそのまま使えそうです。
CP932はShift-JISの拡張のようなものと調べており、CP932もしくは、Shift-JISでEncodingトレイトを拡張出来ないかなと思い、試しましたが、実装すべき関数が下記となっており、
rust
1 fn decode<'a>(&self, bytes: &'a [u8]) -> Result<Cow<'a, str>, DecodeError>; 2 3 fn encode<'a>(&self, s: &'a str) -> Result<Cow<'a, [u8]>, EncodeError>;
DecodeError
がprivate moduleとなっており、私の力では拡張する方法が分からないです。
Encodingトレイトのコメント文に、featureフラグyoreをオンにすると、すべてのyore::CodePage(このリンク先だと認識)が使えるようになる。
と解釈しておりますが、CP932が存在しません。
該当のソースコード
Rust
1use std::collections::BTreeMap; 2use shapefile::dbase::{FieldValue, Reader, Encoding, encoding::AsCodePageMark, CodePageMark}; 3use shapefile::dbase::error::{DecodeError, EncodeError}; // ここがエラーになります。 4use encoding_rs::SHIFT_JIS; 5use std::fmt; 6use std::borrow::Cow; 7 8fn main() { 9 let mut dbf_reader = Reader::from_path_with_encoding("resources/P17-12_13_FireStation.dbf", CP932Encoding) 10 .expect("Failed to open DBF file"); 11 12 for (i, record_result) in dbf_reader.iter_records().enumerate() { 13 println!("Record {}", i); 14 let record = record_result.expect("Failed to read record"); 15 let mut records_map: BTreeMap<String, Vec<String>> = BTreeMap::new(); 16 17 for (name, value) in record { 18 let v: String = match value { 19 FieldValue::Character(Some(chara)) => { 20 chara.to_string() 21 }, 22 FieldValue::Character(None) => String::new(), 23 FieldValue::Numeric(s) => s.map_or(String::new(), |num| num.to_string()), 24 _ => String::new(), 25 }; 26 records_map.entry(name.to_string()) 27 .or_insert_with(Vec::new) 28 .push(v); 29 } 30 println!("\tRecords {:?}: {:?}", i, records_map); 31 } 32} 33 34#[derive(Debug)] 35pub enum MyError { 36 DecodeError(String), 37 EncodeError(String), 38 IoError(std::io::Error), 39} 40 41impl fmt::Display for MyError { 42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 43 match self { 44 MyError::DecodeError(msg) => write!(f, "Decode Error: {}", msg), 45 MyError::EncodeError(msg) => write!(f, "Encode Error: {}", msg), 46 MyError::IoError(err) => write!(f, "IO Error: {}", err), 47 } 48 } 49} 50 51impl From<std::io::Error> for MyError { 52 fn from(err: std::io::Error) -> MyError { 53 MyError::IoError(err) 54 } 55} 56 57// CP932 (Windows-31J) 用のカスタムエンコーディング構造体 58#[derive(Copy, Clone, Debug)] 59pub struct CP932Encoding; 60 61impl AsCodePageMark for CP932Encoding{ 62 fn code_page_mark(&self) -> CodePageMark { 63 CodePageMark::CP932 64 } 65} 66 67impl Encoding for CP932Encoding { 68 fn decode<'a>(&self, bytes: &'a [u8]) -> Result<Cow<'a, str>, DecodeError> { 69 let (decoded, _, had_errors) = SHIFT_JIS.decode(bytes); 70 if had_errors { 71 Err("CP932 decoding error".to_string()) 72 } else { 73 Ok(Cow::Owned(decoded.into_owned())) 74 } 75 } 76 77 fn encode<'a>(&self, s: &'a str) -> Result<Cow<'a, [u8]>, EncodeError> { 78 let (encoded, _, had_errors) = SHIFT_JIS.encode(s); 79 if had_errors { 80 Err("CP932 encoding error".to_string()) 81 } else { 82 Ok(Cow::Owned(encoded.into_owned())) 83 } 84 } 85}
toml
1[package] 2name = "dbf_sample" 3version = "0.1.0" 4edition = "2021" 5 6[dependencies] 7shapefile = "0.6.0" 8encoding_rs = "0.8" 9encoding = "0.2" 10dbase = { version = "0.5", features = ["yore"] } 11encoding_rs_io = "0.1.0"
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
- teratailで「Rust dbf CP932」や「Rust dbase CP932」と検索しましたが、同様の質問が見つけられませんでした。
補足
前述のtomlに記載済みです。(漏れていたら、すみません)

あなたの回答
tips
プレビュー