Минимальное туалет для ввода


Это минимальный reimplemtation из wc. Он поддерживает на данный момент только stdin и без аргументов командной строки, что на более поздней версии.

Это моя первая полная программа ржавчины/пакет, так что мне интересны любые комментарии, включая, но не ограничиваясь:

  • документация,
  • комментарии,
  • общий стиль и типы,
  • тесты,
  • любое другое замечание.

Грузов.томл

[package]
name = "wc"
version = "0.1.0"

[dependencies]

в src/Либ.РС (на детской площадке)

use std::io::Read;

/// The statistics returned by `wordcount`.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct WordCountStats {
    /// number of bytes in the input
    pub bytes: usize,
    /// number of groups of consecutive non-whitespace characters
    pub words: usize,
    /// number of newline characters (`\n`)
    pub newlines: usize,
}

/// Returns the word count statistics of the given `reader`.
///
/// ```
/// use wc::{wordcount,WordCountStats};
///
/// assert_eq!(
///    wordcount("Hello, World!".as_bytes()).unwrap(),
///    WordCountStats {
///        bytes: 13,
///        words: 2,
///        newlines: 0,
///    }
/// );
/// ```
///
/// The statistics follow `wc` (`man 1 wc`) output:
///
/// * bytes is always the number of bytes (not utf8 characters or similar)
/// * words is the number of positive length consecutive non-whitespace runs
/// * newlines is the number of newlines (NOT the number of lines)
///
/// `wordcount` uses `bytes()` internally and tries not to
/// add any buffering to the `reader`. If you use an unbuffered
/// device, consider using `BufRead` around your content.
///
/// # Errors
/// If a `byte` couldn't get read you will get a `Err(std::io::Error)`.
/// This can happen if the socket disconnects suddenly, a filesystem
/// error occurred, or your scanner couldn't continue to read the stripes
/// from your cat.
pub fn wordcount<R>(reader: R) -> std::io::Result<WordCountStats>
where
    R: Read,
{
    let mut bytes = 0;
    let mut words = 0;
    let mut newlines = 0;
    let mut spacemode = true;

    for byte in reader.bytes() {
        bytes += 1;

        let c = byte?;

        if (c as char).is_whitespace() {
            spacemode = true
        } else if spacemode {
            // A non-whitespace character after a whitespace character sequence.
            words += 1;
            spacemode = false
        }

        if c as char == '\n' {
            newlines += 1
        }
    }

    Ok(WordCountStats {
        bytes,
        words,
        newlines,
    })
}

#[cfg(test)]
mod tests {
    use WordCountStats;
    fn wc_string(input: &str) -> ::WordCountStats {
        ::wordcount(input.as_bytes()).unwrap()
    }
    #[test]
    fn empty_input() {
        assert_eq!(
            wc_string(""),
            WordCountStats {
                bytes: 0,
                words: 0,
                newlines: 0,
            }
        )
    }
    #[test]
    fn single_letter_input() {
        assert_eq!(
            wc_string("a"),
            WordCountStats {
                bytes: 1,
                words: 1,
                newlines: 0,
            }
        )
    }
    #[test]
    fn single_space_input() {
        assert_eq!(
            wc_string(" "),
            WordCountStats {
                bytes: 1,
                words: 0,
                newlines: 0,
            }
        )
    }
    #[test]
    fn two_letters_separated_by_spaces() {
        assert_eq!(
            wc_string("a \t b"),
            WordCountStats {
                bytes: 5,
                words: 2,
                newlines: 0,
            }
        )
    }
    #[test]
    fn two_line_input() {
        assert_eq!(
            wc_string("\n"),
            WordCountStats {
                bytes: 1,
                words: 0,
                newlines: 1,
            }
        )
    }

    #[test]
    fn complicated_input() {
        assert_eq!(
            wc_string("Hello, World!\nHow are you today?\nI hope you're fine!"),
            WordCountStats {
                bytes: 52,
                words: 10,
                newlines: 2,
            }
        )
    }
}

в src/ОГРН/главная.РС

Это всего лишь огрызок main для проверки библиотеки. Я развернуть его в более поздней версии. Вы можете рассмотреть его, но я не акцентировал на этом.

extern crate wc;

fn main() {
    let filename = "-";
    let stdin = std::io::stdin();
    let handle = stdin.lock();
    match wc::wordcount(handle) {
        Err(e) => eprintln!("{}: {}", filename, e),
        Ok(wc::WordCountStats {
            bytes,
            words,
            newlines,
        }) => println!("{:8} {:8} {:12} {}", newlines, words, bytes, filename),
    }
}


134
5
задан 28 марта 2018 в 06:03 Источник Поделиться
Комментарии
1 ответ

Для такой программы, определение пробелов имеет решающее значение. Ваш код не документ, что означает пробел. Проверка кода показывает, что, поскольку вы переходите по байтам, вы, вероятно, не справиться с любой из более интересных символов Юникода:

printf %b '\u200b \u200b \u200b \u200b \u200b' | cargo run
0 5 34 -

Я бы, наверное, получают Default для структуры, называют его, затем мутировать поля структуры в функцию, вместо 3 отдельных переменных:

Он чувствует себя очень странно, не сразу выполняют проверку ошибок внутри for петли. Я бы переместить ? в первом выражении. Я бы тоже тень на имя byte потому что имя c звучит как char, что это не так. Это освобождает имени переменной c чтобы быть тип-литой версии.

Я бы перемещение свободной функции в связанную функцию WordCountStats.

Использовать более вертикали в ваших тестах. Вертикальные пробелы поможет наш глаз легко подобрать различные связанные разделы друг от друга.

Поскольку вы импортировали WordCountStats в ваших тестах, вам не нужно путь-добавлять ::.

Я фанат маленьких служебная функция тестирования, которые вы добавили.

use std::io::Read;

/// The statistics returned by `WordCountStats::from_reader`.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct WordCountStats {
/// number of bytes in the input
pub bytes: usize,
/// number of groups of consecutive non-whitespace characters
pub words: usize,
/// number of newline characters (`\n`)
pub newlines: usize,
}

impl WordCountStats {
/// Returns the word count statistics of the given `reader`.
///
/// ```
/// use wc::WordCountStats;
///
/// assert_eq!(
/// WordCountStats::from_reader("Hello, World!".as_bytes()).unwrap(),
/// WordCountStats {
/// bytes: 13,
/// words: 2,
/// newlines: 0,
/// }
/// );
/// ```
///
/// The statistics follow `wc` (`man 1 wc`) output:
///
/// * bytes is always the number of bytes (not utf8 characters or similar)
/// * words is the number of positive length consecutive non-whitespace runs
/// * newlines is the number of newlines (NOT the number of lines)
///
/// `WordCountStats::from_reader` uses `bytes()` internally and tries not to
/// add any buffering to the `reader`. If you use an unbuffered
/// device, consider using `BufRead` around your content.
///
/// # Errors
/// If a `byte` couldn't get read you will get a `Err(std::io::Error)`.
/// This can happen if the socket disconnects suddenly, a filesystem
/// error occurred, or your scanner couldn't continue to read the stripes
/// from your cat.
pub fn from_reader<R>(reader: R) -> std::io::Result<WordCountStats>
where
R: Read,
{
let mut stats = WordCountStats::default();
let mut spacemode = true;

for byte in reader.bytes() {
let byte = byte?;
let c = byte as char;

stats.bytes += 1;

if c.is_whitespace() {
spacemode = true
} else if spacemode {
// A non-whitespace character after a whitespace character sequence.
stats.words += 1;
spacemode = false
}

if c == '\n' {
stats.newlines += 1
}
}

Ok(stats)
}
}

#[cfg(test)]
mod tests {
use WordCountStats;

fn wc_string(input: &str) -> WordCountStats {
::WordCountStats::from_reader(input.as_bytes()).unwrap()
}

#[test]
fn empty_input() {
assert_eq!(
wc_string(""),
WordCountStats {
bytes: 0,
words: 0,
newlines: 0,
}
)
}

#[test]
fn single_letter_input() {
assert_eq!(
wc_string("a"),
WordCountStats {
bytes: 1,
words: 1,
newlines: 0,
}
)
}

#[test]
fn single_space_input() {
assert_eq!(
wc_string(" "),
WordCountStats {
bytes: 1,
words: 0,
newlines: 0,
}
)
}

#[test]
fn two_letters_separated_by_spaces() {
assert_eq!(
wc_string("a \t b"),
WordCountStats {
bytes: 5,
words: 2,
newlines: 0,
}
)
}

#[test]
fn two_line_input() {
assert_eq!(
wc_string("\n"),
WordCountStats {
bytes: 1,
words: 0,
newlines: 1,
}
)
}

#[test]
fn complicated_input() {
assert_eq!(
wc_string("Hello, World!\nHow are you today?\nI hope you're fine!"),
WordCountStats {
bytes: 52,
words: 10,
newlines: 2,
}
)
}
}



Реальные тестовые случаи были поэтому ли cargo run возвращает то же, что wc

Сделать этот тест, затем. Мне нравится использовать встраиваемое:

use std::{
io::Write,
process::{Command, Stdio},
};

fn wc_program(bytes: &[u8]) -> WordCountStats {
let mut child = Command::new("wc")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();

child.stdin.as_mut().unwrap().write_all(bytes).unwrap();

let out = child.wait_with_output().unwrap();
let out = String::from_utf8(out.stdout).unwrap();
let mut nums = out.split_whitespace().map(|n| n.parse::<usize>().unwrap()).fuse();

WordCountStats {
newlines: nums.next().unwrap(),
words: nums.next().unwrap(),
bytes: nums.next().unwrap(),
}
}

quickcheck! {
fn prop(xs: Vec<u8>) -> bool {
let me = WordCountStats::from_reader(&xs[..]).unwrap();
let them = wc_program(&xs);
me == them
}
}

1
ответ дан 15 апреля 2018 в 12:04 Источник Поделиться