rust - 如何从已拆分的文件中创建行的迭代器?
问题描述
我有一个文件,我需要逐行阅读并分成两个用“=”分隔的句子。我正在尝试使用迭代器,但我找不到如何在split
. 文档说std::str::Split
实现了这个特征,但我仍然不知道如何使用它。
use std::{
fs::File,
io::{prelude::*, BufReader},
};
fn example(path: &str) {
for line in BufReader::new(File::open(path).expect("Failed at opening file.")).lines() {
let words = line.unwrap().split("="); //need to make this an iterable
}
}
我怎样才能使用我知道已经实现为拆分之类的特征?
解决方案
正如@Mateen 评论的那样,split
已经返回了一个可迭代的。要解决生命周期问题,请unwrap()
在调用之前将返回的值保存到变量中split
。
我将尝试在这里解释生命周期问题。
首先,查看函数签名确实很有帮助。
pub fn unwrap(self) -> T
pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>
unwrap
非常简单,它拥有自己的所有权并返回内部值。
split
看起来很吓人,其实也不算太难,'a
只是一个终身的名字而已,它只是说明了返回值可以使用多长时间。在这种情况下,这意味着两个输入参数必须至少与返回值一样长。
// Takes by reference, no ownership change
// v
pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>
// ^ ^ ^ ^
// | |--|---| |
// This just declares a name. | |
// | |
// Both of these values must last longer than -----|
这是因为split
不复制任何字符串,它只是指向原始字符串上发生拆分的位置。如果原始字符串由于某种原因被删除,Split
则不会指向无效数据。
一个变量的生命周期(除非所有权被传递给其他东西)一直持续到它超出范围,}
如果它被命名(例如 with let
)或者它在行的末尾,这要么在结束时 /;
这就是您的代码中存在终身问题的原因:
for line in std::io::BufReader::new(std::fs::File::open(path).expect("Failed at opening file.")).lines() {
let words = line
.unwrap() // <--- Unwrap consumes `line`, `line` can not be used after calling unwrap(),
.split("=") // Passed unwrap()'s output to split as a reference
; //<-- end of line, unwrap()'s output is dropped due to it not being saved to a variable, the result of split now points to nothing, so the compiler complains.
}
解决方案
保存返回值unwrap()
for line in std::io::BufReader::new(std::fs::File::open("abc").expect("Failed at opening file.")).lines() {
let words = line.unwrap();
let words_split = words.split("=");
} // <--- `word`'s lifetime ends here, but there is no lifetime issues since `words_split` also ends here.
如果需要,您可以重命名words_split
以words
将原始变量隐藏为不混乱的变量名称,这也不会导致问题,因为隐藏的变量不会立即删除,而是在其原始范围的末尾。
或者
与其拥有一个 type 的迭代器str
,所有这些都只是指向原始字符串的精美指针,您可以将每个切片复制到它自己的字符串中,从而消除对将原始字符串保持在范围内的依赖。
在您的情况下几乎可以肯定没有理由这样做,因为复制每个切片需要更多的处理能力和更多的内存,但 rust 可以让您控制。
let words = line
.unwrap()
.split("=")
.map(|piece|
piece.to_owned() // <--- This copies all the characters in the str into it's own String.
).collect::<Vec<String>>()
; // <--- unwrap()'s output dropped here, but it doesn't matter since the pieces no longer points to the original line string.
let words_iterator = words.iter();
collect
给你错误cannot infer type
,因为你没有说明你想收集到什么,要么使用上面的turbofish语法,要么在words
ie上说明let words: Vec<String> = ...
您必须打电话collect
,因为map
除非您使用它,否则不会做任何事情,但这超出了此答案的范围。
推荐阅读
- php - Laravel 无法访问共享存储
- javascript - XMLHttpRequest() POST 返回 405 错误;不允许的方法
- java - 将包含多字节字符的字符串拆分为字符串数组
- python - 如何在 python Telegram bot 对话处理程序中循环
- html - 为什么浏览器上不显示图片?
- css - CSS 网格/Flex 调整大小魔术
- java - 转换为 Dame 工作,但捕获不工作......为什么?
- python - Python 错误:request.exceptions.MissingsSchma 错误。尝试解析多个 url 并使用 requests 模块访问
- javascript - 如何让 rangeslider.js 在移动设备上工作?
- r - rstan MCMC:不同的数据序列导致不同的结果,为什么?