我经常对日志文件执行相同的分析。最初,我有一个用于 grep 和 sort 的小型 Awk 脚本。为了好玩,我把它改写成 Python:
#!/usr/bin/python3
import sys
months = { "Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4, "May": 5, "Jun": 6,
"Jul": 7, "Aug": 8, "Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12 }
months_r = { v:k for k,v in months.items() }
totals = {}
for line in sys.stdin:
if "redis" in line and "Partial" in line:
f1, f2 = line.split()[:2]
w = (months[f1], int(f2))
totals[w] = totals.get(w, 0) + 1
for k in sorted(totals.keys()):
print(months_r[k[0]], k[1], totals[k])
然后转到 Go(为了避免太长,这里不再引用 Go 版本(68 行))。
我现在试图用 Rust 来表达它(到目前为止我只写了一些玩具示例)并且我很卡。一开始,我有很多错误,而且越来越好,但现在我有一个我无法修复......
有人可以帮忙说明如何在 Rust 中表达这一点吗? Python 版本很短,所以最好有一些非常地道的东西,而不是太冗长。
这是我目前为止的位置,但我无法取得任何进一步的进展。
use std::array::IntoIter;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;
use std::iter::FromIterator;
fn main() {
let m = HashMap::<_, _>::from_iter(IntoIter::new([
("Jan", 1),
("Feb", 2),
("Mar", 3),
("Apr", 4),
("May", 5),
("Jun", 6),
("Jul", 7),
("Aug", 8),
("Sep", 9),
("Oct", 10),
("Nov", 11),
("Dec", 12),
]));
let mut totals = HashMap::new();
for l in io::stdin().lock().lines() {
let ul = l.unwrap();
if ul.contains("redis") && ul.contains("Partial") {
let mut wi = ul.split_whitespace();
let f1 = wi.next().unwrap();
let f2 = wi.next().unwrap();
let count = totals.entry((m.get(&f1).unwrap(), f2)).or_insert(0);
*count += 1;
}
}
}
简单的提示将不胜感激,我不是要一个完整的工作解决方案,这是更多的工作(但如果有的话,我当然会欢迎)。
非常感谢!
答案 0 :(得分:1)
问题是您的 f2
指的是当前行字符串 ul
拥有的数据。但是,在循环的每次迭代中都会删除 ul
,并由 lines()
迭代器分配一个新的。如果您将引用 ul
的切片插入 totals
哈希图中,该切片将在循环的下一次迭代中失效,并且当您稍后尝试访问已释放的内存时,程序将崩溃或出现故障数据。
split_whitespace()
的行为是为了效率:在调用它时,您通常只需要检查字符串,返回所有内容的新分配副本将是一种浪费。取而代之的是,ul.split_whitespace()
提供廉价的切片,这些切片实际上是 ul
的视图,允许您选择是否复制返回的字符串切片。
解决方案很简单,只需使用 to_string()
从返回的切片创建一个拥有的字符串。例如,这会编译:
fn main() {
let m = HashMap::<_, _>::from_iter(IntoIter::new([
("Jan", 1),
// ...
]));
let mut totals = HashMap::new();
for l in io::stdin().lock().lines() {
let ul = l.unwrap();
if ul.contains("redis") && ul.contains("Partial") {
let mut wi = ul.split_whitespace();
let f1 = wi.next().unwrap();
let f2 = wi.next().unwrap().to_string();
*totals.entry((m.get(f1).unwrap(), f2)).or_insert(0) += 1;
}
}
}
一个更简单的选择是做你的 Python 代码所做而 Rust 翻译没有做的事情,即将 f2
解析为整数。整数可以按值复制到映射中,您不再需要分配 f2
的副本:
for l in io::stdin().lock().lines() {
let ul = l.unwrap();
if ul.contains("redis") && ul.contains("Partial") {
let mut wi = ul.split_whitespace();
let f1 = wi.next().unwrap();
let f2 = wi.next().unwrap();
let w = (m.get(f1).unwrap(), f2.parse::<u32>().unwrap());
*totals.entry(w).or_insert(0) += 1;
}
}
最后,排序和打印应该通过对原始 Python 的相当简单的翻译来实现:
let months_r: HashMap<_, _> = m.iter().map(|(k, v)| (v, k)).collect();
let mut totals_keys: Vec<_> = totals.keys().collect();
totals_keys.sort();
for k in totals_keys {
println!(
"{} {} {}", months_r.get(k.0).unwrap(),
k.1, totals.get(k).unwrap()
);
}
总共有 48 行代码 rustfmt
- 比 Python 长,但仍比 Go 短。