Zig: 1バイトごとに読み書きするだけのcatコマンドを書いてみた

Zig(ziglang) で標準入力から1バイト読んで標準出力に書くのを繰り返すだけの素朴な cat コマンドを書いてみました。

Zig はさっき触り始めたばかりで右も左も分からない状態です。


<追記 2022-07-17>

v0.8.0 で InStream, OutStream がそれぞれ reader, writer という名前に変わりました。

Proposal: Rename InStream and OutStream · Issue #4917 · ziglang/zig

この変更にあわせて、下記のように修正する必要があります。v0.9.1 でコンパイル+実行できることを確認しました。

- const outstream = std.io.getStdOut().outStream();
+ const outstream = std.io.getStdOut().writer();

- const instream = std.io.getStdIn().inStream();
+ const instream = std.io.getStdIn().reader();

<追記おわり>


const std = @import("std");

pub fn main() !void {
    const outstream = std.io.getStdOut().outStream();
    const instream = std.io.getStdIn().inStream();

    while (true) {
        const byte = instream.readByte() catch |err| switch (err) {
            error.EndOfStream => {
                break;
            },
            else => |e| {
                return e;
            },
        };

        try outstream.writeByte(byte);
    }
}

実行。

$ head -5 cat.zig | zig-0.6.0 run cat.zig | cat -A
const std = @import("std");$
$
pub fn main() !void {$
    const outstream = std.io.getStdOut().outStream();$
    const instream = std.io.getStdIn().inStream();$

リファレンスの探し方がよく分かってなくて GitHub でソースとコメントを見た方が速かった。

zig/in_stream.zig at 0.6.0 · ziglang/zig
https://github.com/ziglang/zig/blob/0.6.0/lib/std/io/in_stream.zig


0.6.0 では in_stream.zig だったのが master(現在は 4fbf9f7f79c8e0df)では reader.zig に変わっていたりします。

2020-09-21 追記

関数呼び出しや条件分岐などの要素が加わったサンプルとして、 CR, LF, タブ, EOF を分かりやすく表示するバージョンを書いてみました。 入力が UTF-8 である想定です。

const std = @import("std");
const outstream = std.io.getStdOut().outStream();

fn printByte(byte: u8) !void {
    if (byte == '\t') {
        try outstream.print("<TAB>", .{});
    } else if (byte == '\r') {
        try outstream.print("<CR>", .{});
    } else if (byte == '\n') {
        try outstream.print("<LF>", .{});
        try outstream.writeByte(byte);
    } else {
        try outstream.writeByte(byte);
    }
}

pub fn main() !void {
    const instream = std.io.getStdIn().inStream();

    while (true) {
        const byte = instream.readByte() catch |err| switch (err) {
            error.EndOfStream => {
                break;
            },
            else => |e| {
                return e;
            },
        };

        try printByte(byte);
    }

    try outstream.print("<EOF>\n", .{});
}

実行例:

$ export PS1='--------\n\n$ '
--------

$ cat sample.txt 
ab      cd
あいうえお
0123--------

$ cat -A sample.txt 
ab^Icd^M$
M-cM-^AM-^BM-cM-^AM-^DM-cM-^AM-^FM-cM-^AM-^HM-cM-^AM-^J^M$
0123--------

$ cat sample.txt | zig-0.6.0 run cat_v2.zig 
ab<TAB>cd<CR><LF>
あいうえお<CR><LF>
0123<EOF>
--------

この記事を読んだ人は(ひょっとしたら)こちらも読んでいます

cat コマンドを改造して育てていくとコンパイラが作れます。

zenn.dev