Open23

ちょこっとZig (Tips)

PG_WalkerPG_Walker

comptimeの使いどころn選

// $ zig version
// 0.12.0-dev.3435+091aa54a3

const std = @import("std");

// typeの引数
fn add(comptime T: type, a: T, b: T) T {
    return a + b;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // 変数の代入
    comptime var a: u8 = undefined;
    // assert
    a = 1;
    comptime std.debug.assert(@TypeOf(a) == u8);
    comptime std.debug.assert(a == 1);

    // 定数の代入
    const b = comptime a + 1;

    // 書式文字列はcomptime
    try stdout.print("a + b = {}\n", .{a + b}); // a + b = 3

    // ブロック
    comptime {
        var c: u8 = undefined;
        c = add(u8, a, b);
        // try stdout.print("a + b = {}\n", .{c}); // エラー
    }

    // struct内の変数
    const t = struct {
        comptime d: u8 = 1,
    };
    const ts = t{};
    try stdout.print("ts.d = {}\n", .{ts.d}); // ts.d = 1

    // OSごとの設定
    const builtin = @import("builtin");
    const os_name: ?[]const u8 = comptime switch (builtin.os.tag) {
        .linux => "Linux",
        .windows => "Windows",
        .macos => "MacOS",
        else => null,
    };
    try stdout.print("{?s}\n", .{os_name});
}
PG_WalkerPG_Walker

匿名関数(無名関数)の取得

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // structから関数のみを取得 - 匿名関数(無名関数)
    const anon_fn = struct {
        pub fn f(a: u8, b: []const i16) f16 {
            _ = .{ a, b };
            return 1.0;
        }
    }.f;

    _ = anon_fn(1, &[_]i16{1}); // エラー防止

    // anon_fnの型 - fn (u8, []const i16) f16
    try stdout.print("{}\n", .{@TypeOf(anon_fn)});
}
PG_WalkerPG_Walker

コマンドライン引数の取得

コマンドライン引数をすべて取得
const std = @import("std");
pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const alloc = std.heap.page_allocator;

    // コマンドライン引数をすべて取得(メモリはAllocatorで確保)
    const args = try std.process.argsAlloc(alloc);
    // 最後にメモリを解放
    defer std.process.argsFree(alloc, args);

    try stdout.print("args\n-----\n", .{});
    for (args) |arg| {
        try stdout.print("{s}\n", .{arg});
    }
}
コマンドライン引数をイテレータで1つずつ取得
const std = @import("std");
pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const alloc = std.heap.page_allocator;

    // コマンドライン引数を取得するイテレータ
    var itor = try std.process.argsWithAllocator(alloc);
    // 最後にイテレータを解放
    defer itor.deinit();

    try stdout.print("args\n-----\n", .{});

    // コマンドライン引数を1つずつ取得
    while (itor.next()) |arg| {
        try stdout.print("{s}\n", .{arg});
    }
}
コンパイルしてから実行
$ zig build-exe -O ReleaseSafe example_args.zig
$ ./example_args Hello Zig.
args
-----
./example_args
Hello
Zig.
ソースコードから実行
$ zig run example_args.zig -- Hello Zig.
args
-----
/path/to/.../example_args
Hello
Zig.
PG_WalkerPG_Walker

実行環境の情報を取得

  • Zigのバージョン、CPUアーキテクチャ、OS、ABI
実行環境の情報を取得
const std = @import("std");
const builtin = @import("builtin");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    try stdout.print("zig version: {s}\n", .{builtin.zig_version_string});
    try stdout.print("target cpu:  {s}\n", .{enumTagName(builtin.cpu.arch)});
    try stdout.print("target os:   {s}\n", .{enumTagName(builtin.os.tag)});
    try stdout.print("target abi:  {s}\n", .{enumTagName(builtin.abi)});
}

// enumのタグ名を文字列に変換
fn enumTagName(e: anytype) []const u8 {
    return std.enums.tagName(@TypeOf(e), e).?;
}
結果(実行環境により異なります)
zig version: 0.12.0-dev.3496+a2df84d0f
target cpu:  x86_64
target os:   linux
target abi:  gnu
PG_WalkerPG_Walker

ソート

ソート
const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    var a = [_]u8{ 3, 5, 2 };
    try stdout.print("a[0] = {}, a[1] = {}, a[2] = {}\n", .{ a[0], a[1], a[2] });

    // ソート用に変換
    const b: []u8 = &a;
    // 昇順にソート
    std.mem.sort(u8, b, {}, std.sort.asc(u8));
    try stdout.print("sort asc:  b[0] = {}, b[1] = {}, b[2] = {}\n", .{ b[0], b[1], b[2] });
    // 降順にソート
    std.mem.sort(u8, b, {}, std.sort.desc(u8));
    try stdout.print("sort desc: b[0] = {}, b[1] = {}, b[2] = {}\n", .{ b[0], b[1], b[2] });
}

// 結果
a[0] = 3, a[1] = 5, a[2] = 2
sort asc:  b[0] = 2, b[1] = 3, b[2] = 5
sort desc: b[0] = 5, b[1] = 3, b[2] = 2
PG_WalkerPG_Walker

型のつくり方(数値型) - @typeInfo, std.meta

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // 整数型(符号あり、符号なし)
    // std.builtin.Signednessはsignedが符号あり、unsignedが符号なし
    // std.meta.Int(Signedness, ビット数) で型をつくれる
    inline for (@typeInfo(std.builtin.Signedness).Enum.fields) |field| {
        try stdout.print("{s}\n", .{field.name});
        inline for (3..10) |n| {
            // 符号あり、符号なしの設定
            const signedness: std.builtin.Signedness = @enumFromInt(field.value);
            // 整数型をつくる
            const T = std.meta.Int(signedness, 1 << n);
            try stdout.print("{} ", .{T});
        }
        try stdout.print("\n", .{});
    }

    // 実数型
    // std.meta.Float(ビット数) で型を作れる
    // 対応するビット数は限られる
    try stdout.print("float\n", .{});
    inline for ([_]u8{ 16, 32, 64, 80, 128 }) |n| {
        // 実数型をつくる
        const T = std.meta.Float(n);
        try stdout.print("{} ", .{T});
    }
    try stdout.print("\n", .{});
}

// 結果
signed
i8 i16 i32 i64 i128 i256 i512
unsigned
u8 u16 u32 u64 u128 u256 u512
float
f16 f32 f64 f80 f128
PG_WalkerPG_Walker

子プロセスでコマンド実行(std.ChlidProcess)

// zig version: 0.11.0
const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // 実行するコマンド
    const result = try std.ChildProcess.exec(.{
        .argv = &[_][]const u8{ "echo", "-n", "Hello Zig!" },
        .allocator = std.heap.page_allocator
    });
    // 標準出力
    try stdout.print("{s}\n", .{ result.stdout });
    // 終了ステータス
    try stdout.print("$? = {}\n", .{ result.term.Exited });
}

// 結果
Hello Zig!
$? = 0
PG_WalkerPG_Walker

OS情報の取得(Linux)

/etc/os-releaseの内容を読み込み、一部を抜粋して出力

  • std.heap.GeneralPurposeAllocator
  • std.fs.openFileAbsolute
  • std.fs.File.readToEndAlloc
  • std.math.maxInt
  • std.hash_map.StringHashMap([]const u8)
  • std.mem.TokenIterator
  • std.mem.indexOf
/etc/os-releaseの内容(環境により異なる)
$ cat /etc/os-release
NAME="Fedora Linux"
VERSION="39 (Thirty Nine)"
ID=fedora
VERSION_ID=39
.....
.....
/etc/os-releaseの一部を抜粋
const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // メモリアロケータでリーク検出
    // https://ziglang.org/learn/samples/#memory-leak-detection
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    // OS情報の読み取り
    const file_name = "/etc/os-release";
    const file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only });
    const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
    defer allocator.free(content);
    defer file.close();

    // 情報格納用
    const string_map = std.hash_map.StringHashMap([]const u8);  // Generics
    var info_map = string_map.init(allocator);
    defer info_map.deinit();

    // 1行ずつ読み込み
    var iterator = std.mem.tokenize(u8, content, "\n");
    while (iterator.next()) |line| {
        // "="の前後で名称と値に分ける
        if (std.mem.indexOf(u8, line, "=")) |pos| {
            const name = line[0..pos];      // 名称
            const value = line[pos + 1..];  // 値
            try info_map.put(name, value);  // データを格納
            // try stdout.print("{s}: {s}\n", .{ name, value });
        }
    }

    // 取得した情報を参照 (fedora 39など)
    try stdout.print("{s} {s}\n", .{ info_map.get("ID").?, info_map.get("VERSION_ID").? });
}
PG_WalkerPG_Walker

gzipでの圧縮、解凍(展開)

//
// gzipでの圧縮、解凍(展開)
// zig test (ソースファイル名)
//
const std = @import("std");

const assert = std.debug.assert;
const allocator = std.testing.allocator;

// gzip圧縮
test "compress gzip" {
    // 原本ファイル
    const image_file = try std.fs.cwd().openFile("image_original.jpg", .{});
    // 圧縮ファイル(作成)
    const gzip_file = try std.fs.cwd().createFile("image_original.gz", .{});
    defer {
        image_file.close();
        gzip_file.close();
    }

    // gzipで圧縮
    try std.compress.gzip.compress(image_file.reader(), gzip_file.writer(), .{});
}

// gzip解凍(展開)
test "decompress gzip" {
    // gzip圧縮ファイル
    const gzip_file = try std.fs.cwd().openFile("image_original.gz", .{});
    // 解凍後ファイル(作成)
    const image_file = try std.fs.cwd().createFile("image_decompress.jpg", .{});
    defer {
        gzip_file.close();
        image_file.close();
    }

    // gzipで解凍
    try std.compress.gzip.decompress(gzip_file.reader(), image_file.writer());
}

// 原本ファイルと解凍後ファイルの比較
test "file equality" {
    // 原本ファイル
    const file_orig = try std.fs.cwd().openFile("image_original.jpg", .{});
    // 解凍後ファイル
    const file_deco = try std.fs.cwd().openFile("image_decompress.jpg", .{});
    defer {
        file_orig.close();
        file_deco.close();
    }

    // 両ファイルを読み込み
    const data_orig = try file_orig.readToEndAlloc(allocator, std.math.maxInt(usize));
    const data_deco = try file_deco.readToEndAlloc(allocator, std.math.maxInt(usize));
    defer {
        allocator.free(data_orig);
        allocator.free(data_deco);
    }

    // 両ファイルを比較
    assert(std.mem.eql(u8, data_orig, data_deco));
}
PG_WalkerPG_Walker

桁数の多い計算

Zigでは桁数の多い整数の計算もできます。このときの数値型はcomptime_intです。

fn fact(n: anytype) @TypeOf(n) {
    if (n < 1) return 1;
    return n * fact(n - 1);
}

pub fn main() !void {
    const std = @import("std");
    const stdout = std.io.getStdOut().writer();

    const n = 300;  // nはcomptime_int型
    try stdout.print("fact({}) = {}\n", .{ n, fact(n) });   // fact(n)もcomptime_int型
}
結果
fact(300) = 306057512216440636035370461297268629388588804173576999416776741259476533176716867465515291422477573349939147888701726368864263907759003154226842927906974559841225476930271954604008012215776252176854255965356903506788725264321896264299365204576448830388909753943489625436053225980776521270822437639449120128678675368305712293681943649956460498166450227716500185176546469340112226034729724066333258583506870150169794168850353752137554910289126407157154830282284937952636580145235233156936482233436799254594095276820608062232812387383880817049600000000000000000000000000000000000000000000000000000000000000000000000000
PG_WalkerPG_Walker

ZigでHTTPサーバ(std.http.Server, std.net.Address) - 0.12.0対応

httpserver_example.zig
const std = @import("std");

// URI判定用(^.....([\?/]|$))
fn eqlUriAlloc(allocator: std.mem.Allocator, uri: []const u8, target: []const u8) !bool {
    if (std.mem.eql(u8, uri, target)) return true;
    if (uri.len >= target.len) return false;

    const uri_s: []u8 = try allocator.alloc(u8, uri.len + 1);
    defer allocator.free(uri_s);
    std.mem.copyForwards(u8, uri_s, uri);

    for ([_]u8{ '?', '/' }) |c| { // '#'は自動で区切られるため不要
        uri_s[uri.len] = c;
        if (std.mem.startsWith(u8, target, uri_s)) return true;
    }
    return false;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    // サーバのアドレスとポート番号
    const port = 8088;
    const addr = try std.net.Address.parseIp4("127.0.0.1", port);

    // 受信バッファ
    var buff: [1024]u8 = undefined;

    // ネットワーク起動
    var net_server = try addr.listen(.{});
    try stdout.print("Server started ({}). Stop Ctrl + C...\n", .{port});

    accept: while (true) {
        const conn = try net_server.accept();
        defer conn.stream.close();

        // HTTPサーバ
        var http_server = std.http.Server.init(conn, &buff);
        while (http_server.state == .ready) {
            // リクエスト受信
            var request = http_server.receiveHead() catch |err| switch (err) {
                error.HttpConnectionClosing => continue :accept,
                else => return,
            };

            const method = request.head.method; // HTTPメソッド
            const target = request.head.target; // URI(パス)

            if (std.mem.eql(u8, target, "/")) {
                // レスポンス出力
                try request.respond("Hello Zig!", .{ .extra_headers = &.{
                    .{ .name = "content-type", .value = "text/plain;charset=UTF-8" },
                } });
                continue;
            }

            if (method == .POST and std.mem.eql(u8, target, "/echo")) {
                // リクエストボディ読み込み
                var reader = try request.reader();
                const body = try reader.readAllAlloc(allocator, 8192);
                defer allocator.free(body);

                // レスポンス出力(リクエストボディをそのまま)
                try request.respond(body, .{ .extra_headers = &.{
                    .{ .name = "content-type", .value = request.head.content_type.? },
                } });
                continue;
            }

            if (try eqlUriAlloc(allocator, "/html/hello", target)) {
                // HTMLの出力(ソースコードはUTF-8)
                try request.respond("<html><body>こんにちはZig</body></html>", .{ .extra_headers = &.{
                    .{ .name = "content-type", .value = "text/html;charset=UTF-8" },
                } });
                continue;
            }

            if (try eqlUriAlloc(allocator, "/json/hello", target)) {
                // JSONの出力(ソースコードはUTF-8)
                try request.respond("{\"message\":\"こんにちはZig\"}", .{ .extra_headers = &.{
                    .{ .name = "content-type", .value = "application/json;charset=UTF-8" },
                } });
                continue;
            }

            if (try eqlUriAlloc(allocator, "/redirect/hello", target)) {
                // /html/helloに転送
                try request.respond("", .{ .status = .found, .extra_headers = &.{
                    .{ .name = "location", .value = "/html/hello" },
                } });
                continue;
            }

            try request.respond("", .{ .status = .not_found });
        }
    }
}
結果
$ curl http://127.0.0.1:8088/
Hello Zig!

$ curl http://127.0.0.1:8088/html/hello
<html><body>こんにちはZig</body></html>

$ curl http://127.0.0.1:8088/json/hello/
{"message":"こんにちはZig"}

$ curl http://127.0.0.1:8088/html/hello?
<html><body>こんにちはZig</body></html>

$ curl http://127.0.0.1:8088/json/hello#
{"message":"こんにちはZig"}

$ curl -v -H 'Content-Type: application/json;charset=UTF-8' -d '{"message":"こんにちはZig"}' http://127.0.0.1:8088/echo
* processing: http://127.0.0.1:8088/echo
*   Trying 127.0.0.1:8088...
* Connected to 127.0.0.1 (127.0.0.1) port 8088
> POST /echo HTTP/1.1
> Host: 127.0.0.1:8088
> User-Agent: curl/8.2.1
> Accept: */*
> Content-Type: application/json;charset=UTF-8
> Content-Length: 32
>
< HTTP/1.1 200 OK
< content-length: 32
< content-type: application/json;charset=UTF-8
<
* Connection #0 to host 127.0.0.1 left intact
{"message":"こんにちはZig"}

$ curl -vL http://127.0.0.1:8088/redirect/hello
* processing: http://127.0.0.1:8088/redirect/hello
*   Trying 127.0.0.1:8088...
* Connected to 127.0.0.1 (127.0.0.1) port 8088
> GET /redirect/hello HTTP/1.1
> Host: 127.0.0.1:8088
> User-Agent: curl/8.2.1
> Accept: */*
>
< HTTP/1.1 302 Found
< content-length: 0
< location: /html/hello
<
* Connection #0 to host 127.0.0.1 left intact
* Issue another request to this URL: 'http://127.0.0.1:8088/html/hello'
* Found bundle for host: 0xa00016c20 [serially]
* Can not multiplex, even if we wanted to
* Re-using existing connection with host 127.0.0.1
> GET /html/hello HTTP/1.1
> Host: 127.0.0.1:8088
> User-Agent: curl/8.2.1
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 44
< content-type: text/html;charset=UTF-8
<
* Connection #0 to host 127.0.0.1 left intact
<html><body>こんにちはZig</body></html>

$ curl -I http://127.0.0.1:8088/html/hellogreet
HTTP/1.1 404 Not Found
content-length: 0

参考: https://github.com/ziglang/zig/blob/master/lib/std/http/test.zig

PG_WalkerPG_Walker

ZigでHTTPクライアント(std.http.Client, std.net.Address) - 0.12.0対応

httpclient_example.zig
const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    const location = "http://127.0.0.1:8088/echo";
    const uri = try std.Uri.parse(location);

    const content = "{\"message\":\"こんにちはZig\"}";

    // バッファ
    var server_header_buffer: [1024]u8 = undefined;

    // HTTPクライアント
    var client = std.http.Client{ .allocator = allocator };
    defer client.deinit();

    // 接続開始
    var req = try client.open(.POST, uri, .{ .server_header_buffer = &server_header_buffer, .extra_headers = &.{
        .{ .name = "content-type", .value = "application/json;charset=UTF-8" },
    } });
    defer req.deinit();
    req.transfer_encoding = .{ .content_length = content.len };

    // リクエスト送信
    try req.send();
    try req.writeAll(content);
    try req.finish();
    try req.wait();

    // ヘッダ用のHashMap
    const shm = std.hash_map.StringHashMap([]const u8);
    var hash_map = shm.init(allocator);
    defer hash_map.deinit();

    // ヘッダをHashMapに追加
    var it_header = req.response.iterateHeaders();
    while (it_header.next()) |header| {
        try hash_map.put(header.name, header.value);
    }

    // ヘッダの出力
    try stdout.print("Header:\n", .{});
    var it_key = hash_map.keyIterator();
    while (it_key.next()) |key| {
        try stdout.print("{s} = {s}\n", .{ key.*, hash_map.get(key.*).? });
    }

    // ボディの読み込み
    const body = try req.reader().readAllAlloc(allocator, std.math.maxInt(usize));
    defer allocator.free(body);

    // ボディの出力
    try stdout.print("Body:\n", .{});
    try stdout.print("{s}\n", .{body});
}
結果
$ zig run httpserver_example.zig &
Server started (8088). Stop Ctrl + C...

$ zig run httpclient_example.zig
Header:
content-length = 32
content-type = application/json;charset=UTF-8
Body:
{"message":"こんにちはZig"}
PG_WalkerPG_Walker

複数の出力先に同時出力

std.io.MultiWriterに出力先を登録すると、1度の書き込みで登録されたすべての出力先にデータが出力されます。MultiWriterはstd.io.multiWriter関数で生成できます。

//
// 複数の出力先に同時出力
// zig test ソースファイル名 で実行
//

const std = @import("std");

test "MultiWriter" {
    const assert = std.debug.assert;
    const print = std.debug.print;
    print("\n", .{});

    // textの内容をbuff1, buff2へ同時に出力
    const text = "こんにちはZig!";

    // 出力用バッファ
    var buff1: [20]u8 = [_]u8{0} ** 20;
    var buff2: [20]u8 = [_]u8{0} ** 20;

    // 出力先
    var stream1 = std.io.fixedBufferStream(&buff1);
    var stream2 = std.io.fixedBufferStream(&buff2);

    // std.io.MultiWriterによる同時出力
    var writer = std.io.multiWriter(.{ stream1.writer(), stream2.writer() });
    _ = try writer.write(text);

    // 出力された内容
    print("buff1: {s}\n", .{buff1});
    print("buff2: {s}\n", .{buff2});

    assert(std.mem.eql(u8, &buff1, &buff2));
}
Test [1/1] multiwriter_test.test.MultiWriter...
buff1: こんにちはZig!
buff2: こんにちはZig!
All 1 tests passed.
PG_WalkerPG_Walker

ファイルからJSON文字列を読み込んで型変換

ファイルからJSON形式の文字列を読み込み、定義済みの構造型(struct)に変換します。std.json.reader関数とstd.json.parseFromTokenSource関数で実行できます。

//
// ファイルからJSON文字列を読み込んで型変換
// zig test ソースファイル名
//

const std = @import("std");

const allocator = std.testing.allocator;
const print = std.debug.print;

// 変換する型
const Store = struct {
    name: []const u8,
    date: []const u8,
    items: []Item,
    total_amount: u16,
};

const Item = struct {
    name: []const u8,
    quantity: u8,
    unit_price: u32,
    total_price: u32,
};

test "file2struct" {
    const file_name = "JSONファイル名.json";

    // ファイルオープン(カレントディレクトリ内)
    var file = try std.fs.cwd().openFile(file_name, .{});
    defer file.close();

    // JSON文字列読み込み
    var json_reader = std.json.reader(allocator, file.reader());
    defer json_reader.deinit();

    // JSON文字列の解析とStore型への変換
    var parsed = try std.json.parseFromTokenSource(Store, allocator, &json_reader, .{});
    defer parsed.deinit();

    print("\n", .{});

    // Store型の値
    const store = parsed.value;
    print("store.name = {s}\n", .{store.name});
    for (store.items) |item| {
        print("item.name = {s}, item.total_price = {}\n", .{item.name, item.total_price});
    }
    print("store.total_amount = {}\n", .{store.total_amount});
}
JSONデータ(ファイルでは改行、空白なし)
{
  "name": "スーパーマーケット",
  "date": "2024-05-09",
  "items": [
    {
      "name": "りんご",
      "quantity": 3,
      "unit_price": 100,
      "total_price": 300
    },
    {
      "name": "牛乳",
      "quantity": 1,
      "unit_price": 150,
      "total_price": 150
    },
    {
      "name": "卵",
      "quantity": 6,
      "unit_price": 30,
      "total_price": 180
    },
    {
      "name": "パン",
      "quantity": 1,
      "unit_price": 120,
      "total_price": 120
    }
  ],
  "total_amount": 750
}
結果
store.name = スーパーマーケット
item.name = りんご, item.total_price = 300
item.name = 牛乳, item.total_price = 150
item.name = 卵, item.total_price = 180
item.name = パン, item.total_price = 120
store.total_amount = 750
PG_WalkerPG_Walker

疑似乱数の生成

ポイント

議事乱数の生成
const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // シード生成用
    var buff: [8]u8 = undefined;
    try std.posix.getrandom(&buff);

    // 疑似乱数発生器
    var prng = std.Random.DefaultPrng.init(@as(u64, @bitCast(buff)));
    const random = prng.random();

    // 生成した疑似乱数を表示
    for (0..5) |_| {
        for (0..5) |_| {
            try stdout.print("{d:3} ", .{random.intRangeAtMost(u16, 0, 1000)});
        }
        try stdout.print("\n", .{});
    }
}
結果(数値は実行するごとに異なる)
633 274 405 769 918
574 883 448 990 106
 43 991 208 128 978
536 688 668 152 376
790 416  99 820 720
PG_WalkerPG_Walker

日本語を含む文字列の文字数とバイト数(UTF-8)

日本語を含む文字列の文字数とバイト数(UTF-8)
//
// 日本語を含む文字列の文字数とバイト数(UTF-8)
// zig version: 0.13.0-dev.211+6a65561e3
// zig test ソースファイル名
//

const std = @import("std");
const print = std.debug.print;

test "UTF-8 Codepoint" {
    const s = "こんにちはZig!";

    // 文字列
    print("\ns = {s}\n", .{s});

    // バイト数
    print("s.len = {}\n", .{s.len});

    // 文字数
    print("std.unicode.utf8CountCodePoints(s) = {}\n", .{try std.unicode.utf8CountCodepoints(s)});
}
結果
Test [1/1] codepoint_test.test.UTF-8 Codepoint...
s = こんにちはZig!
s.len = 19
std.unicode.utf8CountCodePoints(s) = 9
All 1 tests passed.
PG_WalkerPG_Walker

環境変数PATHの値をパスごとに改行して表示

環境変数PATHの値を取得しパス区切り文字を改行に変換して表示

環境変数の値を取得
const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    const stdout = std.io.getStdOut().writer();

    const path = try std.process.getEnvVarOwned(allocator, "PATH");
    defer allocator.free(path);

    // print("PATH={s}\n", .{path});
    for (path, 0..) |c, i| {
        if (c == std.fs.path.delimiter) path[i] = '\n';
    }
    try stdout.print("{s}\n", .{path});
}
結果(実行環境により異なる)
/usr/local/bin
/usr/bin
/bin
/usr/local/sbin
/usr/sbin
/sbin
PG_WalkerPG_Walker

zipファイルエントリ一覧表示

一時ディレクトリを使用していることに注意

*.zipエントリ(容量, ファイル名)一覧表示
// *.zipエントリ(容量, ファイル名)一覧表示
// *** 一時ディレクトリ作成に注意 ***
// zig test ソースファイル名
test "zip_entries" {
    const print = std.debug.print;
    const allocator = std.testing.allocator;
    print("\n", .{});

    // 読み込むファイル
    const dir_name = "zipファイルのディレクトリ";
    const file_name = "zig-0.12.0.zip";

    // ファイルオープン(std.fs.openFileAbsoluteも可)
    var dir = try std.fs.openDirAbsolute(dir_name, .{});
    defer dir.close();
    var file = try dir.openFile(file_name, .{});
    defer file.close();

    // 展開用一時ディレクトリ
    var tmpDir = std.testing.tmpDir(.{});
    defer tmpDir.cleanup();

    // ファイル名用バッファ
    var filename_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;

    // ファイル名展開用
    var diagnostics = std.zip.Diagnostics{ .allocator = allocator };
    defer diagnostics.deinit();

    // エントリ展開用イテレータ
    const stream = file.seekableStream();
    const zip_iter = std.zip.Iterator(@TypeOf(stream));
    var iter = try zip_iter.init(stream);

    // zipエントリ(容量, ファイル名)一覧表示
    while (try iter.next()) |entry| {
        const crc32 = try entry.extract(stream, .{ .diagnostics = &diagnostics }, &filename_buf, tmpDir.dir);
        if (crc32 != entry.crc32) continue;
        print("{d:10} {s}\n", .{ entry.uncompressed_size, filename_buf[0..entry.filename_len] });
    }
}
結果(抜粋)
         0 zig-0.12.0/
    .....................
     41086 zig-0.12.0/CMakeLists.txt
      1080 zig-0.12.0/LICENSE
      5611 zig-0.12.0/README.md
      5655 zig-0.12.0/bootstrap.c
     48503 zig-0.12.0/build.zig
       390 zig-0.12.0/build.zig.zon
    .....................
         0 zig-0.12.0/doc/
      1920 zig-0.12.0/doc/build.zig.zon.md
    465363 zig-0.12.0/doc/langref.html.in
    .....................
    (以下続く)
PG_WalkerPG_Walker

*.tar.gzファイルエントリ一覧表示

// *.tar.gzエントリ(容量, ファイル名)一覧表示
// zig test ソースファイル名
test "tar_gz_entries" {
    const print = std.debug.print;
    const allocator = std.testing.allocator;
    print("\n", .{});

    // *.tar.gzのエントリ(容量, ファイル名)一覧表示
    // 読み込むファイル
    const dir_name = "ディレクトリ名";
    const file_name = "zig-0.12.0.tar.gz";

    // ファイルオープン
    var dir = try std.fs.openDirAbsolute(dir_name, .{});
    defer dir.close();
    var file = try dir.openFile(file_name, .{});
    defer file.close();

    // gzip解凍後データ読み込み用バッファ
    var content = std.ArrayList(u8).init(allocator);
    defer content.deinit();

    // gzip解凍
    try std.compress.gzip.decompress(file.reader(), content.writer());

    // tar読み込み用
    var buff = std.io.fixedBufferStream(content.items);

    // tar読み込み
    var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    var iter = std.tar.iterator(buff.reader(), .{
        .file_name_buffer = &file_name_buffer,
        .link_name_buffer = &link_name_buffer,
    });

    // エントリ表示(容量, ファイル名)
    while (try iter.next()) |item| {
        print("{d:10} {s}\n", .{ item.size, item.name });
    }
}
結果(抜粋)
         0 zig-0.12.0/
.....................
     41086 zig-0.12.0/CMakeLists.txt
      1080 zig-0.12.0/LICENSE
      5611 zig-0.12.0/README.md
      5655 zig-0.12.0/bootstrap.c
     48503 zig-0.12.0/build.zig
       390 zig-0.12.0/build.zig.zon
.....................
         0 zig-0.12.0/doc/
      1920 zig-0.12.0/doc/build.zig.zon.md
    465363 zig-0.12.0/doc/langref.html.in
.....................
(以下続く)
PG_WalkerPG_Walker

*.tar.xzファイルエントリ一覧表示

ファイルの容量が大きいと処理に時間がかかる可能性がある

*.tar.xzエントリ一覧
// *.tar.xzエントリ(容量, ファイル名)一覧表示
// zig test ソースファイル名
test "tar_xz_entries" {
    const allocator = std.testing.allocator;
    const print = std.debug.print;

    print("\n", .{});

    // 読み込むファイル
    const file_path = "/path/to/zig-0.12.0.tar.xz";
    // ファイルオープン
    const file = try std.fs.openFileAbsolute(file_path, .{});
    defer file.close();

    // xz展開
    var decompress = try std.compress.xz.decompress(allocator, file.reader());
    defer decompress.deinit();

    // tar展開用
    var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    // エントリ用イテレータ
    var iter = std.tar.iterator(decompress.reader(), .{
        .file_name_buffer = &file_name_buffer,
        .link_name_buffer = &link_name_buffer,
    });

    // エントリ(容量, ファイル名)一覧表示
    while (try iter.next()) |entry| {
        print("{d:10} {s}\n", .{ entry.size, entry.name });
    }
}
結果(抜粋)
         0 zig-0.12.0/
.....................
     41086 zig-0.12.0/CMakeLists.txt
      1080 zig-0.12.0/LICENSE
      5611 zig-0.12.0/README.md
      5655 zig-0.12.0/bootstrap.c
     48503 zig-0.12.0/build.zig
       390 zig-0.12.0/build.zig.zon
.....................
         0 zig-0.12.0/doc/
      1920 zig-0.12.0/doc/build.zig.zon.md
    465363 zig-0.12.0/doc/langref.html.in
.....................
(以下続く)
PG_WalkerPG_Walker

*.zipファイルの展開

Zipファイルの展開
// Zipファイルの展開
// zig test ソースファイル名
test "zip_extract" {
    // 読み込むZipファイルのフルパス
    const file_path = "/path/to/zig-0.12.0.zip";
    // 展開するディレクトリのフルパス(作成済みとする)
    const dir_path = "/path/to/dest_dir";

    // Zipファイルのオープン
    const file = try std.fs.openFileAbsolute(file_path, .{});
    defer file.close();

    // 読み込み用ストリーム
    const stream = file.seekableStream();

    // 展開するディレクトリをオープン
    var dir = try std.fs.openDirAbsolute(dir_path, .{});
    defer dir.close();

    // Zipファイルを展開
    try std.zip.extract(dir, stream, .{});
}
PG_WalkerPG_Walker

*.tar.gzファイルの展開

*tar.gzファイルの展開
// *.tar.gzファイルの展開
// zig test ソースファイル名
test "tar_gz_extract" {
    const allocator = std.testing.allocator;

    // *.tar.gzのエントリ(容量, ファイル名)一覧表示
    // 読み込むファイル
    const file_path = "/path/to/zig-0.12.0.tar.gz";

    // 展開先のディレクトリ(作成済みであること)
    const dir_path = "/path/to/dest_dir";

    // ファイルオープン
    var file = try std.fs.openFileAbsolute(file_path, .{});
    defer file.close();

    // 展開先ディレクトリのオープン
    var dest_dir = try std.fs.openDirAbsolute(dir_path, .{});
    defer dest_dir.close();

    // gzip解凍後データ読み込み用バッファ
    var content = std.ArrayList(u8).init(allocator);
    defer content.deinit();

    // gzip解凍
    try std.compress.gzip.decompress(file.reader(), content.writer());

    // tar読み込み用
    var buff = std.io.fixedBufferStream(content.items);

    // tar展開
    try std.tar.pipeToFileSystem(dest_dir, buff.reader(), .{});
}