如何使用带有 BufReader 的文件并且仍然能够写入它?
How to use a file with a BufReader and still be able to write to it?
我想打开一个文件并使用 lines()
将其内容读取为 BufReader
。我还希望能够找到文件的末尾并写一些新行。
使用 let mut file
可以写入文件,但是一旦我将文件交给 BufReader
我就不能再写入了,因为主函数不再拥有 file
:
fn main() {
let filename = "tt.txt";
// open a tt.txt file in the local directory
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(filename)
.unwrap();
// now read the whole file to get the latest state
let date_re = Regex::new(r"^(\d{4})-(\d{2})-(\d{2})").unwrap();
let time_activity_re = Regex::new(r"^(\d{2}):(\d{2})\s*(.*)").unwrap();
let reader = BufReader::new(file);
let mut latest_date: Option<Date<Local>> = None;
let mut latest_datetime: Option<DateTime<Local>> = None;
let mut latest_activity: Option<String> = None;
for wrapped_line in reader.lines() {
let line = wrapped_line.unwrap();
println!("line: {}", line);
if date_re.is_match(&line) {
let captures = date_re.captures(&line).unwrap();
let year = captures.at(1).unwrap().parse::<i32>().unwrap();
let month = captures.at(2).unwrap().parse::<u32>().unwrap();
let day = captures.at(3).unwrap().parse::<u32>().unwrap();
latest_date = Some(Local.ymd(year, month, day));
latest_datetime = None;
latest_activity = None;
}
if time_activity_re.is_match(&line) && latest_date != None {
let captures = time_activity_re.captures(&line).unwrap();
let hour = captures.at(1).unwrap().parse::<u32>().unwrap();
let minute = captures.at(2).unwrap().parse::<u32>().unwrap();
let activity = captures.at(3).unwrap();
latest_datetime = Some(latest_date.unwrap().and_hms(hour, minute, 0));
latest_activity = if activity.len() > 0 {
// TODO: if latest_activity already constains a string, clear it and reuse it
// as per:
Some(activity.to_string())
} else {
None
};
println!("time activity: {} |{}|", latest_datetime.unwrap(), activity);
}
}
// FIXME: I have to open a second file descriptor to the same file, in order to be able to write to it
let mut out = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(filename)
.unwrap();
out.seek(End(0));
let now = Local::now();
if latest_date == None || latest_date.unwrap().year() != now.year()
|| latest_date.unwrap().month() != now.month()
|| latest_date.unwrap().day() != now.day()
{
if (latest_date != None) {
// not an empy file, as far as tt is concerned
out.write_all(b"\n\n");
}
out.write_all(format!("{}\n", now.format("%Y-%m-%d")).as_bytes());
out.write_all(b"\n");
}
let activity = env::args().skip(1).join(" ");
if (activity.len() > 0) {
out.write_all(format!("{} {}\n", now.format("%H:%M"), activity).as_bytes());
} else {
// if there was no latest activity *and* there is no activity, then there's no point in writing a second blank line with just a time
if latest_activity == None {
return;
}
out.write_all(format!("{}\n", now.format("%H:%M")).as_bytes());
}
// FIXME: we're just relying on the program exit to close the two file descriptors (which point at the same file).
}
如何使用单个文件描述符读取现有行并追加新行?
(来自 https://github.com/chrisdew/tt/blob/e899f252014391f2e01c3cc9e281cab1ab88936f/src/main.rs 的代码)
为避免移动值,您可以使用引用和新范围。
以下是您可以执行此操作的方法:
fn main() {
let filename = "tt.txt";
// open a tt.txt file in the local directory
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(filename)
.unwrap();
// now read the whole file to get the latest state
let date_re = Regex::new(r"^(\d{4})-(\d{2})-(\d{2})").unwrap();
let time_activity_re = Regex::new(r"^(\d{2}):(\d{2})\s*(.*)").unwrap();
{
// BufReader now borrows the value instead of taking ownership.
let reader = BufReader::new(&mut file);
let mut latest_date: Option<Date<Local>> = None;
let mut latest_datetime: Option<DateTime<Local>> = None;
let mut latest_activity: Option<String> = None;
for wrapped_line in reader.lines() {
let line = wrapped_line.unwrap();
println!("line: {}", line);
if date_re.is_match(&line) {
let captures = date_re.captures(&line).unwrap();
let year = captures.at(1).unwrap().parse::<i32>().unwrap();
let month = captures.at(2).unwrap().parse::<u32>().unwrap();
let day = captures.at(3).unwrap().parse::<u32>().unwrap();
latest_date = Some(Local.ymd(year, month, day));
latest_datetime = None;
latest_activity = None;
}
if time_activity_re.is_match(&line) && latest_date != None {
let captures = time_activity_re.captures(&line).unwrap();
let hour = captures.at(1).unwrap().parse::<u32>().unwrap();
let minute = captures.at(2).unwrap().parse::<u32>().unwrap();
let activity = captures.at(3).unwrap();
latest_datetime = Some(latest_date.unwrap().and_hms(hour, minute, 0));
latest_activity = if activity.len() > 0 {
// TODO: if latest_activity already constains a string, clear it and reuse it
// as per:
Some(activity.to_string())
} else {
None
};
println!("time activity: {} |{}|", latest_datetime.unwrap(), activity);
}
}
}
// End of the scope, so now file is not borrowed anymore.
file.seek(End(0));
let now = Local::now();
if latest_date == None || latest_date.unwrap().year() != now.year()
|| latest_date.unwrap().month() != now.month()
|| latest_date.unwrap().day() != now.day()
{
if (latest_date != None) {
// not an empy file, as far as tt is concerned
file.write_all(b"\n\n");
}
file.write_all(format!("{}\n", now.format("%Y-%m-%d")).as_bytes());
file.write_all(b"\n");
}
let activity = env::args().skip(1).join(" ");
if (activity.len() > 0) {
file.write_all(format!("{} {}\n", now.format("%H:%M"), activity).as_bytes());
} else {
// if there was no latest activity *and* there is no activity, then there's no point in writing a second blank line with just a time
if latest_activity == None {
return;
}
file.write_all(format!("{}\n", now.format("%H:%M")).as_bytes());
}
// FIXME: we're just relying on the program exit to close the two file descriptors (which point at the same file).
}
您可以使用 BufReader::into_inner
to "recover" the file after it's been passed to the BufReader
. This can be used in conjunction with Read::by_ref
来避免首先放弃 BufReader<File>
的所有权:
use std::{
fs::File,
io::{BufRead, BufReader, Read, Write},
};
fn example(file: File) {
let mut reader = BufReader::new(file);
for _ in reader.by_ref().lines() {}
let mut out = reader.into_inner();
out.write_all(b"new stuff").unwrap();
}
这里是 具有类似的简化代码:
use std::{
fs::File,
io::{BufRead, BufReader, Write},
};
fn example(mut file: File) {
let reader = BufReader::new(&file);
for _ in reader.lines() {}
file.write_all(b"new stuff").unwrap();
}
我想打开一个文件并使用 lines()
将其内容读取为 BufReader
。我还希望能够找到文件的末尾并写一些新行。
使用 let mut file
可以写入文件,但是一旦我将文件交给 BufReader
我就不能再写入了,因为主函数不再拥有 file
:
fn main() {
let filename = "tt.txt";
// open a tt.txt file in the local directory
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(filename)
.unwrap();
// now read the whole file to get the latest state
let date_re = Regex::new(r"^(\d{4})-(\d{2})-(\d{2})").unwrap();
let time_activity_re = Regex::new(r"^(\d{2}):(\d{2})\s*(.*)").unwrap();
let reader = BufReader::new(file);
let mut latest_date: Option<Date<Local>> = None;
let mut latest_datetime: Option<DateTime<Local>> = None;
let mut latest_activity: Option<String> = None;
for wrapped_line in reader.lines() {
let line = wrapped_line.unwrap();
println!("line: {}", line);
if date_re.is_match(&line) {
let captures = date_re.captures(&line).unwrap();
let year = captures.at(1).unwrap().parse::<i32>().unwrap();
let month = captures.at(2).unwrap().parse::<u32>().unwrap();
let day = captures.at(3).unwrap().parse::<u32>().unwrap();
latest_date = Some(Local.ymd(year, month, day));
latest_datetime = None;
latest_activity = None;
}
if time_activity_re.is_match(&line) && latest_date != None {
let captures = time_activity_re.captures(&line).unwrap();
let hour = captures.at(1).unwrap().parse::<u32>().unwrap();
let minute = captures.at(2).unwrap().parse::<u32>().unwrap();
let activity = captures.at(3).unwrap();
latest_datetime = Some(latest_date.unwrap().and_hms(hour, minute, 0));
latest_activity = if activity.len() > 0 {
// TODO: if latest_activity already constains a string, clear it and reuse it
// as per:
Some(activity.to_string())
} else {
None
};
println!("time activity: {} |{}|", latest_datetime.unwrap(), activity);
}
}
// FIXME: I have to open a second file descriptor to the same file, in order to be able to write to it
let mut out = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(filename)
.unwrap();
out.seek(End(0));
let now = Local::now();
if latest_date == None || latest_date.unwrap().year() != now.year()
|| latest_date.unwrap().month() != now.month()
|| latest_date.unwrap().day() != now.day()
{
if (latest_date != None) {
// not an empy file, as far as tt is concerned
out.write_all(b"\n\n");
}
out.write_all(format!("{}\n", now.format("%Y-%m-%d")).as_bytes());
out.write_all(b"\n");
}
let activity = env::args().skip(1).join(" ");
if (activity.len() > 0) {
out.write_all(format!("{} {}\n", now.format("%H:%M"), activity).as_bytes());
} else {
// if there was no latest activity *and* there is no activity, then there's no point in writing a second blank line with just a time
if latest_activity == None {
return;
}
out.write_all(format!("{}\n", now.format("%H:%M")).as_bytes());
}
// FIXME: we're just relying on the program exit to close the two file descriptors (which point at the same file).
}
如何使用单个文件描述符读取现有行并追加新行?
(来自 https://github.com/chrisdew/tt/blob/e899f252014391f2e01c3cc9e281cab1ab88936f/src/main.rs 的代码)
为避免移动值,您可以使用引用和新范围。 以下是您可以执行此操作的方法:
fn main() {
let filename = "tt.txt";
// open a tt.txt file in the local directory
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(filename)
.unwrap();
// now read the whole file to get the latest state
let date_re = Regex::new(r"^(\d{4})-(\d{2})-(\d{2})").unwrap();
let time_activity_re = Regex::new(r"^(\d{2}):(\d{2})\s*(.*)").unwrap();
{
// BufReader now borrows the value instead of taking ownership.
let reader = BufReader::new(&mut file);
let mut latest_date: Option<Date<Local>> = None;
let mut latest_datetime: Option<DateTime<Local>> = None;
let mut latest_activity: Option<String> = None;
for wrapped_line in reader.lines() {
let line = wrapped_line.unwrap();
println!("line: {}", line);
if date_re.is_match(&line) {
let captures = date_re.captures(&line).unwrap();
let year = captures.at(1).unwrap().parse::<i32>().unwrap();
let month = captures.at(2).unwrap().parse::<u32>().unwrap();
let day = captures.at(3).unwrap().parse::<u32>().unwrap();
latest_date = Some(Local.ymd(year, month, day));
latest_datetime = None;
latest_activity = None;
}
if time_activity_re.is_match(&line) && latest_date != None {
let captures = time_activity_re.captures(&line).unwrap();
let hour = captures.at(1).unwrap().parse::<u32>().unwrap();
let minute = captures.at(2).unwrap().parse::<u32>().unwrap();
let activity = captures.at(3).unwrap();
latest_datetime = Some(latest_date.unwrap().and_hms(hour, minute, 0));
latest_activity = if activity.len() > 0 {
// TODO: if latest_activity already constains a string, clear it and reuse it
// as per:
Some(activity.to_string())
} else {
None
};
println!("time activity: {} |{}|", latest_datetime.unwrap(), activity);
}
}
}
// End of the scope, so now file is not borrowed anymore.
file.seek(End(0));
let now = Local::now();
if latest_date == None || latest_date.unwrap().year() != now.year()
|| latest_date.unwrap().month() != now.month()
|| latest_date.unwrap().day() != now.day()
{
if (latest_date != None) {
// not an empy file, as far as tt is concerned
file.write_all(b"\n\n");
}
file.write_all(format!("{}\n", now.format("%Y-%m-%d")).as_bytes());
file.write_all(b"\n");
}
let activity = env::args().skip(1).join(" ");
if (activity.len() > 0) {
file.write_all(format!("{} {}\n", now.format("%H:%M"), activity).as_bytes());
} else {
// if there was no latest activity *and* there is no activity, then there's no point in writing a second blank line with just a time
if latest_activity == None {
return;
}
file.write_all(format!("{}\n", now.format("%H:%M")).as_bytes());
}
// FIXME: we're just relying on the program exit to close the two file descriptors (which point at the same file).
}
您可以使用 BufReader::into_inner
to "recover" the file after it's been passed to the BufReader
. This can be used in conjunction with Read::by_ref
来避免首先放弃 BufReader<File>
的所有权:
use std::{
fs::File,
io::{BufRead, BufReader, Read, Write},
};
fn example(file: File) {
let mut reader = BufReader::new(file);
for _ in reader.by_ref().lines() {}
let mut out = reader.into_inner();
out.write_all(b"new stuff").unwrap();
}
这里是
use std::{
fs::File,
io::{BufRead, BufReader, Write},
};
fn example(mut file: File) {
let reader = BufReader::new(&file);
for _ in reader.lines() {}
file.write_all(b"new stuff").unwrap();
}