Итерирование байтов файла пусто после итерации строк одного и того же файла - PullRequest
2 голосов
/ 24 апреля 2019

Я создаю что-то похожее на команду wc.Подсчет строк, кажется, работает нормально, но подсчет байтов всегда возвращает 0. Подсчет слов также не работает;кажется, что вывод "зависает", как будто он чего-то ждет.

Я понимаю, что способ его создания (чтение файла 3 раза) не самый оптимальный способ сделать это, но я просто хочурабочий и простой пример

use std::fs::File;
use std::io::{BufRead, BufReader, Read};

fn main() {
    let arg = &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(&file);
    print!("{} ", lines);
    let bytes = count_bytes(&file);
    println!("{}", bytes);
    let words = count_words(&file);
    print!("{} ", words);
}

fn count_lines(file: &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: &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: &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' ', &mut buf) {
        count += 1;
    }

    count
}

1 Ответ

2 голосов
/ 24 апреля 2019

Ваша проблема в том, что вы открываете файл один раз, читаете весь файл и затем предполагаете, что он будет сброшен волшебным образом.

A File имеет позицию "указатель", чтобы знать, какой байт читать следующим,После того, как вы прочитали один байт, эта позиция будет увеличена на единицу, поэтому при следующем вызове чтения будет прочитан следующий, а не тот же байт.

Вы можете изменить эту позицию с помощью File::seek между вашими звонками на count_lines, count_bytes и count_words.

use std::io::{Seek, SeekFrom};

fn main() {
    let arg = &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(&file);
    print!("{} ", lines);

    file.seek(SeekFrom::Start(0)).expect("Seek failed");
    let bytes = count_bytes(&file);
    println!("{}", bytes);

    file.seek(SeekFrom::Start(0)).expect("Seek failed");
    let words = count_words(&file);
    print!("{} ", words);
}

Для дальнейшего обращения к вашему коду он не считается очень "ржавым".Ваш ручной подсчет может быть упрощен с помощью Iterator::count.

fn count_lines(file: &File) -> u32 {
    BufReader::new(file).lines().count() as u32
}

fn count_bytes(file: &File) -> u32 {
    BufReader::new(file).bytes().count() as u32
}

Причина, по которой ваша функция count_words "зависает", заключается в том, что вы игнорируете количество прочитанных байтов,Когда read_until достигает EOF (конец файла), возвращается 0 как сумма.Вы должны ввести условие останова, например,

fn count_words(file: &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' ', &mut buf) {
        if amount == 0 {
            break
        }
        count += 1;
    }

    count
}

Обратите внимание, что эта реализация на самом деле не верна, потому что "hello " (два пробела в конце) даст вам 2 вместо 1, но это решать вам. добавьте несколько тестов , чтобы убедиться, что все работает правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...