std.mem.split e tokenize em Zig — Referência e Exemplos

std.mem.split e tokenize — Divisão e Tokenização

As funções split e tokenize de std.mem permitem dividir slices em partes com base em delimitadores. A diferença principal é que split preserva campos vazios entre delimitadores consecutivos, enquanto tokenize os ignora.

Visão Geral

const std = @import("std");
const mem = std.mem;

// split: preserva campos vazios
var iter_split = mem.splitScalar(u8, "a,,b,,c", ',');
// Produz: "a", "", "b", "", "c"

// tokenize: ignora campos vazios
var iter_token = mem.tokenizeScalar(u8, "a,,b,,c", ',');
// Produz: "a", "b", "c"

Funções de Split

splitScalar

pub fn splitScalar(comptime T: type, buffer: []const T, delimiter: T) SplitIterator(T, .scalar)

Divide por um único valor delimitador.

splitSequence

pub fn splitSequence(comptime T: type, buffer: []const T, delimiter: []const T) SplitIterator(T, .sequence)

Divide por uma sequência de valores.

splitAny

pub fn splitAny(comptime T: type, buffer: []const T, delimiters: []const T) SplitIterator(T, .any)

Divide por qualquer um dos delimitadores.

Funções de Tokenize

tokenizeScalar

pub fn tokenizeScalar(comptime T: type, buffer: []const T, delimiter: T) TokenIterator(T, .scalar)

Tokeniza por um único delimitador, ignorando campos vazios.

tokenizeSequence

pub fn tokenizeSequence(comptime T: type, buffer: []const T, delimiter: []const T) TokenIterator(T, .sequence)

Tokeniza por uma sequência delimitadora.

tokenizeAny

pub fn tokenizeAny(comptime T: type, buffer: []const T, delimiters: []const T) TokenIterator(T, .any)

Tokeniza por qualquer delimitador do conjunto.

Métodos do Iterador

Todos os iteradores retornados possuem:

// Retorna o próximo segmento ou null quando terminado
pub fn next(self: *Self) ?[]const T

// Retorna o restante do buffer (ainda não iterado)
pub fn rest(self: Self) []const T

// Retorna o segmento anterior ao último next()
pub fn peek(self: Self) ?[]const T

// Reseta o iterador ao início
pub fn reset(self: *Self) void

Exemplo 1: Processamento de CSV Simples

const std = @import("std");
const mem = std.mem;

const Registro = struct {
    nome: []const u8,
    cidade: []const u8,
    idade: u32,
};

fn parsearLinhaCSV(linha: []const u8) ?Registro {
    var campos = mem.splitScalar(u8, linha, ',');

    const nome = campos.next() orelse return null;
    const cidade = campos.next() orelse return null;
    const idade_str = campos.next() orelse return null;

    const idade = std.fmt.parseInt(u32, idade_str, 10) catch return null;

    return .{
        .nome = mem.trim(u8, nome, " "),
        .cidade = mem.trim(u8, cidade, " "),
        .idade = idade,
    };
}

pub fn main() void {
    const csv =
        \\Ana Silva, São Paulo, 28
        \\Bruno Santos, Rio de Janeiro, 35
        \\Carla Oliveira, Belo Horizonte, 22
    ;

    var linhas = mem.splitScalar(u8, csv, '\n');

    std.debug.print("{s:<20} {s:<25} {s}\n", .{ "Nome", "Cidade", "Idade" });
    std.debug.print("{s}\n", .{"-" ** 55});

    while (linhas.next()) |linha| {
        if (parsearLinhaCSV(linha)) |reg| {
            std.debug.print("{s:<20} {s:<25} {d}\n", .{
                reg.nome, reg.cidade, reg.idade,
            });
        }
    }
}

Exemplo 2: Split vs Tokenize

const std = @import("std");
const mem = std.mem;

pub fn main() void {
    const texto = "  palavra1   palavra2   palavra3  ";

    // split por espaço — inclui strings vazias entre espaços
    std.debug.print("=== splitScalar ===\n", .{});
    var split_iter = mem.splitScalar(u8, texto, ' ');
    var count_split: usize = 0;
    while (split_iter.next()) |parte| {
        std.debug.print("  [{d}] '{s}' (len={d})\n", .{ count_split, parte, parte.len });
        count_split += 1;
    }
    std.debug.print("  Total: {d} partes\n\n", .{count_split});

    // tokenize por espaço — ignora espaços consecutivos
    std.debug.print("=== tokenizeScalar ===\n", .{});
    var token_iter = mem.tokenizeScalar(u8, texto, ' ');
    var count_token: usize = 0;
    while (token_iter.next()) |parte| {
        std.debug.print("  [{d}] '{s}'\n", .{ count_token, parte });
        count_token += 1;
    }
    std.debug.print("  Total: {d} partes\n\n", .{count_token});

    // splitSequence por sequência "::"
    std.debug.print("=== splitSequence ===\n", .{});
    const caminho = "std::mem::Allocator";
    var seq_iter = mem.splitSequence(u8, caminho, "::");
    while (seq_iter.next()) |parte| {
        std.debug.print("  '{s}'\n", .{parte});
    }
}

Exemplo 3: Parser de Variáveis de Ambiente

const std = @import("std");
const mem = std.mem;

fn parsearPath(path_var: []const u8) void {
    var iter = mem.splitScalar(u8, path_var, ':');
    var i: usize = 0;

    std.debug.print("Diretórios no PATH:\n", .{});
    while (iter.next()) |diretorio| {
        if (diretorio.len == 0) continue;
        i += 1;
        std.debug.print("  {d}. {s}\n", .{ i, diretorio });
    }
}

fn parsearQueryString(query: []const u8) void {
    std.debug.print("\nParâmetros da query string:\n", .{});

    var pares = mem.splitScalar(u8, query, '&');
    while (pares.next()) |par| {
        var kv = mem.splitScalar(u8, par, '=');
        const chave = kv.next() orelse continue;
        const valor = kv.next() orelse "";

        std.debug.print("  {s} = '{s}'\n", .{ chave, valor });
    }
}

pub fn main() void {
    // Simular PATH
    const path = "/usr/local/bin:/usr/bin:/bin:/home/user/.local/bin";
    parsearPath(path);

    // Simular query string
    const query = "nome=Ana&cidade=SP&idade=28&ativo=true";
    parsearQueryString(query);
}

Padrões Comuns

Coletar em ArrayList

fn splitParaLista(allocator: Allocator, texto: []const u8, delim: u8) !std.ArrayList([]const u8) {
    var lista = std.ArrayList([]const u8).init(allocator);
    var iter = mem.splitScalar(u8, texto, delim);
    while (iter.next()) |parte| {
        try lista.append(parte);
    }
    return lista;
}

Dividir em N Partes

fn splitPrimeiro(texto: []const u8, delim: u8) struct { chave: []const u8, valor: []const u8 } {
    var iter = mem.splitScalar(u8, texto, delim);
    const chave = iter.next() orelse return .{ .chave = texto, .valor = "" };
    const valor = iter.rest();
    return .{ .chave = chave, .valor = valor };
}

// "chave=valor=com=igual" -> chave="chave", valor="valor=com=igual"

Tokenizar Espaços em Branco Mistos

var iter = mem.tokenizeAny(u8, texto, " \t\n\r");
while (iter.next()) |palavra| {
    // Processa cada palavra, ignorando qualquer whitespace
}

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.