我对Rust很新 - 这段代码是人工学习项目的一部分。记住这一点;)
我有一组元组:&[(i32, String, String, PathBuf)]
,它们被传递到一个用于将数据写入JSON文件的函数中。
问题:当我将PathBuf
转换为&str
时 - 写入文件的路径具有未转义的反斜杠字符,因此JSON无效。
以下是代码:
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::path::PathBuf;
pub fn write_review_queue(ordered_review_queue: &[(i32, String, String, PathBuf)]) -> io::Result<()> {
let output_file = "C:\\Dev\\Temp\\ReviewQueue\\review_queue.json";
let mut buffer = try!(File::create(output_file));
try!(buffer.write("{".to_string().as_bytes()));
let mut is_first_item = true;
for review_item in ordered_review_queue {
if !is_first_item {
try!(buffer.write(",".to_string().as_bytes()));
}
is_first_item = false;
let json_string = "\"ReviewItem\": ".to_string() +
"{\"Index\": " + &review_item.0.to_string() +
", \"ReviewItemName\": \"" + &review_item.1 +
"\", \"ReviewItemPath\": \"" + &review_item.2 +
"\", \"MetadataPath\": \"" + review_item.3.to_str().unwrap() +
"\"}";
try!(buffer.write(json_string.as_bytes()));
}
try!(buffer.write("}".to_string().as_bytes()));
Ok(())
}
输出的一个例子:
{
"ReviewItem": {
"Index": 1,
"ReviewItemName": "Crying Cat",
"ReviewItemPath": "C:/Temp",
"MetadataPath": "C:\Dev\Temp\ReviewQueue\Metadata\cryingcat.json"
},
"ReviewItem": {
"Index": 2,
"ReviewItemName": "Rusty Rat",
"ReviewItemPath": "C:/Temp",
"MetadataPath": "C:\Dev\Temp\ReviewQueue\Metadata\rustyrat.json"
}
}
为PathBuf
生成MetadataPath
s的代码是这样的:
let metadata_files = metadata_read::read_filenames_from_dir("C:\\Dev\\Temp\\ReviewQueue\\Metadata");
if !metadata_files.is_ok() {
println!("reading metadata filenames failed");
return;
}
let mut metadata_counts = Vec::new();
for file in metadata_files.unwrap() {
let metadata_field_count = metadata_read::count_nonempty_metadata_fields(&file, &keys);
metadata_counts.push(metadata_field_count.unwrap());
}
count_nonempty_metadata_fields
函数:
pub fn count_nonempty_metadata_fields(file_path: &PathBuf, metadata_keys: &[String]) -> Result<(i32, String, String, PathBuf), io::Error>
{
// a bunch of code here...
let path = file_path.to_path_buf();
Ok((key_count, review_item_name, review_item_path, path))
}
如果我将原始目录路径字符串更改为:
let metadata_files = metadata_read::read_filenames_from_dir("C:/Dev/Temp/ReviewQueue/Metadata");
确实改变了输出,例如
{
"ReviewItem": {
"Index": 1,
"ReviewItemName": "Crying Cat",
"ReviewItemPath": "C:/Temp",
"MetadataPath": "C:/Dev/Temp/ReviewQueue/Metadata\cryingcat.json"
},
"ReviewItem": {
"Index": 2,
"ReviewItemName": "Rusty Rat",
"ReviewItemPath": "C:/Temp",
"MetadataPath": "C:/Dev/Temp/ReviewQueue/Metadata\rustyrat.json"
}
}
但它仍然不对。
问题
String
的方法,如何将PathBuf
的路径内容转换为格式有正斜杠还是逃逸反斜杠?我错过了API中的内容吗?Json
对象来构建数据(可能更可靠)?如果是这样,将Json
对象的内容写入文件的正常方法是什么?答案 0 :(得分:1)
从不手动生成任何结构化格式是一个好主意,因为最终输出会变得格格不入。此外,您的输出具有一个具有相同两个键的对象。虽然不是无效,但它可能不是你想要的。
在这种情况下,您将很快遇到试图逃避引号和反斜杠以及撇号和&符号的墙。您还必须手动跟踪最后一项。让图书馆努力工作。
Rust有两个很好的JSON库:rustc_serialize和serde。
第一步是为您的数据创建一些实际类型。元组很棒,但你真的记得foo.1
是名字......还是foo.2
?
完成后,您只需输出切片:
extern crate rustc_serialize;
use rustc_serialize::json;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::path::PathBuf;
#[derive(RustcEncodable)]
struct Item {
index: i32,
name: String,
path: String,
metadata_path: PathBuf,
}
fn write_review_queue(ordered_review_queue: &[Item]) -> io::Result<()> {
let mut buffer = try!(File::create("/tmp/output"));
write!(buffer, "{}", json::as_json(&ordered_review_queue))
}
fn main() {
let a = [Item { index: 0, name: "He\"llo".into(), path: "Good\\bye".into(), metadata_path: PathBuf::from(r#"C:\path\with'n\special"\chars"#)}];
write_review_queue(&a).expect("Failed");
}
不幸的是,这会以丑陋的方式打印出PathBuf
:
[{"index":0,"name":"He\"llo","path":"Good\\bye","metadata_path":[67,58,92,112,97,116,104,92,119,105,116,104,39,110,92,115,112,101,99,105,97,108,34,92,99,104,97,114,115]}]
了解PathBuf
不字符串非常重要。具体来说,它们是与平台相关的抽象。在类Unix系统上,路径是接近但不是UTF-8的字节集合,在Windows上它是接近但不是UCS-2。
您必须决定哪种有损变换适合您的情况将其转换为真正的UTF-8。我将使用内置于标准库的to_string_lossy
。我还为类型实现ToJson
以允许更多自定义:
extern crate rustc_serialize;
use rustc_serialize::json::{self, ToJson, Json};
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::path::PathBuf;
use std::collections::BTreeMap;
struct Item {
index: i32,
name: String,
path: String,
metadata_path: PathBuf,
}
impl ToJson for Item {
fn to_json(&self) -> Json {
let mut obj = BTreeMap::new();
obj.insert("Index".to_string(), self.index.to_json());
obj.insert("ReviewItemName".to_string(), self.name.to_json());
obj.insert("ReviewItemPath".to_string(), self.path.to_json());
obj.insert("MetadataPath".to_string(), self.metadata_path.to_string_lossy().to_json());
obj.to_json()
}
}
fn write_review_queue(ordered_review_queue: &[Item]) -> io::Result<()> {
let mut buffer = try!(File::create("/tmp/output"));
write!(buffer, "{}", json::as_json(&ordered_review_queue.to_json()))
}
fn main() {
let a = [Item { index: 0, name: "He\"llo".into(), path: "Good\\bye".into(), metadata_path: PathBuf::from(r#"C:\path\with'n\special"\chars"#)}];
write_review_queue(&a).expect("Failed");
}
请注意,这也让您有机会重命名对象的键(尽管这些名称对我来说似乎很多)。
[{"Index":0,"MetadataPath":"C:\\path\\with'n\\special\"\\chars","ReviewItemName":"He\"llo","ReviewItemPath":"Good\\bye"}]