Como Buscar Substrings em Zig

Como Buscar Substrings em Zig

Encontrar um trecho de texto dentro de uma string é uma tarefa comum em processamento de texto, validação de dados e parsing. Zig oferece funções eficientes na biblioteca padrão para busca de substrings.

Busca Básica com indexOf

A função std.mem.indexOf retorna a posição da primeira ocorrência de uma substring, ou null se não for encontrada.

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const texto = "A linguagem Zig é moderna e eficiente";

    // Buscar substring
    if (std.mem.indexOf(u8, texto, "Zig")) |pos| {
        try stdout.print("\"Zig\" encontrado na posição {d}\n", .{pos});
    } else {
        try stdout.print("\"Zig\" não encontrado\n", .{});
    }

    // Buscar substring inexistente
    if (std.mem.indexOf(u8, texto, "Rust")) |pos| {
        try stdout.print("\"Rust\" encontrado na posição {d}\n", .{pos});
    } else {
        try stdout.print("\"Rust\" não encontrado\n", .{});
    }

    // Buscar caractere único com indexOfScalar
    if (std.mem.indexOfScalar(u8, texto, 'Z')) |pos| {
        try stdout.print("'Z' encontrado na posição {d}\n", .{pos});
    }
}

Saída esperada:

"Zig" encontrado na posição 12
"Rust" não encontrado
'Z' encontrado na posição 12

Buscar Última Ocorrência

Use std.mem.lastIndexOf para encontrar a última ocorrência de uma substring.

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const caminho = "/home/usuario/documentos/projeto/arquivo.zig";

    // Encontrar última barra
    if (std.mem.lastIndexOfScalar(u8, caminho, '/')) |pos| {
        const diretorio = caminho[0..pos];
        const arquivo = caminho[pos + 1 ..];
        try stdout.print("Diretório: {s}\n", .{diretorio});
        try stdout.print("Arquivo: {s}\n", .{arquivo});
    }

    // Encontrar última ocorrência de extensão
    const nome_arquivo = "relatorio.backup.2026.tar.gz";
    if (std.mem.lastIndexOfScalar(u8, nome_arquivo, '.')) |pos| {
        const extensao = nome_arquivo[pos..];
        try stdout.print("Extensão: {s}\n", .{extensao});
    }
}

Saída esperada:

Diretório: /home/usuario/documentos/projeto
Arquivo: arquivo.zig
Extensão: .gz

Verificar se Contém (Contains)

Crie uma função simples para verificar se uma string contém outra.

const std = @import("std");

fn contem(texto: []const u8, busca: []const u8) bool {
    return std.mem.indexOf(u8, texto, busca) != null;
}

fn contemIgnorandoCaso(allocator: std.mem.Allocator, texto: []const u8, busca: []const u8) !bool {
    const texto_lower = try std.ascii.allocLowerString(allocator, texto);
    defer allocator.free(texto_lower);
    const busca_lower = try std.ascii.allocLowerString(allocator, busca);
    defer allocator.free(busca_lower);
    return std.mem.indexOf(u8, texto_lower, busca_lower) != null;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const texto = "Programação em Zig é divertida";

    try stdout.print("Contém \"Zig\": {}\n", .{contem(texto, "Zig")});
    try stdout.print("Contém \"Python\": {}\n", .{contem(texto, "Python")});

    // Case-insensitive
    const resultado = try contemIgnorandoCaso(allocator, texto, "zig");
    try stdout.print("Contém \"zig\" (case-insensitive): {}\n", .{resultado});
}

Saída esperada:

Contém "Zig": true
Contém "Python": false
Contém "zig" (case-insensitive): true

Contar Ocorrências

Conte quantas vezes uma substring aparece no texto.

const std = @import("std");

fn contarOcorrencias(texto: []const u8, busca: []const u8) usize {
    var contador: usize = 0;
    var pos: usize = 0;

    while (pos <= texto.len - busca.len) {
        if (std.mem.indexOf(u8, texto[pos..], busca)) |encontrado| {
            contador += 1;
            pos += encontrado + busca.len;
        } else {
            break;
        }
    }

    return contador;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const texto = "abracadabra";
    try stdout.print("Ocorrências de \"abra\" em \"abracadabra\": {d}\n", .{contarOcorrencias(texto, "abra")});
    try stdout.print("Ocorrências de \"a\" em \"abracadabra\": {d}\n", .{contarOcorrencias(texto, "a")});

    const frase = "o rato roeu a roupa do rei de roma";
    try stdout.print("Ocorrências de \"r\" em frase: {d}\n", .{contarOcorrencias(frase, "r")});
    try stdout.print("Ocorrências de \"ro\" em frase: {d}\n", .{contarOcorrencias(frase, "ro")});
}

Saída esperada:

Ocorrências de "abra" em "abracadabra": 2
Ocorrências de "a" em "abracadabra": 5
Ocorrências de "r" em frase: 5
Ocorrências de "ro" em frase: 3

Encontrar Todas as Posições

Retorne todas as posições onde uma substring ocorre.

const std = @import("std");

fn encontrarTodas(allocator: std.mem.Allocator, texto: []const u8, busca: []const u8) ![]usize {
    var posicoes = std.ArrayList(usize).init(allocator);
    var pos: usize = 0;

    while (pos <= texto.len - busca.len) {
        if (std.mem.indexOf(u8, texto[pos..], busca)) |encontrado| {
            try posicoes.append(pos + encontrado);
            pos += encontrado + 1;
        } else {
            break;
        }
    }

    return posicoes.toOwnedSlice();
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const texto = "Zig é legal. Zig é rápido. Zig é seguro.";
    const posicoes = try encontrarTodas(allocator, texto, "Zig");
    defer allocator.free(posicoes);

    try stdout.print("\"Zig\" encontrado em {d} posições: ", .{posicoes.len});
    for (posicoes, 0..) |pos, i| {
        if (i > 0) try stdout.print(", ", .{});
        try stdout.print("{d}", .{pos});
    }
    try stdout.print("\n", .{});
}

Saída esperada:

"Zig" encontrado em 3 posições: 0, 14, 28

Buscar com indexOfAny

Use std.mem.indexOfAny para encontrar qualquer caractere de um conjunto.

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const texto = "nome.sobrenome@email.com";

    // Encontrar primeiro caractere especial
    if (std.mem.indexOfAny(u8, texto, ".@-_")) |pos| {
        try stdout.print("Primeiro caractere especial na posição {d}: '{c}'\n", .{ pos, texto[pos] });
    }

    // Verificar se contém dígitos
    const frase = "Olá Mundo 2026";
    if (std.mem.indexOfAny(u8, frase, "0123456789")) |pos| {
        try stdout.print("Primeiro dígito na posição {d}: '{c}'\n", .{ pos, frase[pos] });
    }
}

Saída esperada:

Primeiro caractere especial na posição 4: '.'
Primeiro dígito na posição 10: '2'

Exemplo Prático: Validar Email Simples

const std = @import("std");

fn validarEmail(email: []const u8) bool {
    // Deve conter exatamente um @
    const arroba = std.mem.indexOf(u8, email, "@") orelse return false;

    // @ não pode ser o primeiro ou último caractere
    if (arroba == 0 or arroba == email.len - 1) return false;

    // Deve ter pelo menos um ponto após o @
    const dominio = email[arroba + 1 ..];
    if (std.mem.indexOf(u8, dominio, ".") == null) return false;

    // Domínio não pode terminar com ponto
    if (std.mem.endsWith(u8, dominio, ".")) return false;

    return true;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const emails = [_][]const u8{
        "usuario@email.com",
        "invalido",
        "@sem-usuario.com",
        "sem-dominio@",
        "user@sem-ponto",
        "ok@dominio.com.br",
    };

    for (emails) |email| {
        const valido = validarEmail(email);
        try stdout.print("  {s:<25} -> {s}\n", .{ email, if (valido) "Válido" else "Inválido" });
    }
}

Saída esperada:

  usuario@email.com         -> Válido
  invalido                  -> Inválido
  @sem-usuario.com          -> Inválido
  sem-dominio@              -> Inválido
  user@sem-ponto            -> Inválido
  ok@dominio.com.br         -> Válido

Veja Também

Continue aprendendo Zig

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