Open23
ちょこっとZig (Tips)
ピン留めされたアイテム
Zig Playground
(本コメント公開時点でバージョンはいずれも0.11.0)
項目 | https://zig-play.dev/ | https://codapi.org/zig/ | https://www.zigfiddle.dev/ |
---|---|---|---|
main実行 | ○ | ○ | ○ |
実行中表示 | ✕ | ○ | ✕ |
行番号 | ✕ | ○ | ○ |
ソース整形 | ○ | ✕ | ○ |
テスト実行 | ✕ | ○ | ✕ |
share | ✕ | ○ | ○ |
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});
}
匿名関数(無名関数)の取得
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)});
}
コマンドライン引数の取得
コマンドライン引数をすべて取得
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.
実行環境の情報を取得
- 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
ソート
ソート
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
型のつくり方(数値型) - @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
子プロセスでコマンド実行(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
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").? });
}
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));
}
桁数の多い計算
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
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
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"}
複数の出力先に同時出力
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.
ファイルから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
疑似乱数の生成
ポイント
- シード(seed)の生成にstd.posix.getrandomと@bitCastを使用
- 疑似乱数発生器にstd.Random.DefaultPrngを使用
議事乱数の生成
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
日本語を含む文字列の文字数とバイト数(UTF-8)
- std.unicode.utf8CountCodepointsでコードポイントの数を取得する
-
len
はバイト数
日本語を含む文字列の文字数とバイト数(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.
環境変数PATHの値をパスごとに改行して表示
環境変数PATHの値を取得しパス区切り文字を改行に変換して表示
- std.process.getEnvVarOwned - 指定した環境変数の値を取得
- std.fs.path.delimiter - パス区切り文字
環境変数の値を取得
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
zipファイルエントリ一覧表示
一時ディレクトリを使用していることに注意
- std.testing.tmpDir
- std.zip.Diagnostics
- std.io.SeekableStream
- std.zip.Iterator
- std.zip.Iterator.Entry
*.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
.....................
(以下続く)
*.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
.....................
(以下続く)
*.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
.....................
(以下続く)
*.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, .{});
}
*.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(), .{});
}