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)
readsn
data items from input and stores them in a Vec.read!(m, n)
readsm * 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)
writese
to output. They are formatted with default format.show!([a, b], sep = [", "])
writes[a, b]
like aVec
to output. They are separated by a comma and a space, as specified in thesep
parameter.show!([[a, b], [c, d]], sep = ["\n", " "])
writese
like aMat
to output. Rows of them are separated by LF, and columns are separated by a space, as specified in thesep
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:
read<T>()
(ortry_read<T>()
) reads a single sequence from input and converts it to a value ofT
.read_n<T>(n)
(ortry_read_n<T>(n)
) readsn
sequences from input and converts them to a value of Vec.read_m_n<T>(m, n)
(ortry_read_m_n<T>(m, n)
) readsm * n
sequences from input and converts them to a value ofMat<T>
.
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]
whereT
implements ReadInto trait;Box<[T; N]>
whereT
implements ReadInto trait.- Tuple types, e.g.,
(T1, T2, ..., Tn)
, whereTi
implements ReadInto trait andn
is neither 0 nor more than 12. - …
§ReadOneFrom
Some lower-level functions are provided to read a single data item from input:
-
read_one<T>()
(ortry_read_one<T>()
) reads a ASCII-whitespace-separated fragment of string (for char, it reads a single non-ASCII-whitespace character instead), and converts it to a value ofT
.The fragment is a string that does not contain any ASCII whitespace characters, and ASCII whitespace characters here are defined as
[' ', '\t', '\n', '\r']
.For example, given the input below:
1 2,3 3 4;sa
If you call
read_one<String>()
for 4 times, it will read 4 string fragments1
,2,3
,3
, and4;sa
. -
read_in_line_some_trimmed<T>()
(ortry_read_in_line_some_trimmed<T>()
) reads a non-empty line of string from input, trims the trailing newline, and converts it to a value ofT
.If all characters in remained part of current line are ASCII whitespace characters, it will discard current line and read one more line, otherwise it will return remained part of current line.
For example, given the input below:
1 2 3 4 5 6
If you call
read_in_line_some_trimmed<T>()
for 2 times, it will read1 2 3
and4 5 6
as two lines of string.And given the input below:
1 2 3
If you call
read_one<T>()
once andread_in_line_some_trimmed<T>()
once, they will read1
and2 3
respectively.If you call
read_in_line_some_trimmed<T>()
once more, it will panic because there is no non-empty line remained.Again, given the input below:
1 2 3 4 5 6
If you call
read_one<T>()
for 3 times andread_in_line_some_trimmed<T>()
for 1 time, they will read1
,2
,3
, and4 5 6
respectively. -
read_in_line_trimmed<T>()
(orread_in_line_trimmed<T>()
) reads the remained line from input regardless of whether it is empty or not, trims the trailing newline, and converts it to a value ofT
.For example, given the input below:
1 2 3 4 5 6
If you call
read_one<T>
for 3 times andread_in_line_trimmed<T>
for 1 time, they will read1
,2
,3
, and an empty string respectively.If you call
read_in_line_trimmed<T>
once more, it will still read an empty string. -
read_in_char<T>
(ortry_read_in_char<T>
) reads a single non-ASCII-whitespace character from input and converts it to a value ofT
.For example, given the input below:
1 2 3
If you call
read_in_char
for 3 times, it will read1
,2
, and3
as three characters. -
read_all<T>()
(ortry_read_all<T>()
) reads all remaining data items from input and converts them to a value of Vec. -
read_any_in_line<T>()
(ortry_read_any_in_line<T>()
) reads all data items in current line from input and converts them to a value of Vec. -
read_some_in_line<T>()
(ortry_read_some_in_line<T>()
) reads all data items in the next non-empty line from input and converts them to a value of Vec.
These functions are implemented for types that implement ReadOneFrom trait. Currently, the following types in std (or core) implement ReadOneFrom trait:
- String and ASCIIString;
- char and ASCIIChar (but it has different behavior from other types);
- u8, u16, u32, u64, u128, usize;
- i8, i16, i32, i64, i128, isize;
- f32, f64;
- bool;
- NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize;
- NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize;
- …
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:
- SepBy::sep_by(iter, sep) writes a sequence of data items from
iter
, which implements IntoIterator, to output with a separatorsep
. - sep_by!(iter, sep) writes a sequence of data items from
iter
, which implements IntoIterator, to output with a separatorsep
.
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:
- WriteInto::try_write() writes to standard output with default format.
- WriteInto::try_write_into() writes to given buffer that implements std::io::Write with default format.
- WriteInto::try_write_into_string() writes to a new string with default format.
The default format is defined as follows:
- For types that implement Display trait (but we only implement WriteInto for a part of types that implement Display), it uses Display::fmt method;
- For String, it writes the string as is;
- For char, it writes the character as is;
- For u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, it writes the value as is in decimal format;
- For
[T]
,[T; N]
and Vec whereT
implements WriteInto trait, it writes each item in the vector with a space as separator; - For Mat where
T
implements WriteInto trait, it writes each row in the matrix with a newline as separator, and writes each item in a row with a space as separator; - For all
&T
whereT
implements WriteInto trait, it writes the value as is.
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§
- ASCII
String - A string that only contains ASCII characters.
- Default
Separator - Use default separator.
- Input
Stream - C++-like Stream.
- Vec
- A contiguous growable array type, written as
Vec<T>
, short for ‘vector’.
Enums§
- ASCII
Char - An ASCII character. This is a subset of the Unicode character set.
- Read
Error - Error during using ReadInto or ReadOneFrom.
Traits§
- BufRead
Ext - Extension trait for BufRead.
- BufRead
ExtWith Format - Extension trait for BufReadExt with CharSet and Pattern.
- Read
From - Read data from input stream.
- Read
Into - The opposite of ReadFrom.
- Read
OneFrom - Read a single data item from input stream.
- Read
OneInto - The opposite of ReadOneFrom.
- SepBy
- Create a new object using given iterator and separator.
- Separators
- Separator by.
- Write
Into - 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 andn
columns. - Read
From Error - The error type for ReadFrom.
- Read
OneFrom Error - The error type for ReadOneFrom.