Zig w/ Example - String Handling

Reminder that, despite the title of this example, the concept of string doesn't actually exist in Zig. Instead, a string is an array of bytes (u8).

Which is why, most of the functions here will also work to handle arrays whose individual types are not u8.

Copy

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

test "String copy" {
    const allocator = std.testing.allocator;

    const message = "The quick brown fox.";

    const copied = try allocator.alloc(u8, message.len);
    defer allocator.free(copied);

    std.mem.copy(u8, copied, message);

    std.debug.print("\nOriginal: {s}\n    Copy: {s}\n", .{ message, copied });
}
Terminal
$ zig test string_handling.zig
Test [1/1] test "String copy"...
Original: The quick brown fox.
    Copy: The quick brown fox.
All 1 tests passed.
$ 

Alternatively, if you're using an allocator, you can instead duplicate the string by using Allocator.dupe().

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

test "String copy/dupe" {
    const allocator = std.testing.allocator;

    const message = "The quick brown fox.";

    const copied = try allocator.dupe(u8, message);
    defer allocator.free(copied);

    std.debug.print("\nOriginal: {s}\n    Copy: {s}\n", .{ message, copied });
}
Terminal
$ zig test string_handling.zig
Test [1/1] test "String copy/dupe"...
Original: The quick brown fox.
    Copy: The quick brown fox.
All 1 tests passed.
$ 

Equal

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

test "String equal" {
    const allocator = std.testing.allocator;

    const message = "The quick brown fox.";

    const copied = try allocator.dupe(u8, message);
    defer allocator.free(copied);

    const is_equal = std.mem.eql(u8, copied, message);

    std.debug.print("\nOriginal: {s}\n    Copy: {s}\n", .{ message, copied });
    std.debug.print("Are the two strings equal? {}\n", .{is_equal});

    try std.testing.expect(is_equal);
}
Terminal
$ zig test string_handling.zig
Test [1/1] test "String equal"...
Original: The quick brown fox.
    Copy: The quick brown fox.
Are the two strings equal? true
All 1 tests passed.
$ 

Be extra careful when using this function to compare C null-terminated array, however, as, unlike C's strcmp, this function doesn't stop when it reaches a zero value.

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

test "String equal" {
    const message0 = "Equal?";
    const message1 = &[_]u8{ 'E', 'q', 'u', 'a', 'l', '?', 0, 0 };

    const is_equal = std.mem.eql(u8, message0, message1);

    try std.testing.expect(is_equal);
}
Terminal
$ zig test string_handling.zig
Test [2/1] test "String equal"... FAIL (TestUnexpectedResult)
/home/zig/std/testing.zig:309:14: 0x7ff6d97231fb in std.testing.expect (test.obj)
    if (!ok) return error.TestUnexpectedResult;
             ^
/home/zig/test.zig:9:5: 0x7ff6d9721890 in test "String equal" (test.obj)
    try std.testing.expect(is_equal);
    ^
0 passed; 0 skipped; 1 failed.
error: the following test command failed with exit code 1:
zig-cache/o/12cf2178032725d246a4b465b2a8361b/test /home/zig/zig
$ 

One way to solve this is by counting how many bytes it takes until it reaches the zero value, then create a slice out of it.

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

test "String equal" {
    const message0 = "Equal?";
    const message1 = &[_]u8{ 'E', 'q', 'u', 'a', 'l', '?', 0, 0 };

    var message1_length: usize = 0;

    for (message1) |byte| {
        if (byte == 0) break;
        message1_length += 1;
    }

    const message1_slice = message1[0 .. message1_length];

    const is_equal = std.mem.eql(u8, message0, message1_slice);

    try std.testing.expect(is_equal);
}
Terminal
$ zig test string_handling.zig
All 1 tests passed.
$ 

Or alternatively, you can just use std.mem.sliceTo():

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

test "String equal" {
    const message0 = "Equal?";
    const message1 = &[_]u8{ 'E', 'q', 'u', 'a', 'l', '?', 0, 0 };

    const message1_slice = std.mem.sliceTo(message1, 0);

    const is_equal = std.mem.eql(u8, message0, message1_slice);

    try std.testing.expect(is_equal);
}
Terminal
$ zig test string_handling.zig
All 1 tests passed.
$ 

Contains

To check whether a substring is present in a given string, you can use std.mem.count().

std.mem.count() returns how many times a substring is detected inside the string.

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

test "String contains" {
    const message = "Hello, world!";

    var contains = if (std.mem.count(u8, message, "Hell") > 0) true else false;
    try std.testing.expect(contains);

    contains = if (std.mem.count(u8, message, "What") > 0) true else false;
    try std.testing.expect(!contains);
}
Terminal
$ zig test string_handling.zig
All 1 tests passed.
$