#rust
#Ржавчина
Вопрос:
Я создаю что-то похожее на wc
команду. Подсчет строк, кажется, работает нормально, но подсчет байтов всегда возвращает 0. Подсчет слов также не работает; вывод, похоже, «зависает», как будто он чего-то ждет.
Я понимаю, что способ, которым это сделано (чтение файла 3 раза), не самый оптимальный способ сделать это, но я просто хочу рабочий и простой пример
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
fn main() {
let arg = amp;std::env::args()
.nth(1)
.expect("No file operand found")
.to_owned();
let file = File::open(arg).expect("Unable to open file for reading");
let lines = count_lines(amp;file);
print!("{} ", lines);
let bytes = count_bytes(amp;file);
println!("{}", bytes);
let words = count_words(amp;file);
print!("{} ", words);
}
fn count_lines(file: amp;File) -> u32 {
let mut count: u32 = 0;
BufReader::new(file).lines().for_each(|f| {
if f.is_ok() {
count = 1;
}
});
count
}
fn count_bytes(file: amp;File) -> u32 {
let mut count: usize = 0;
BufReader::new(file).bytes().for_each(|f| {
if f.is_ok() {
count = 1;
}
});
count as u32
}
fn count_words(file: amp;File) -> u32 {
let mut count: u32 = 0;
let mut buf: Vec<u8> = Vec::new();
let mut reader = BufReader::new(file);
while let Ok(_) = reader.read_until(b' ', amp;mut buf) {
count = 1;
}
count
}
Ответ №1:
Ваша проблема в том, что вы открываете файл один раз, читаете весь файл, а затем предполагаете, что он будет сброшен волшебным образом.
A File
имеет позицию «указатель», чтобы знать, какой байт читать следующим. После того, как вы прочитаете один байт, эта позиция будет увеличена на единицу, поэтому следующий вызов read прочитает следующий байт, а не тот же самый.
Вы можете изменить эту позицию, используя File::seek
между вашими вызовами count_lines
, count_bytes
и count_words
.
use std::io::{Seek, SeekFrom};
fn main() {
let arg = amp;std::env::args()
.nth(1)
.expect("No file operand found")
.to_owned();
let mut file = File::open(arg).expect("Unable to open file for reading");
let lines = count_lines(amp;file);
print!("{} ", lines);
file.seek(SeekFrom::Start(0)).expect("Seek failed");
let bytes = count_bytes(amp;file);
println!("{}", bytes);
file.seek(SeekFrom::Start(0)).expect("Seek failed");
let words = count_words(amp;file);
print!("{} ", words);
}
Для дальнейшего обращения к вашему коду, он не считается очень «ржавым». Ваш ручной подсчет может быть упрощен с помощью Iterator::count
.
fn count_lines(file: amp;File) -> u32 {
BufReader::new(file).lines().count() as u32
}
fn count_bytes(file: amp;File) -> u32 {
BufReader::new(file).bytes().count() as u32
}
Причина, по которой ваша count_words
функция «зависает», заключается в том, что вы игнорируете количество прочитанных байтов. Когда read_until
будет достигнут EOF (конец файла), он вернется 0
в виде суммы. Вы должны ввести условие прерывания, например
fn count_words(file: amp;File) -> u32 {
let mut count: u32 = 0;
let mut buf: Vec<u8> = Vec::new();
let mut reader = BufReader::new(file);
while let Ok(amount) = reader.read_until(b' ', amp;mut buf) {
if amount == 0 {
break
}
count = 1;
}
count
}
Пожалуйста, обратите внимание, что эта реализация не совсем правильная, потому что "hello "
(два пробела в конце) даст вам 2
вместо 1
, но это вам решать исправить. Не забудьте добавить несколько тестов, чтобы убедиться, что все работает правильно.
Комментарии:
1. Честно говоря, меня это всегда сбивало с толку; хотелось бы, чтобы это называлось FileCursor, чтобы было понятнее.
2. @MatthieuM. возможно, вы правы, но AFAIK каждый язык работает таким образом (вероятно, из-за того, как linux обрабатывает файлы). Боюсь, это то, чему нужно научиться ^^