Como Dividir (Split) Strings por Delimitador em Zig
Dividir strings por um delimitador é uma operação fundamental para parsing de dados, processamento de texto e análise de formatos como CSV. Zig oferece várias funções na biblioteca padrão para fazer isso de forma eficiente e sem alocação de memória.
Split vs Tokenize
Zig oferece duas famílias de funções para dividir strings:
- split: mantém campos vazios quando delimitadores consecutivos são encontrados
- tokenize: ignora campos vazios (semelhante ao comportamento de
strtokem C)
Dividir por Caractere Único com splitScalar
A função std.mem.splitScalar divide uma string usando um único caractere como delimitador.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Dividir por vírgula
const csv = "João,25,São Paulo,Brasil";
var iter = std.mem.splitScalar(u8, csv, ',');
try stdout.print("Campos do CSV:\n", .{});
while (iter.next()) |campo| {
try stdout.print(" -> \"{s}\"\n", .{campo});
}
// Dividir caminho por /
try stdout.print("\nPartes do caminho:\n", .{});
const caminho = "/home/usuario/documentos/arquivo.txt";
var iter2 = std.mem.splitScalar(u8, caminho, '/');
while (iter2.next()) |parte| {
try stdout.print(" -> \"{s}\"\n", .{parte});
}
}
Saída esperada:
Campos do CSV:
-> "João"
-> "25"
-> "São Paulo"
-> "Brasil"
Partes do caminho:
-> ""
-> "home"
-> "usuario"
-> "documentos"
-> "arquivo.txt"
Note que splitScalar preserva campos vazios (como o primeiro campo vazio antes de /home).
Dividir por Sequência de Caracteres
Use std.mem.splitSequence para dividir por uma sequência de múltiplos caracteres.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Dividir por " -> "
const fluxo = "início -> processamento -> validação -> fim";
var iter = std.mem.splitSequence(u8, fluxo, " -> ");
try stdout.print("Etapas do fluxo:\n", .{});
while (iter.next()) |etapa| {
try stdout.print(" [{s}]\n", .{etapa});
}
// Dividir por quebra de linha "\r\n"
try stdout.print("\nLinhas:\n", .{});
const texto = "Linha 1\r\nLinha 2\r\nLinha 3";
var iter2 = std.mem.splitSequence(u8, texto, "\r\n");
while (iter2.next()) |linha| {
try stdout.print(" \"{s}\"\n", .{linha});
}
}
Saída esperada:
Etapas do fluxo:
[início]
[processamento]
[validação]
[fim]
Linhas:
"Linha 1"
"Linha 2"
"Linha 3"
Tokenizar Strings (Ignorar Vazios)
Use std.mem.tokenizeScalar quando quiser ignorar campos vazios resultantes de delimitadores consecutivos.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Tokenizar por espaço (ignora espaços múltiplos)
const texto = " Olá Mundo Zig ";
var tokens = std.mem.tokenizeScalar(u8, texto, ' ');
try stdout.print("Tokens (espaço):\n", .{});
while (tokens.next()) |token| {
try stdout.print(" \"{s}\"\n", .{token});
}
// Comparação: split mantém vazios
try stdout.print("\nSplit (espaço) - com vazios:\n", .{});
var split = std.mem.splitScalar(u8, texto, ' ');
while (split.next()) |parte| {
try stdout.print(" \"{s}\"\n", .{parte});
}
}
Saída esperada:
Tokens (espaço):
"Olá"
"Mundo"
"Zig"
Split (espaço) - com vazios:
""
""
"Olá"
""
""
""
"Mundo"
""
""
"Zig"
""
""
Tokenizar por Múltiplos Delimitadores
Use std.mem.tokenizeAny para dividir por qualquer caractere de um conjunto.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Tokenizar por múltiplos delimitadores (espaço, vírgula, ponto-e-vírgula)
const entrada = "maçã, banana; laranja uva,melão";
var tokens = std.mem.tokenizeAny(u8, entrada, " ,;");
try stdout.print("Frutas:\n", .{});
while (tokens.next()) |fruta| {
try stdout.print(" - {s}\n", .{fruta});
}
}
Saída esperada:
Frutas:
- maçã
- banana
- laranja
- uva
- melão
Coletar Resultados em ArrayList
Para armazenar os resultados do split em uma lista, use ArrayList.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const csv = "nome,idade,cidade,profissão";
var iter = std.mem.splitScalar(u8, csv, ',');
// Coletar em ArrayList
var campos = std.ArrayList([]const u8).init(allocator);
defer campos.deinit();
while (iter.next()) |campo| {
try campos.append(campo);
}
try stdout.print("Total de campos: {d}\n", .{campos.items.len});
for (campos.items, 0..) |campo, i| {
try stdout.print(" Campo {d}: \"{s}\"\n", .{ i, campo });
}
}
Saída esperada:
Total de campos: 4
Campo 0: "nome"
Campo 1: "idade"
Campo 2: "cidade"
Campo 3: "profissão"
Exemplo Prático: Parser de CSV Simples
Veja um exemplo mais completo que faz parsing de um conteúdo CSV.
const std = @import("std");
const Pessoa = struct {
nome: []const u8,
idade: u32,
cidade: []const u8,
};
fn parseLinhaCsv(linha: []const u8) !Pessoa {
var campos = std.mem.splitScalar(u8, linha, ',');
const nome = campos.next() orelse return error.CampoAusente;
const idade_str = campos.next() orelse return error.CampoAusente;
const cidade = campos.next() orelse return error.CampoAusente;
const idade = try std.fmt.parseInt(u32, idade_str, 10);
return Pessoa{
.nome = nome,
.idade = idade,
.cidade = cidade,
};
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const dados =
\\Ana,30,São Paulo
\\Carlos,25,Rio de Janeiro
\\Maria,35,Belo Horizonte
;
var linhas = std.mem.splitScalar(u8, dados, '\n');
try stdout.print("Pessoas cadastradas:\n", .{});
while (linhas.next()) |linha| {
if (linha.len == 0) continue;
const pessoa = try parseLinhaCsv(linha);
try stdout.print(" {s}, {d} anos, {s}\n", .{ pessoa.nome, pessoa.idade, pessoa.cidade });
}
}
Saída esperada:
Pessoas cadastradas:
Ana, 30 anos, São Paulo
Carlos, 25 anos, Rio de Janeiro
Maria, 35 anos, Belo Horizonte
Referência Rápida
| Função | Delimitador | Campos Vazios |
|---|---|---|
splitScalar | Caractere único | Mantém |
splitSequence | Sequência de chars | Mantém |
splitAny | Qualquer de um conjunto | Mantém |
tokenizeScalar | Caractere único | Remove |
tokenizeSequence | Sequência de chars | Remove |
tokenizeAny | Qualquer de um conjunto | Remove |
Veja Também
- Concatenar Strings — Operação inversa: juntar strings
- Buscar Substrings — Encontre texto antes de dividir
- Converter String para Número — Converta campos numéricos após o split
- Ler Arquivo Linha por Linha — Combine com leitura de arquivos