第18章: 標準ライブラリ

学習目標

この章を終えると、以下ができるようになります:

  • std.mem でメモリ操作とアロケータを使いこなす
  • std.fs でファイルシステムを操作する
  • std.json でJSON解析と生成を行う
  • std.fmt でフォーマット出力を活用する
  • std.log でロギングシステムを構築する
  • 標準ライブラリの概要

    Zigの標準ライブラリは、システムプログラミングに必要な機能を提供します:

    std
    ├── mem      メモリ管理
    ├── fs       ファイルシステム
    ├── json     JSON処理
    ├── fmt      フォーマット
    ├── log      ロギング
    ├── http     HTTP(実験的)
    ├── crypto   暗号化
    ├── Thread   スレッド
    ├── process  プロセス
    └── ...      その他多数
    

    std.mem - メモリ管理

    アロケータの種類

    const std = @import("std");
    
    pub fn allocatorTypes() !void {
        // 1. ページアロケータ(シンプル、デバッグ向き)
        const page_allocator = std.heap.page_allocator;
    
        // 2. 汎用アロケータ(本番環境向き)
        var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        defer _ = gpa.deinit();
        const gpa_allocator = gpa.allocator();
    
        // 3. アリーナアロケータ(一括解放)
        var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
        defer arena.deinit();
        const arena_allocator = arena.allocator();
    
        // 4. 固定バッファアロケータ
        var buffer: [1024]u8 = undefined;
        var fba = std.heap.FixedBufferAllocator.init(&buffer);
        const fba_allocator = fba.allocator();
    
        _ = page_allocator;
        _ = gpa_allocator;
        _ = arena_allocator;
        _ = fba_allocator;
    }
    

    メモリ操作

    const std = @import("std");
    
    pub fn memoryOperations() !void {
        const allocator = std.heap.page_allocator;
    
        // メモリコピー
        const src = "Hello, World!";
        const dst = try allocator.alloc(u8, src.len);
        defer allocator.free(dst);
        @memcpy(dst, src);
    
        // メモリ比較
        const equal = std.mem.eql(u8, src, dst);
        std.debug.print("Equal: {}\n", .{equal});
    
        // メモリ設定
        var buffer: [10]u8 = undefined;
        @memset(&buffer, 0);
    
        // メモリ検索
        const needle = "World";
        if (std.mem.indexOf(u8, src, needle)) |index| {
            std.debug.print("Found at: {}\n", .{index});
        }
    }
    
    pub fn sliceOperations() void {
        const data = [_]u8{ 1, 2, 3, 4, 5 };
    
        // スライス
        const slice = data[1..4]; // { 2, 3, 4 }
    
        // 連結
        const a = "Hello, ";
        const b = "World!";
        const allocator = std.heap.page_allocator;
        const result = std.mem.concat(allocator, u8, &[_][]const u8{ a, b }) catch return;
        defer allocator.free(result);
    
        std.debug.print("Concatenated: {s}\n", .{result});
    }
    
    pub fn stringOperations() void {
        const str = "Hello, World!";
    
        // 分割
        var it = std.mem.split(u8, str, ", ");
        while (it.next()) |part| {
            std.debug.print("Part: {s}\n", .{part});
        }
    
        // トリム
        const padded = "  Hello  ";
        const trimmed = std.mem.trim(u8, padded, " ");
        std.debug.print("Trimmed: '{s}'\n", .{trimmed});
    
        // 置換
        const allocator = std.heap.page_allocator;
        const replaced = std.mem.replaceOwned(
            u8,
            allocator,
            str,
            "World",
            "Zig",
        ) catch return;
        defer allocator.free(replaced);
    
        std.debug.print("Replaced: {s}\n", .{replaced});
    }
    

    std.fs - ファイルシステム

    ファイル操作

    const std = @import("std");
    
    pub fn fileOperations() !void {
        const allocator = std.heap.page_allocator;
    
        // ファイルを開く
        const file = try std.fs.cwd().createFile("test.txt", .{});
        defer file.close();
    
        // 書き込み
        try file.writeAll("Hello, Zig!\n");
    
        // 読み込み
        const read_file = try std.fs.cwd().openFile("test.txt", .{});
        defer read_file.close();
    
        const content = try read_file.readToEndAlloc(allocator, 1024 * 1024);
        defer allocator.free(content);
    
        std.debug.print("Content: {s}\n", .{content});
    }
    
    pub fn fileInfo() !void {
        // ファイル情報を取得
        const stat = try std.fs.cwd().statFile("test.txt");
    
        std.debug.print("Size: {} bytes\n", .{stat.size});
        std.debug.print("Kind: {}\n", .{stat.kind});
    }
    
    pub fn workingWithPaths() !void {
        const allocator = std.heap.page_allocator;
    
        // パスの結合
        const path = try std.fs.path.join(allocator, &[_][]const u8{ "dir", "subdir", "file.txt" });
        defer allocator.free(path);
    
        std.debug.print("Path: {s}\n", .{path});
    
        // ベース名とディレクトリ名
        const basename = std.fs.path.basename(path);
        const dirname = std.fs.path.dirname(path);
    
        std.debug.print("Basename: {s}\n", .{basename});
        std.debug.print("Dirname: {s}\n", .{dirname orelse "none"});
    }
    

    ディレクトリ操作

    const std = @import("std");
    
    pub fn directoryOperations() !void {
        // ディレクトリ作成
        try std.fs.cwd().makeDir("test_dir");
    
        // ディレクトリの存在確認
        std.fs.cwd().access("test_dir", .{}) catch |err| {
            std.debug.print("Directory not found: {}\n", .{err});
            return;
        };
    
        // ディレクトリ削除
        try std.fs.cwd().deleteDir("test_dir");
    }
    
    pub fn iterateDirectory() !void {
        // ディレクトリの内容を列挙
        var dir = try std.fs.cwd().openDir(".", .{ .iterate = true });
        defer dir.close();
    
        var it = dir.iterate();
        while (try it.next()) |entry| {
            std.debug.print("{s} ({s})\n", .{ entry.name, @tagName(entry.kind) });
        }
    }
    
    pub fn walkDirectory() !void {
        const allocator = std.heap.page_allocator;
    
        // ディレクトリを再帰的に探索
        var dir = try std.fs.cwd().openDir(".", .{ .iterate = true });
        defer dir.close();
    
        var walker = try dir.walk(allocator);
        defer walker.deinit();
    
        while (try walker.next()) |entry| {
            std.debug.print("{s} ({s})\n", .{ entry.path, @tagName(entry.kind) });
        }
    }
    

    std.json - JSON処理

    JSON解析

    const std = @import("std");
    
    pub const User = struct {
        name: []const u8,
        age: u32,
        email: []const u8,
    };
    
    pub fn parseJSON() !void {
        const allocator = std.heap.page_allocator;
    
        const json_str =
            \\{
            \\  "name": "Alice",
            \\  "age": 30,
            \\  "email": "alice@example.com"
            \\}
        ;
    
        const parsed = try std.json.parseFromSlice(User, allocator, json_str, .{});
        defer parsed.deinit();
    
        const user = parsed.value;
        std.debug.print("Name: {s}\n", .{user.name});
        std.debug.print("Age: {}\n", .{user.age});
        std.debug.print("Email: {s}\n", .{user.email});
    }
    
    pub const Config = struct {
        host: []const u8,
        port: u16,
        debug: bool,
        tags: []const []const u8,
    };
    
    pub fn parseComplexJSON() !void {
        const allocator = std.heap.page_allocator;
    
        const json_str =
            \\{
            \\  "host": "localhost",
            \\  "port": 8080,
            \\  "debug": true,
            \\  "tags": ["web", "api", "v1"]
            \\}
        ;
    
        const parsed = try std.json.parseFromSlice(Config, allocator, json_str, .{});
        defer parsed.deinit();
    
        const config = parsed.value;
        std.debug.print("Host: {s}\n", .{config.host});
        std.debug.print("Port: {}\n", .{config.port});
        std.debug.print("Debug: {}\n", .{config.debug});
    
        for (config.tags) |tag| {
            std.debug.print("Tag: {s}\n", .{tag});
        }
    }
    

    JSON生成

    const std = @import("std");
    
    pub fn generateJSON() !void {
        const allocator = std.heap.page_allocator;
    
        var buffer = std.ArrayList(u8).init(allocator);
        defer buffer.deinit();
    
        var writer = buffer.writer();
    
        const user = User{
            .name = "Bob",
            .age = 25,
            .email = "bob@example.com",
        };
    
        try std.json.stringify(user, .{}, writer);
    
        std.debug.print("JSON: {s}\n", .{buffer.items});
    }
    
    pub fn generatePrettyJSON() !void {
        const allocator = std.heap.page_allocator;
    
        var buffer = std.ArrayList(u8).init(allocator);
        defer buffer.deinit();
    
        var writer = buffer.writer();
    
        const user = User{
            .name = "Charlie",
            .age = 35,
            .email = "charlie@example.com",
        };
    
        try std.json.stringify(user, .{ .whitespace = .indent_2 }, writer);
    
        std.debug.print("Pretty JSON:\n{s}\n", .{buffer.items});
    }
    

    std.fmt - フォーマット

    基本的なフォーマット

    const std = @import("std");
    
    pub fn basicFormatting() !void {
        const allocator = std.heap.page_allocator;
    
        // 文字列のフォーマット
        const str = try std.fmt.allocPrint(allocator, "Hello, {s}!", .{"World"});
        defer allocator.free(str);
    
        std.debug.print("{s}\n", .{str});
    
        // 数値のフォーマット
        const num_str = try std.fmt.allocPrint(allocator, "Number: {}", .{42});
        defer allocator.free(num_str);
    
        std.debug.print("{s}\n", .{num_str});
    }
    
    pub fn advancedFormatting() void {
        // 整数
        std.debug.print("Decimal: {}\n", .{255});
        std.debug.print("Hex: {x}\n", .{255});
        std.debug.print("Binary: {b}\n", .{255});
        std.debug.print("Octal: {o}\n", .{255});
    
        // 浮動小数点
        std.debug.print("Float: {d}\n", .{3.14159});
        std.debug.print("Fixed: {d:.2}\n", .{3.14159});
        std.debug.print("Scientific: {e}\n", .{1234.5});
    
        // パディング
        std.debug.print("Padded: '{:10}'\n", .{42});
        std.debug.print("Zero-padded: '{:0>10}'\n", .{42});
    }
    
    pub fn customFormatting() !void {
        const Point = struct {
            x: f32,
            y: f32,
    
            pub fn format(
                self: @This(),
                comptime fmt: []const u8,
                options: std.fmt.FormatOptions,
                writer: anytype,
            ) !void {
                _ = fmt;
                _ = options;
                try writer.print("({d:.2}, {d:.2})", .{ self.x, self.y });
            }
        };
    
        const p = Point{ .x = 1.5, .y = 2.7 };
        std.debug.print("Point: {}\n", .{p});
    }
    

    バッファ操作

    const std = @import("std");
    
    pub fn bufferFormatting() !void {
        var buffer: [100]u8 = undefined;
    
        // 固定バッファへのフォーマット
        const str = try std.fmt.bufPrint(&buffer, "Value: {}", .{42});
    
        std.debug.print("Formatted: {s}\n", .{str});
    }
    
    pub fn streamFormatting() !void {
        const allocator = std.heap.page_allocator;
    
        var buffer = std.ArrayList(u8).init(allocator);
        defer buffer.deinit();
    
        var writer = buffer.writer();
    
        try writer.print("Line 1\n", .{});
        try writer.print("Line 2\n", .{});
        try writer.print("Value: {}\n", .{42});
    
        std.debug.print("Buffer:\n{s}", .{buffer.items});
    }
    

    std.log - ロギング

    基本的なロギング

    const std = @import("std");
    
    pub fn basicLogging() void {
        std.log.debug("Debug message", .{});
        std.log.info("Info message", .{});
        std.log.warn("Warning message", .{});
        std.log.err("Error message", .{});
    }
    
    pub fn loggingWithData() void {
        const user_id = 12345;
        const username = "alice";
    
        std.log.info("User logged in: id={}, name={s}", .{ user_id, username });
    
        const duration = 1.23;
        std.log.debug("Request completed in {d:.3}s", .{duration});
    }
    

    カスタムロガー

    const std = @import("std");
    
    pub const CustomLogger = struct {
        level: std.log.Level,
    
        pub fn log(
            self: CustomLogger,
            comptime level: std.log.Level,
            comptime scope: @TypeOf(.EnumLiteral),
            comptime format: []const u8,
            args: anytype,
        ) void {
            if (@intFromEnum(level) < @intFromEnum(self.level)) {
                return;
            }
    
            const level_str = switch (level) {
                .debug => "DEBUG",
                .info => "INFO",
                .warn => "WARN",
                .err => "ERROR",
            };
    
            const scope_str = @tagName(scope);
    
            std.debug.print("[{s}] [{s}] ", .{ level_str, scope_str });
            std.debug.print(format, args);
            std.debug.print("\n", .{});
        }
    };
    
    pub fn customLogging() void {
        var logger = CustomLogger{ .level = .info };
    
        logger.log(.debug, .app, "This won't be shown", .{});
        logger.log(.info, .app, "Application started", .{});
        logger.log(.warn, .network, "Connection slow", .{});
        logger.log(.err, .database, "Query failed", .{});
    }
    

    実践例: 設定ファイルマネージャー

    const std = @import("std");
    
    pub const Config = struct {
        server: ServerConfig,
        database: DatabaseConfig,
        logging: LoggingConfig,
    
        pub const ServerConfig = struct {
            host: []const u8,
            port: u16,
            workers: u32,
        };
    
        pub const DatabaseConfig = struct {
            url: []const u8,
            max_connections: u32,
        };
    
        pub const LoggingConfig = struct {
            level: []const u8,
            file: ?[]const u8,
        };
    
        pub fn loadFromFile(allocator: std.mem.Allocator, path: []const u8) !Config {
            // ファイルを読み込む
            const file = try std.fs.cwd().openFile(path, .{});
            defer file.close();
    
            const content = try file.readToEndAlloc(allocator, 1024 * 1024);
            defer allocator.free(content);
    
            // JSONをパース
            const parsed = try std.json.parseFromSlice(Config, allocator, content, .{});
            defer parsed.deinit();
    
            // 設定を複製して返す
            return parsed.value;
        }
    
        pub fn saveToFile(self: Config, path: []const u8) !void {
            const file = try std.fs.cwd().createFile(path, .{});
            defer file.close();
    
            var writer = file.writer();
    
            try std.json.stringify(self, .{ .whitespace = .indent_2 }, writer);
        }
    };
    
    pub fn configExample() !void {
        const allocator = std.heap.page_allocator;
    
        // デフォルト設定を作成
        const config = Config{
            .server = .{
                .host = "localhost",
                .port = 8080,
                .workers = 4,
            },
            .database = .{
                .url = "postgres://localhost/mydb",
                .max_connections = 10,
            },
            .logging = .{
                .level = "info",
                .file = null,
            },
        };
    
        // ファイルに保存
        try config.saveToFile("config.json");
    
        // ファイルから読み込み
        const loaded = try Config.loadFromFile(allocator, "config.json");
    
        std.log.info("Loaded config: {s}:{}", .{ loaded.server.host, loaded.server.port });
    }
    

    まとめ

    この章では、Zigの標準ライブラリについて学びました:

  • std.mem: メモリ管理とアロケータ
  • std.fs: ファイルシステム操作
  • std.json: JSON解析と生成
  • std.fmt: フォーマット出力
  • std.log: ロギングシステム
  • 次の章では、デバッグとプロファイリングについて学びます。

    参考文献

  • Zig Standard Library: https://ziglang.org/documentation/master/std/
  • Zig Standard Library Source: https://github.com/ziglang/zig/tree/master/lib/std