第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の標準ライブラリについて学びました:
次の章では、デバッグとプロファイリングについて学びます。