Crate iof

Source
Expand description

A utility library for reading data from input and writing data to output.

§Principles

  • Simple: You can read and write data with a single line of code.

  • Flexible: You can customize the format of data if the default format does not meet your needs.

  • Efficient: You can read and write data with minimal overhead.

  • Safe: You can read and write data without worrying about buffer overflow or other security issues.

  • Easy to Learn: You can read and write data with a similar interface to Python3 and C++.

  • Extensible: You can implement your own types to read and write data.

  • Compatible: You can read and write data with types that implement std::fmt::Display and std::str::FromStr.

  • Human Readable: You can read and write data in a human-readable format.

    For types whose representation does not have a fixed length in characters, the default separator is a space; otherwise, such as for char, it is an empty string.

§In Short

§read!

You can use read! macro to read a single data item, a Vec or a Mat from input.

  • read!() reads a single data item from input.
  • read!(n) reads n data items from input and stores them in a Vec.
  • read!(m, n) reads m * n data items from input and stores them in a Mat.

Given the input below:

42 abc def
0 0.3 lmn
1 2 3
1 2 3
4 5 6
.@/#$
!@#!@
*&@:,
use iof::{read, Mat};

/// Some examples of reading from standard input.
///
/// We use a `v` to indicate the cursor position.
fn main() {
    // Read a single integer from input.
    //
    // v
    // 42 abc def
    let n: u32 = read!();
    assert_eq!(n, 42);

    // Read a single string from input.
    //
    //    v
    // 42 abc def
    let n: String = read!();
    assert_eq!(n, "abc");

    // Read a vector of characters from input.
    // Spaces are ignored, and those characters need not be separated by spaces.
    //
    //        v
    // 42 abc def
    let v: Vec<char> = read!();
    assert_eq!(v, ['d', 'e', 'f']);

    // Read a tuple from input. Equivalent to:
    //
    // ```
    // let l: u32 = read!();
    // let m: f64 = read!();
    // let n: String = read!();
    // ```
    //
    // v
    // 0 0.3 lmn
    let (l, m, n): (u32, f64, String) = read!();
    assert_eq!(l, 0);
    assert_eq!(m, 0.3);
    assert_eq!(n, "lmn");

    // Read a vector of integers from input.
    // They are separated by spaces.
    //
    // v
    // 1 2 3
    let v: Vec<u32> = read!(3);
    assert_eq!(v, [1, 2, 3]);

    // Read a matrix of integers from input.
    // They are separated by spaces, and newlines are unnecessary but useful for readability.
    //
    // v
    // 1 2 3
    // 4 5 6
    let m: Mat<u32> = read!(2, 3);
    assert_eq!(m, [[1, 2, 3], [4, 5, 6]]);

    // Read a matrix of characters from input.
    // Spaces are ignored and unnecessary, too.
    //
    // v
    // .@/#$
    // !@#!@
    // *&@:,
    let m: Mat<char> = read!(3, 5);
    assert_eq!(
        m,
        [
            ['.', '@', '/', '#', '$'],
            ['!', '@', '#', '!', '@'],
            ['*', '&', '@', ':', ',']
        ]
    );
}

§get_line and get_line_some

You can use get_line functions to read a line of string from current position of cursor in standard input to the end of the line.

Given the input below:

42
abc
use iof::{get_line, read};

fn main() {
    // Read a single string from input.
    //
    // v
    // 42
    // abc
    let s: String = read!();
    assert_eq!(s, "42");

    // Read a line of string from input.
    // Before reading, the cursor is at the end of the previous line.
    // Therefore, it reads an empty string.
    //
    //   v
    // 42
    // abc
    let s: String = get_line();
    assert_eq!(s, "");

    // Read a line of string from input.
    //
    // v
    // abc
    let s: String = get_line();
    assert_eq!(s, "abc");
}

You may have noticed that the get_line function is similar to the input function in Python3 and std::get_line in C++.

Sometimes you may want to ensure that the line is not empty. You can use get_line_some functions to read a non-empty line of string from the position of next non-whitespace character to the end of the line.

Given the input below:

42
abc
use iof::{get_line_some, read};

fn main() {
    // Read a single string from input.
    //
    // v
    // 42
    // abc
    let s: String = read!();
    assert_eq!(s, "42");

    // Read a non-empty line of string from input.
    // Before reading, the cursor is at the end of the previous line.
    // However, as the function name implies, it will repeatedly read lines until a non-empty line is found.
    //
    //   v
    // 42
    // abc
    let s: String = get_line_some();
    assert_eq!(s, "abc");
}

See Cursor for more details.

§show!

You can use show! macro to write something to output.

  • show!(e) writes e to output. They are formatted with default format.
  • show!([a, b], sep = [", "]) writes [a, b] like a Vec to output. They are separated by a comma and a space, as specified in the sep parameter.
  • show!([[a, b], [c, d]], sep = ["\n", " "]) writes e like a Mat to output. Rows of them are separated by LF, and columns are separated by a space, as specified in the sep parameter.

Also, you can append a => and a buffer to write to a buffer instead of standard output, such as show!(e => buf) and show!([a, b], sep = [", "] => buf).

Note that all parameters are optional and placed after a comma, and the order of parameters does not matter. The default value of sep and the default value of end are from the get_default_separator function. See Separator for more details.

You may have noticed that the show! macro is similar to the print function in Python.

Code below writes the output to standard output:

use iof::show;

fn main() {
    // Write a single integer to output.
    show!(42);

    // Write a single string to output.
    show!("Hello, World!");

    // Write a vector of integers to output. There will be a space between the integers.
    show!([1, 2, 3]);

    // Write a matrix of integers to output. There will be a newline between the rows, and a space between the integers.
    show!([[1, 2, 3], [4, 5, 6]]);

    // Write a matrix of characters to output. There will be a newline between the rows, and no space between the characters.
    show!([['.', '@', '/'], ['#', '$', '$']]);

    // Write a tuple to output.
    show!((1, 2, 3));

    // Write a tuple of tuples to output.
    show!(((1, 2), (3, 4)));

    // Write an empty tuple to output.
    show!(());

    // Write a tuple of vectors to output.
    show!(([1, 2], [3, 4]));

    // Write a 3-dimensional vector to output with custom separators.
    show!(
        [[[1, 2], [3, 4]], [[5, 6], [7, 8]]],
        sep = [" | ", " :: ", " "],
    );

    // Write several strings to output and append an exclamation mark.
    show!(("Hello", "World"), sep = ", ", end = "!\n");
}

And code above generates the output below:

42
Hello, World!
1 2 3
1 2 3
4 5 6
.@/
#$$
1 2 3
1 2
3 4

1 2
3 4
1 2 :: 3 4 | 5 6 :: 7 8
Hello, World!

§Input

§ReadInto

Some higher-level functions are provided to read data sequence (a single item is also a sequence) from input:

These functions are implemented for types that implement ReadInto trait. Currently, the following types implement ReadInto trait:

  • All types that implement ReadOneFrom trait;
  • [T; N] where T implements ReadInto trait;
  • Box<[T; N]> where T implements ReadInto trait.
  • Tuple types, e.g., (T1, T2, ..., Tn), where Ti implements ReadInto trait and n is neither 0 nor more than 12.

§ReadOneFrom

Some lower-level functions are provided to read a single data item from input:

These functions are implemented for types that implement ReadOneFrom trait. Currently, the following types in std (or core) implement ReadOneFrom trait:

And you can implement ReadOneFrom trait for your own types by implementing ReadOneFrom::parse method. For FromStr types, you can use the macro impl_read_one_from_for_from_str!.

§Extra ASCII Support

For ASCII characters, you can use ASCIIChar and ASCIIString to read and write them.

Given the input below:

1
23
456
789

Code below reads the input and stores it in variables:

use iof::{read_m_n, read_n, read_one, ASCIIChar, Mat};

fn main() {
    let c: ASCIIChar = read_one();
    assert_eq!(c, ASCIIChar::Digit1);

    let v: Vec<ASCIIChar> = read_n(2);
    assert_eq!(v, [ASCIIChar::Digit2, ASCIIChar::Digit3]);

    let m: Mat<ASCIIChar> = read_m_n(2, 3);
    assert_eq!(
        m,
        [
            [ASCIIChar::Digit4, ASCIIChar::Digit5, ASCIIChar::Digit6],
            [ASCIIChar::Digit7, ASCIIChar::Digit8, ASCIIChar::Digit9],
        ]
    );
}

§Complex Examples for Input

Function read() is the simplest way to read data from standard input.

Given the input below:

42
Hello!
1 2 3 4
1 2
3 4

Code below reads the input and stores it in variables:

use iof::read;

/// Some examples of reading from standard input using the `read` function.
fn main() {
    // Read a single integer from input.
    let n: u32 = read();
    assert_eq!(n, 42);

    // Read a string from input.
    let s: String = read();
    assert_eq!(s, "Hello!");

    // Read an array of integers from input.
    let arr: [u32; 4] = read();
    assert_eq!(arr, [1, 2, 3, 4]);

    // Read a nested array of integers from input.
    let arr: [[u32; 2]; 2] = read();
    assert_eq!(arr, [[1, 2], [3, 4]]);
}

§Output

§SepBy and sep_by!

Some lower-level functions are provided to write a data sequence with customizing format to output:

There won’t be any separator before the first item or after the last item.

For example:

use iof::{sep_by, SepBy};
use std::collections::{BTreeMap, BTreeSet};

let v = vec![1, 2, 3];
let s = sep_by!(&v, ", ");
assert_eq!(s.to_string(), "1, 2, 3");
// Equivalent form:
let s = v.sep_by(", ");
assert_eq!(s.to_string(), "1, 2, 3");

let v = vec![vec![1, 2, 3], vec![4, 5, 6]];
let s = sep_by!(&v, "\n", ", ");
assert_eq!(s.to_string(), "1, 2, 3\n4, 5, 6");
// Equivalent form:
let s = v.iter().map(|e| e.sep_by(", ")).sep_by("\n");
assert_eq!(s.to_string(), "1, 2, 3\n4, 5, 6");

let v = BTreeSet::from_iter([3, 1, 2, 4]);
let s = sep_by!(&v, ", ");
assert_eq!(s.to_string(), "1, 2, 3, 4");
// Equivalent form:
let s = v.sep_by(", ");
assert_eq!(s.to_string(), "1, 2, 3, 4");

let v = BTreeMap::from_iter([(3, "w"), (1, "x"), (2, "y"), (4, "z")]);
let s = sep_by!(v.iter().map(|(k, v)| format!("{} -> {}", k, v)), "\n");
assert_eq!(s.to_string(), "1 -> x\n2 -> y\n3 -> w\n4 -> z");
// Equivalent form:
let s = v.iter().map(|(k, v)| format!("{} -> {}", k, v)).sep_by("\n");
assert_eq!(s.to_string(), "1 -> x\n2 -> y\n3 -> w\n4 -> z");

Note that the iterator must implement Clone trait to use the SepBy trait. And due to this constraint, if you write a container directly as the argument of sep_by!, you may need to use & to borrow it.

And created objects can also be used in some formats other than Display format or Debug format.

use iof::{sep_by, SepBy};
use std::f64::consts::*;

let v = vec![1.0, 2.1, 3.2];
let s = sep_by!(&v, ", ");
assert_eq!(format!("{s:?}"), "1.0, 2.1, 3.2");

let v = vec!["Alice", "Bob", "Charlie"];
let s = sep_by!(&v, ";");
assert_eq!(format!("{s:>10}"), "     Alice;       Bob;   Charlie");
assert_eq!(format!("{s:<10}"), "Alice     ;Bob       ;Charlie   ");

let v = vec![E, PI, FRAC_1_PI, LN_2, LN_10, SQRT_2];
let s = sep_by!(&v, "");
assert_eq!(format!("{s:15.7}"), "      2.7182818      3.1415927      0.3183099      0.6931472      2.3025851      1.4142136");

let v = vec![3735928559u32, 3405691582u32, 3405709037u32, 3435973836u32, 3452816845u32];
let s = sep_by!(&v, " ");
assert_eq!(format!("{s:x}"), "deadbeef cafebabe cafefeed cccccccc cdcdcdcd");

§WriteInto

Some higher-level functions are provided to write data sequence with default format to output:

The default format is defined as follows:

And you can implement WriteInto trait for your own types by implementing WriteInto::try_write_into_with_sep method. For Display types, you can use the macro impl_write_into_for_display!.

§Separator

The separator is a string that separates data items. It can be a single character, a string, or a slice of strings.

The default separator from get_default_separator is defined as follows:

  • For all types whose dimension is 0, it uses [];
  • For all types whose dimension is 1 and T must be separated by a space, it uses [" "];
  • For all types whose dimension is 1 and T need not be separated by a space, it uses [""];
  • For all types whose dimension is 2 and T must be separated by a space, it uses ["\n", " "];
  • For all types whose dimension is 2 and T need not be separated by a space, it uses ["\n", ""].

The dimension of a type is the number of dimensions of the data sequence. For example, the dimension of a primitive type T is 0, the dimension of Vec<T> is 1, and the dimension of Mat<T> is 2.

§Notes

§Concurrency

Take care when using this library in a multi-threaded environment, as the standard input/output streams are shared among all threads. See Stdin and Stdout for more details.

§Cursor

For character streams, The cursor is the position of the next character to be read. It is at the beginning of the input stream initially, and it moves forward as data items are read.

In general, every call to a read function that consume a data item will consume the input up to the next whitespace character (but white spaces after the data item will not be consumed), and every call to a read function that reads a line will consume the input up to the next newline character (and then the cursor will be at the beginning of the next line).

It’s sometimes a little tricky to determine the position of the cursor. For example, given the input below:

1 2 3
4 5 6

If you call read_one<String>() for 3 times and read_in_line_trimmed<String>() for 1 time, they will read 1, 2, 3, and an empty string respectively. Therefore it’s generally unrecommended to use read_in_line_trimmed<String>() and similar functions that read a possibly empty line of string without specifying the number of data items to read.

Re-exports§

pub use crate as iof;

Modules§

ascii
A port of the ascii module from Rust’s standard library with a little bit of extra functionality.
dimension
Dimension markers.
ext
Extensions for characters and strings.
fmt
Format trait for input format and built-in formats.
separator
Separator and default separator.
utf8char
UTF-8 character types.

Macros§

argument_or_default
Return the given expression or the default value.
impl_read_one_from_for_from_str
Implement ReadOneFrom for given types that already implement std::str::FromStr.
impl_write_into_for_display
Implement WriteInto for given types that already implements std::fmt::Display.
read
Read a single data item, a Vec or a Mat from input using ReadInto.
sep_by
Create an object using given separator.
show
Write the given expression into standard output using WriteInto.
unwrap
Unwrap a result or panic with the error message.

Structs§

ASCIIString
A string that only contains ASCII characters.
DefaultSeparator
Use default separator.
InputStream
C++-like Stream.
Vec
A contiguous growable array type, written as Vec<T>, short for ‘vector’.

Enums§

ASCIIChar
An ASCII character. This is a subset of the Unicode character set.
ReadError
Error during using ReadInto or ReadOneFrom.

Traits§

BufReadExt
Extension trait for BufRead.
BufReadExtWithFormat
Extension trait for BufReadExt with CharSet and Pattern.
ReadFrom
Read data from input stream.
ReadInto
The opposite of ReadFrom.
ReadOneFrom
Read a single data item from input stream.
ReadOneInto
The opposite of ReadOneFrom.
SepBy
Create a new object using given iterator and separator.
Separators
Separator by.
WriteInto
Write into a stream.

Functions§

get_line
Read a line from standard input. The trailing newline will be consumed and trimmed.
get_line_some
Read a non-empty line from standard input. The trailing newline will be consumed and trimmed.
read
Unwrap the result of try_read.
read_all
Unwrap the result of try_read_all.
read_any_in_line
Unwrap the result of try_read_any_in_line.
read_in_char
Unwrap the result of try_read_in_char.
read_in_line_some_trimmed
Unwrap the result of try_read_in_line_some_trimmed.
read_in_line_trimmed
Unwrap the result of try_read_in_line_trimmed.
read_m_n
Unwrap the result of try_read_m_n.
read_mat
Unwrap the result of try_read_m_n.
read_n
Unwrap the result of try_read_n.
read_one
Unwrap the result of try_read_one.
read_some_in_line
Unwrap the result of try_read_some_in_line.
read_vec
Unwrap the result of try_read_n.
stdin
Get an exclusive handle to the standard input stream.
stdout
Get an exclusive handle to the standard output stream.
try_read
Call ReadInto::try_read on stdin.
try_read_all
Call ReadOneInto::try_read_all on stdin.
try_read_any_in_line
Call ReadOneInto::try_read_any_in_line on stdin.
try_read_in_char
Call ReadOneInto::try_read_in_char on stdin.
try_read_in_line_some_trimmed
Call ReadOneInto::try_read_in_line_some_trimmed on stdin.
try_read_in_line_trimmed
Call ReadOneInto::try_read_in_line_trimmed on stdin.
try_read_m_n
Call ReadInto::try_read_m_n on stdin.
try_read_mat
Call ReadInto::try_read_m_n on stdin.
try_read_n
Call ReadInto::try_read_n on stdin.
try_read_one
Call ReadOneInto::try_read_one on stdin.
try_read_some_in_line
Call ReadOneInto::try_read_some_in_line on stdin.
try_read_vec
Call ReadInto::try_read_n on stdin.
write
Write the given value into the buffer.

Type Aliases§

Mat
A matrix with m rows and n columns.
ReadFromError
The error type for ReadFrom.
ReadOneFromError
The error type for ReadOneFrom.