0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rust勉強中 - その24 -> I/O

Last updated at Posted at 2019-11-02

自己紹介

出田 守と申します。
しがないPythonプログラマです。
情報セキュリティに興味があり現在勉強中です。CTFやバグバウンティなどで腕を磨いています。主に低レイヤの技術が好きで、そっちばかり目が行きがちです。

Rustを勉強していくうえで、読んで学び、手を動かし、記録し、楽しく学んでいけたらと思います。

環境

新しい言語を学ぶということで、普段使わないWindowsとVimという新しい開発環境で行っています。
OS: Windows10 Home 64bit 1903
CPU: Intel Core i5-3470 @ 3.20GHz
Rust: 1.38.0
RAM: 8.00GB
Editor: Vim 8.1.1
Terminal: PowerShell

前回

前回はテキスト処理と正規表現について学びました。
Rust勉強中 - その23

I/O

std::ioには、RustのI/O操作に必要なRead, BufRead, Write, Seekトレイトや関数がまとめられています。
あらゆるI/Oに関する関数やメソッドの引数にreaderやwriterを渡すものがあります。

  • readerはバイト列を読み出すことができる値です。
  • writerはバイト列を書き出すことができる値です。

この値にはVecやファイルストリーム、ネットワークストリームなどがあります。

Readトレイト

Readトレイトは以下のように定義されています。

pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

    fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> Result<usize> { ... }
    unsafe fn initializer(&self) -> Initializer { ... }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { ... }
    fn by_ref(&mut self) -> &mut Self
    where
        Self: Sized,
    { ... }
    fn bytes(self) -> Bytes<Self>
    where
        Self: Sized,
    { ... }
    fn chain<R: Read>(self, next: R) -> Chain<Self, R>
    where
        Self: Sized,
    { ... }
    fn take(self, limit: u64) -> Take<Self>
    where
        Self: Sized,
    { ... }
}

readメソッドは指定されたバッファのデータソースからbyte単位で読み込みます。データがあってもbufferの長さまでしか読み込みません。読み込み時何らかのエラーが発生した場合、io::Errorを返します。さらにio::Errorはkindメソッドを使うことで、ErrorKind列挙型の値を返すことができます。ErrorKind::Interrupted値は、特にこれについて何かすることがなければ処理を再試行しても良いようです。
readメソッドの他に、バイト列を全て読み出すread_to_endメソッド、指定したバイト長を読み出すtakeメソッド、バイトのイテレータを返すbytesメソッドなどがあります。

参照:
ドキュメント - Read

BufReadトレイト

pub trait BufRead: Read {
    fn fill_buf(&mut self) -> Result<&[u8]>;
    fn consume(&mut self, amt: usize);

    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_line(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn split(self, byte: u8) -> Split<Self>
    where
        Self: Sized,
    { ... }
    fn lines(self) -> Lines<Self>
    where
        Self: Sized,
    { ... }
}

BufReadトレイトは、readerがバッファリングを利用してシステムコール呼び出しの回数を減らすことでオーバヘッドを減らすことができます。
BufReadトレイトが実装するメソッドに加え、Readトレイトを継承しているため、Readトレイトが実装するメソッドを使用することができます。
fill_bufメソッドはバイト列を読み出します。ただし、呼び出し後にreadメソッドを呼び出すと同じバイト列を読み出す可能性があるのでconsumeメソッドを使ってバイトを消費する必要があります。
read_lineやlinesは行単位で読み出すことができるメソッドです。read_lineは改行を含む行をバイト列で読み出し、linesは改行を除く行のバイト列のイテレータを返します。
以下はドキュメントの例にあったfill_bufとconsumeの使用例と、read_lineの例です。

use std::io;
use std::io::prelude::*;

fn input_line() -> io::Result<String> {
    let stdin    = io::stdin();
    let mut line = String::new();
    stdin.lock().read_line(&mut line)?;
    Ok(line)
}

fn main() {
    {
        // fill_buf, consume
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let buffer = stdin.fill_buf().unwrap();
        println!("{:?}", buffer);
        let length = buffer.len();
        stdin.consume(length);
    }
    // read_line
    println!("{:?}", input_line());
}
aaa
[97, 97, 97, 13, 10]
aaa
Ok("aaa\r\n")

linesメソッドなどはResultを返すためエラー処理をする必要があります。そのためcollectメソッドなどのアダプタメソッドを使うときは以下のようにすると良いそうです。

fn main() {
    ...
    // collect
    use std::fs::File;
    let fd = File::open("test.txt").unwrap();
    let reader = io::BufReader::new(fd);
    // reader.lines().collect(); // Error
    let _lines: Vec<String> = reader.lines().collect::<io::Result<Vec<String>>>().unwrap(); // ok
}

これはResultのFromIterator実装のおかげでこのように書けるようです。以下がその実装です。

impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
    ...
}

コレクション型をもつResult型はその値でのイテレータを作り出すことができるという実装です。(たぶん)

参照:
ドキュメント - BufRead

Writeトレイト

pub trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize>;
    fn flush(&mut self) -> Result<()>;

    fn write_vectored(&mut self, bufs: &[IoSlice]) -> Result<usize> { ... }
    fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... }
    fn write_fmt(&mut self, fmt: Arguments) -> Result<()> { ... }
    fn by_ref(&mut self) -> &mut Self
    where
        Self: Sized,
    { ... }
}

Writeトレイトは書き出し用のトレイトです。
writeメソッドはバッファのバイト列の一部をwriterに書き出します。flushメソッドはバッファされているデータをwriterに書き出します。
BufWriterのnewを使えば任意のwriterをバッファリングできます。BufWriterをドロップする前にはflushしておくと良いそうです。

参照:
ドキュメント - Write

std::fs

File::open

openメソッドは、読込み専用としてファイルをオープンします。

fn main() {
    ...
    // open
    let fd = File::open("test.txt").unwrap();
}

File::create

createメソッドは、書出し専用としてファイルを新しくオープンします。もし、既にファイルが存在する場合、上書きします。

fn main() {
    ...
    // create
    let fd = File::create("test2.txt").unwrap();
}

OpenOptions

OpenOptions型のメソッドを利用することで、ファイルオープン時のフラグを細かく設定できます。
例えば、以下はファイルを追記でオープンする例です。

fn main() {
    ...
    // OpenOptions append
    use std::fs::OpenOptions;
    let fd = OpenOptions::new()
        .append(true)
        .open("test.txt").unwrap();
}

参照:
ドキュメント - OpenOptions

今回はここまで!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?