Zen言語の標準ライブラリ紹介〜ascii〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

ASCII文字をうにゃうにゃします。

std.asciiに色々あります。 ソースコードは、配布されているコンパイラlib/zen/std/ascii.zenです。

特定の条件を満たすASCII文字か調べるシリーズ

引数はu8で、boolが返ってきます。

他にもいくつかあります。

const std = @import("std");
const ascii = std.ascii;
const testing = std.testing;

test "ascii isAlpha" {
    testing.ok(ascii.isAlpha('a'));
    testing.ok(ascii.isAlpha('A'));
    testing.ok(!ascii.isAlpha('@'));
    testing.ok(!ascii.isAlpha('0'));
}

test "ascii isDigit" {
    testing.ok(ascii.isDigit('0'));
    testing.ok(ascii.isDigit('9'));
    testing.ok(!ascii.isDigit('a'));
}

test "ascii isAlNum" {
    testing.ok(ascii.isAlNum('0'));
    testing.ok(ascii.isAlNum('a'));
    testing.ok(!ascii.isAlNum('@'));
}

test "ascii isLower" {
    testing.ok(ascii.isLower('a'));
    testing.ok(ascii.isLower('z'));
    testing.ok(!ascii.isLower('A'));
    testing.ok(!ascii.isLower('Z'));
    testing.ok(!ascii.isLower('@'));
}

test "ascii isUpper" {
    testing.ok(ascii.isUpper('A'));
    testing.ok(ascii.isUpper('Z'));
    testing.ok(!ascii.isUpper('a'));
    testing.ok(!ascii.isUpper('z'));
    testing.ok(!ascii.isUpper('@'));
}

test "ascii isXDigit" {
    testing.ok(ascii.isXDigit('0'));
    testing.ok(ascii.isXDigit('9'));
    testing.ok(ascii.isXDigit('a'));
    testing.ok(ascii.isXDigit('f'));
    testing.ok(ascii.isXDigit('A'));
    testing.ok(ascii.isXDigit('F'));
    testing.ok(!ascii.isXDigit('g'));
}

大文字から小文字、小文字から大文字

引数はu8で、u8が返ってきます。

test "ascii toUpper" {
    testing.ok(ascii.toUpper('a') == 'A');
    testing.ok(ascii.toUpper('A') == 'A');
    testing.ok(ascii.toUpper('@') == '@');
}

test "ascii toLower" {
    testing.ok(ascii.toLower('A') == 'a');
    testing.ok(ascii.toLower('a') == 'a');
    testing.ok(ascii.toLower('@') == '@');
}

1文字だけじゃなく、複数の文字を小文字に変換して、新しく確保した領域に格納することもできます。

第一引数にメモリアロケータを、第二引数に小文字に変換したい文字列スライスを渡します。 error.OutOfMemoryのエラーか、文字列スライスが返ってきます。

fn allocLowerString(allocator: heap.Allocator, ascii_string: []const u8) ![]u8
const heap = std.heap;
const FixedBufferAllocator = heap.FixedBufferAllocator;
test "ascii allocLowerString" {
    var buffer = [_]u8{ 0 } ** 1024;
    var allocator = FixedBufferAllocator{ .buffer = buffer[0..] };

    const result = try ascii.allocLowerString(&allocator, "Hello World");
    testing.equalSlices(u8, "hello world", result[0..]);
}

何故か知らんですが、逆はないです。 後で追加しておきましょう。

後、標準ライブラリのテストコードに💩書くの止めて下さい。

test "allocLowerString" {
    const result = try allocLowerString(std.testing.allocator, "aBcDeFgHiJkLmNOPqrst0234+💩!");
    defer heap.free(std.testing.allocator, result);
    std.testing.equalSlices(u8, "abcdefghijklmnopqrst0234+💩!", result);
}

大文字小文字の違いを無視して一致比較

第一引数と第二引数とに、それぞれ一致比較したい文字列スライスを渡します。 boolが返ってきます。

fn eqlIgnoreCase(a: []const u8, b: []const u8) bool
test "ascii eqlIgnoreCase" {
    testing.ok(ascii.eqlIgnoreCase("HELLO", "hello"));
}

後、標準ライブラリのテストコードに💩書くの止めて下さい。

test "eqlIgnoreCase" {
    std.testing.ok(eqlIgnoreCase("HEl💩Lo!", "hel💩lo!"));
    std.testing.ok(!eqlIgnoreCase("hElLo!", "hello! "));
    std.testing.ok(!eqlIgnoreCase("hElLo!", "helro!"));
}

部分文字列がある位置を取得する

第一引数で与える文字列スライス中に、第二引数で与える部分文字列スライスが含まれていれば、その先頭の位置が返ってきます。 部分文字列が含まれていない場合、nullが返ります。

fn indexOfIgnoreCase(container: []const u8, substr: []const u8) ?usize
test "ascii indexOfIgnoreCase" {
    testing.ok(ascii.indexOfIgnoreCase("one two three four", "FOUR").? == 14);
    testing.ok(ascii.indexOfIgnoreCase("one two three four", "five") == null);
}

キャラクリテラル取り扱い上の注意

キャラクリテラルは、comptime_int型になるので注意が必要です。 comptime_int型はそのままフォーマット文字列で処理できないので、型変換が必要です。

test "character literal" {
    const char_literal = 'a';
    comptime testing.ok(@TypeOf(char_literal) == comptime_int);

    std.debug.warn("char_literal = {}\n", .{ @to(u8, char_literal) });

    const char_u8: u8 = 'a';
    comptime testing.ok(@TypeOf(char_u8) == u8);
    std.debug.warn("char_u8 = {}\n", .{ char_u8 });
}