std.mem.eql, indexOf e Comparações
As funções de comparação e busca de std.mem são essenciais para trabalhar com strings e slices em Zig. Como o Zig trata strings como []const u8, essas funções substituem operadores de igualdade e métodos de string de outras linguagens.
Visão Geral
Em Zig, o operador == não funciona para comparar slices (ele compara os ponteiros, não o conteúdo). Para comparar conteúdo, use std.mem.eql:
const std = @import("std");
const mem = std.mem;
// CORRETO: compara conteúdo
if (mem.eql(u8, str1, str2)) { ... }
// INCORRETO: compara ponteiros, não conteúdo
// if (str1 == str2) { ... }
Funções de Igualdade
mem.eql
pub fn eql(comptime T: type, a: []const T, b: []const T) bool
Compara dois slices elemento por elemento. Retorna true se tiverem o mesmo comprimento e conteúdo.
mem.order
pub fn order(comptime T: type, lhs: []const T, rhs: []const T) std.math.Order
Compara lexicograficamente. Retorna .lt, .eq ou .gt.
mem.lessThan
pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool
Retorna true se lhs for lexicograficamente menor que rhs.
Funções de Busca
mem.indexOf
pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
Encontra a primeira ocorrência de needle em haystack. Retorna null se não encontrar.
mem.lastIndexOf
pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
Encontra a última ocorrência.
mem.indexOfScalar
pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize
Encontra a primeira ocorrência de um único valor.
mem.indexOfAny
pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize
Encontra a primeira ocorrência de qualquer valor do conjunto.
mem.count
pub fn count(comptime T: type, haystack: []const T, needle: []const T) usize
Conta ocorrências não sobrepostas de needle em haystack.
Funções de Prefixo e Sufixo
mem.startsWith
pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool
Verifica se haystack começa com needle.
mem.endsWith
pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool
Verifica se haystack termina com needle.
Exemplo 1: Processador de Comandos
const std = @import("std");
const mem = std.mem;
const Comando = enum {
ajuda,
listar,
sair,
desconhecido,
};
fn interpretarComando(entrada: []const u8) Comando {
const cmd = mem.trim(u8, entrada, " \t\n\r");
if (mem.eql(u8, cmd, "ajuda") or mem.eql(u8, cmd, "help")) {
return .ajuda;
} else if (mem.eql(u8, cmd, "listar") or mem.eql(u8, cmd, "ls")) {
return .listar;
} else if (mem.eql(u8, cmd, "sair") or mem.eql(u8, cmd, "exit")) {
return .sair;
} else {
return .desconhecido;
}
}
pub fn main() void {
const comandos = [_][]const u8{ "ajuda", " listar ", "sair", "foo" };
for (comandos) |cmd| {
const resultado = interpretarComando(cmd);
std.debug.print("'{s}' -> {}\n", .{ mem.trim(u8, cmd, " "), resultado });
}
}
Exemplo 2: Busca e Extração de Texto
const std = @import("std");
const mem = std.mem;
fn extrairValorTag(html: []const u8, tag: []const u8) ?[]const u8 {
// Buscar tag de abertura
var buf_abertura: [64]u8 = undefined;
const abertura = std.fmt.bufPrint(&buf_abertura, "<{s}>", .{tag}) catch return null;
var buf_fechamento: [64]u8 = undefined;
const fechamento = std.fmt.bufPrint(&buf_fechamento, "</{s}>", .{tag}) catch return null;
const inicio = (mem.indexOf(u8, html, abertura) orelse return null) + abertura.len;
const fim = mem.indexOf(u8, html[inicio..], fechamento) orelse return null;
return html[inicio..][0..fim];
}
pub fn main() void {
const html = "<div><titulo>Meu Site</titulo><corpo>Conteúdo</corpo></div>";
if (extrairValorTag(html, "titulo")) |valor| {
std.debug.print("Título: {s}\n", .{valor}); // "Meu Site"
}
if (extrairValorTag(html, "corpo")) |valor| {
std.debug.print("Corpo: {s}\n", .{valor}); // "Conteúdo"
}
if (extrairValorTag(html, "autor")) |_| {
std.debug.print("Autor encontrado\n", .{});
} else {
std.debug.print("Tag 'autor' não encontrada\n", .{});
}
}
Exemplo 3: Validação de Formato
const std = @import("std");
const mem = std.mem;
fn validarEmail(email: []const u8) bool {
// Verificações básicas
if (email.len < 5) return false;
// Deve conter exatamente um '@'
if (mem.count(u8, email, "@") != 1) return false;
const pos_arroba = mem.indexOfScalar(u8, email, '@').?;
// Deve ter algo antes e depois do @
if (pos_arroba == 0 or pos_arroba == email.len - 1) return false;
// Domínio deve conter pelo menos um ponto
const dominio = email[pos_arroba + 1 ..];
if (mem.indexOfScalar(u8, dominio, '.') == null) return false;
// Não pode começar ou terminar com ponto
if (mem.startsWith(u8, email, ".") or mem.endsWith(u8, email, ".")) return false;
return true;
}
pub fn main() void {
const emails = [_][]const u8{
"usuario@exemplo.com",
"invalido",
"@sem-usuario.com",
"sem-dominio@",
"dois@@arrobas.com",
"ok@dominio.br",
};
for (emails) |email| {
const valido = if (validarEmail(email)) "valido" else "invalido";
std.debug.print("{s:<25} -> {s}\n", .{ email, valido });
}
}
Padrões Comuns
Comparação em Switch
O Zig não permite switch em slices diretamente. Use uma cadeia de if:
fn processarExtensao(ext: []const u8) []const u8 {
if (mem.eql(u8, ext, ".zig")) return "Zig";
if (mem.eql(u8, ext, ".rs")) return "Rust";
if (mem.eql(u8, ext, ".go")) return "Go";
return "Desconhecido";
}
Busca com Contexto
fn encontrarTodos(texto: []const u8, padrao: []const u8) void {
var pos: usize = 0;
while (mem.indexOf(u8, texto[pos..], padrao)) |offset| {
const abs_pos = pos + offset;
std.debug.print("Encontrado na posição {d}\n", .{abs_pos});
pos = abs_pos + padrao.len;
}
}
Módulos Relacionados
- std.mem — Visão geral de operações de memória
- std.mem.split — Divisão e tokenização
- std.fmt — Formatação de strings
- std.ascii — Funções para caracteres ASCII