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
- 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