我是 Rust 的新手,正在尝试构建 HTML 解析器。
我首先尝试解析字符串并将其放入 Hashmap<&str, i32>
.
我发现我必须处理信件的情况。
所以我添加了 tag.to_lowercase()
这会创建一个 String
类型。从那里它让我的大脑 panic 。
下面是我的代码片段。
fn html_parser<'a>(html:&'a str, mut tags:HashMap<&'a str, i32>) -> HashMap<&'a str, i32>{
let re = Regex::new("<[:alpha:]+?[\\d]*[:space:]*>+").unwrap();
let mut count;
for caps in re.captures_iter(html) {
if !caps.at(0).is_none(){
let tag = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
count = 1;
if tags.contains_key(tag){
count = *tags.get_mut(tag).unwrap() + 1;
}
tags.insert(tag,count);
}
}
tags
}
抛出这个错误,
src\main.rs:58:27: 58:97 error: borrowed value does not live long enough
src\main.rs:58 let tag:&'a str = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
^~~~~~~~~~~~~~~~~~~
src\main.rs:49:90: 80:2 note: reference must be valid for the lifetime 'a as defined on the block at 49:89...
src\main.rs:49 fn html_parser<'a>(html:&'a str, mut tags:HashMap<&'a str, i32>)-> HashMap<&'a str, i32>{
src\main.rs:58:99: 68:6 note: ...but borrowed value is only valid for the block suffix following statement 0 at 58:98
src\main.rs:58 let tag:&'a str = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
src\main.rs:63
...
error: aborting due to previous error
我读过 Rust 中的生命周期,但仍然无法理解这种情况。
如果谁有好的 HTML 标签正则表达式,请推荐,以便我使用。
最佳答案
要了解您的问题,查看函数签名很有用:
fn html_parser<'a>(html: &'a str, mut tags: HashMap<&'a str, i32>) -> HashMap<&'a str, i32>
从这个签名中我们可以粗略地看出,接受和返回的 HashMap 都可能仅由 html
的子片作为键控。 .但是,在您的代码中,您试图插入一个与 html
完全无关(在生命周期意义上)的字符串切片。 :
let tag = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
这里的第一个问题(你的特定错误正是关于这个问题)是你试图从临时 String
中取出一个切片由 to_lowercase()
返回.这个临时字符串只在这个语句期间有效,所以当语句结束时,字符串被释放,如果编译器不禁止,它的引用将变为悬空。所以,这个作业的正确写法如下:
let tag = caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase();
let tag = &*tag;
(或者你可以直接使用top tag
,用的时候转成slice)
但是,即使经过此更改,您的代码也无法正常工作。 to_lowercase()
方法分配一个新的 String
这与 html
无关在生命周期方面。因此,您从中取出的任何切片的生命周期必然短于 'a
.因此不可能将这样的切片作为键插入到映射中,因为它们指向的数据在该函数返回后可能无效(并且在这种特殊情况下,它将无效)。
很难说解决此问题的最佳方法是什么,因为它可能取决于程序的整体架构,但最简单的方法是创建一个新的 HashMap<String, i32>
函数内部:
fn html_parser(html:&str, tags: HashMap<&str, i32>) -> HashMap<String, i32>{
let mut result: HashMap<String, i32> = tags.iter().map(|(k, v)| (k.to_owned(), *v)).collect();
let re = Regex::new("<[:alpha:]+?[\\d]*[:space:]*>+").unwrap();
for caps in re.captures_iter(html) {
if let Some(cap) = caps.at(0) {
let tag = cap
.trim_matches('<')
.trim_matches('>')
.to_lowercase();
let count = result.get(&tag).unwrap_or(0) + 1;
result.insert(tag, count);
}
}
result
}
我还更改了代码以使其更加惯用( if let
而不是 if something.is_none()
, unwrap_or()
而不是可变局部变量等)。这或多或少是对您的原始代码的直接翻译。
至于用正则表达式解析 HTML,我忍不住提供了一个链接 to this answer .认真考虑使用适当的 HTML 解析器而不是依赖正则表达式。
关于html - 编写 HTML 解析器时借用的值不够长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35656029/