std.mem.split e tokenize — Divisão e Tokenização
Para dividir uma string em Zig, use std.mem.splitScalar(u8, texto, ',') para um delimitador de um caractere, splitSequence para uma sequência como "::" e splitAny para qualquer caractere de um conjunto. O retorno é um iterador consumido com while (iter.next()) |parte|, e cada parte é um []const u8 que aponta para o buffer original — nenhuma alocação é feita. Para tokenizar ignorando campos vazios, use tokenizeScalar, tokenizeSequence ou tokenizeAny.
A diferença central entre as duas famílias: split preserva campos vazios entre delimitadores consecutivos (importante quando a posição conta, como colunas de CSV), enquanto tokenize os ignora (ideal para palavras de um texto).
Compatibilidade: as versões sem sufixo (
mem.split(buffer, delim)emem.tokenize) foram removidas do Zig 0.11 em diante. Seu código antigo migra trocandomem.split(buf, ',')pormem.splitScalar(u8, buf, ','). Como o Zig está antes da versão 1.0, confirme as assinaturas comzig versionna documentação do release que você usa.
Comparação Rápida: split vs tokenize
split (splitScalar/Sequence/Any) | tokenize (tokenizeScalar/Sequence/Any) | |
|---|---|---|
| Campos vazios entre delimitadores | Preserva | Ignora |
| Delimitadores nas pontas | Geram campos vazios | São descartados |
| Quando usar | CSV, posições fixas, protocolos | Palavras, PATH, tokens de lexer |
| Alocação | Nenhuma (views do buffer) | Nenhuma (views do buffer) |
const texto = "a,,b";
// splitScalar → "a", "", "b" (3 partes)
// tokenizeScalar → "a", "b" (2 partes)
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.
splitBackwardsScalar, splitBackwardsSequence e splitBackwardsAny
pub fn splitBackwardsScalar(comptime T: type, buffer: []const T, delimiter: T) SplitBackwardsIterator(T, .scalar)
As variantes splitBackwards* iteram do fim para o começo do slice — mesmas regras de split (preservam campos vazios), só que em ordem inversa. São úteis quando o interesse está nas últimas partes: extensão de arquivo, último segmento de um caminho ou o valor final de uma chave composta.
const std = @import("std");
pub fn main() void {
// Extensão do arquivo: comece pelo fim
var partes = std.mem.splitBackwardsScalar(u8, "relatorio.tar.gz", '.');
const ext = partes.next() orelse return; // "gz"
const nome = partes.rest(); // "relatorio.tar"
std.debug.print("ext='{s}' resto='{s}'\n", .{ ext, nome });
// Último segmento de um caminho
var caminho = std.mem.splitBackwardsScalar(u8, "/usr/local/bin/zig", '/');
_ = caminho.next() orelse return; // "zig"
const dir = caminho.rest(); // "/usr/local/bin"
std.debug.print("binario='{s}' dir='{s}'\n", .{ "zig", dir });
}
Para ignorar campos vazios de trás para frente, combine tokenize* com leitura manual ou reverta o buffer — não existe tokenizeBackwards.
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
}
Erros Comuns
error: expected pointer, found 'SplitIterator'— o iterador é mutável: declarevar iter = ..., nãoconst. Onext()avança o estado interno e precisa de um ponteiro.- Passar
","em vez de','—splitScalarrecebe um únicou8. Com string, usesplitSequence(u8, texto, ","). - Delimitador no fim gera campo vazio —
"a,b,"comsplitScalarproduz"a", "b", "". Se isso não é desejado, usetokenize*ou trateparte.len == 0. - Partes invalidadas após liberar o buffer — os segmentos são views do slice original; se o buffer veio de uma alocação, ele precisa viver enquanto as partes forem usadas.
Perguntas Frequentes
Qual a diferença entre split e tokenize em Zig?
split preserva os campos vazios entre delimitadores consecutivos; tokenize os ignora. Use split quando a posição dos campos importa (colunas de CSV) e tokenize quando você quer só as partes não vazias (palavras de um texto).
Como dividir uma string em Zig?
Use std.mem.splitScalar (um caractere), splitSequence (sequência) ou splitAny (conjunto). O iterador retornado entrega cada parte em next(), sem alocar memória.
Por que mem.split e mem.tokenize não existem mais?
Foram removidos no Zig 0.11 e substituídos pelas variantes com sufixo (splitScalar, tokenizeAny, etc.), que deixam o tipo de delimitador explícito.
Como juntar as partes em um ArrayList?
Itere com while (iter.next()) |parte| e faça try lista.append(parte) em um std.ArrayList([]const u8). Lembre-se de que as partes são views do buffer original.
Módulos Relacionados
- std.mem — Visão geral de operações de memória
- std.mem.eql — Comparação e busca
- std.fmt — Formatação e parsing de strings
- std.ArrayList — Para coletar resultados
Tutoriais e Receitas Relacionados
- Tutorial: Strings e Arrays em Zig — Guia completo de strings e slices
- Receita: Como comparar strings em Zig — Igualdade e ordenação com std.mem
- Receita: Converter entre tipos de string em Zig — Slices, arrays e strings com sentinela
- Receita: Como parsear JSON em Zig