Como Substituir Texto em Strings em Zig

Como Substituir Texto em Strings em Zig

Substituir trechos de texto dentro de strings é uma operação comum em processamento de dados, templates e sanitização de entrada. Em Zig, a substituição pode ser feita usando std.mem.replace ou com soluções manuais.

Substituição Básica com std.mem.replace

A função std.mem.replace substitui todas as ocorrências de um padrão em um buffer de saída.

const std = @import("std");

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

    const texto = "Olá Mundo! Olá Zig!";

    // Substituir "Olá" por "Hello"
    // O buffer de saída deve ter o mesmo tamanho ou maior
    var buf: [256]u8 = undefined;
    const resultado = std.mem.replace(u8, texto, "Olá", "Hello", &buf);
    // resultado é o número de substituições feitas
    // Para obter o tamanho final, precisamos calcular
    const novo_tamanho = texto.len + resultado * ("Hello".len - "Olá".len);
    try stdout.print("Original:    \"{s}\"\n", .{texto});
    try stdout.print("Substituído: \"{s}\"\n", .{buf[0..novo_tamanho]});
    try stdout.print("Substituições: {d}\n", .{resultado});
}

Saída esperada:

Original:    "Olá Mundo! Olá Zig!"
Substituído: "Hello Mundo! Hello Zig!"
Substituições: 2

Substituição com Alocação Dinâmica

Quando o tamanho do resultado é desconhecido ou variável, use um ArrayList.

const std = @import("std");

fn substituirTudo(
    allocator: std.mem.Allocator,
    texto: []const u8,
    busca: []const u8,
    substituicao: []const u8,
) ![]u8 {
    var resultado = std.ArrayList(u8).init(allocator);
    defer resultado.deinit();

    var pos: usize = 0;
    while (pos < texto.len) {
        if (pos + busca.len <= texto.len and
            std.mem.eql(u8, texto[pos .. pos + busca.len], busca))
        {
            try resultado.appendSlice(substituicao);
            pos += busca.len;
        } else {
            try resultado.append(texto[pos]);
            pos += 1;
        }
    }

    return resultado.toOwnedSlice();
}

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

    // Substituir texto curto por texto longo
    const texto = "Zig é bom. Zig é rápido.";
    const resultado = try substituirTudo(allocator, texto, "Zig", "A linguagem Zig");
    defer allocator.free(resultado);
    try stdout.print("Original:    \"{s}\"\n", .{texto});
    try stdout.print("Substituído: \"{s}\"\n", .{resultado});

    // Substituir texto longo por curto (reduzir)
    const html = "<b>texto</b> e <b>mais</b>";
    const sem_bold = try substituirTudo(allocator, html, "<b>", "");
    defer allocator.free(sem_bold);
    const limpo = try substituirTudo(allocator, sem_bold, "</b>", "");
    defer allocator.free(limpo);
    try stdout.print("\nHTML:  \"{s}\"\n", .{html});
    try stdout.print("Limpo: \"{s}\"\n", .{limpo});
}

Saída esperada:

Original:    "Zig é bom. Zig é rápido."
Substituído: "A linguagem Zig é bom. A linguagem Zig é rápido."

HTML:  "<b>texto</b> e <b>mais</b>"
Limpo: "texto e mais"

Substituir Apenas a Primeira Ocorrência

const std = @import("std");

fn substituirPrimeiro(
    allocator: std.mem.Allocator,
    texto: []const u8,
    busca: []const u8,
    substituicao: []const u8,
) ![]u8 {
    if (std.mem.indexOf(u8, texto, busca)) |pos| {
        var resultado = std.ArrayList(u8).init(allocator);
        defer resultado.deinit();

        try resultado.appendSlice(texto[0..pos]);
        try resultado.appendSlice(substituicao);
        try resultado.appendSlice(texto[pos + busca.len ..]);

        return resultado.toOwnedSlice();
    }

    // Se não encontrar, retornar uma cópia
    return allocator.dupe(u8, texto);
}

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

    const texto = "foo bar foo baz foo";
    const resultado = try substituirPrimeiro(allocator, texto, "foo", "qux");
    defer allocator.free(resultado);

    try stdout.print("Original:    \"{s}\"\n", .{texto});
    try stdout.print("Substituído: \"{s}\"\n", .{resultado});
}

Saída esperada:

Original:    "foo bar foo baz foo"
Substituído: "qux bar foo baz foo"

Substituir Caractere por Caractere

Para substituições simples de caractere único, itere diretamente.

const std = @import("std");

fn substituirCaractere(allocator: std.mem.Allocator, texto: []const u8, antigo: u8, novo: u8) ![]u8 {
    const resultado = try allocator.alloc(u8, texto.len);
    for (texto, 0..) |c, i| {
        resultado[i] = if (c == antigo) novo else c;
    }
    return resultado;
}

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

    // Substituir espaços por underscores
    const nome = "meu arquivo de texto.txt";
    const slug = try substituirCaractere(allocator, nome, ' ', '_');
    defer allocator.free(slug);
    try stdout.print("Slug: \"{s}\"\n", .{slug});

    // Substituir pontos por hífens
    const versao = "1.2.3";
    const formatado = try substituirCaractere(allocator, versao, '.', '-');
    defer allocator.free(formatado);
    try stdout.print("Versão: \"{s}\"\n", .{formatado});
}

Saída esperada:

Slug: "meu_arquivo_de_texto.txt"
Versão: "1-2-3"

Exemplo Prático: Template Simples

Implemente um sistema básico de templates com substituição de variáveis.

const std = @import("std");

fn renderTemplate(
    allocator: std.mem.Allocator,
    template: []const u8,
    variaveis: []const struct { chave: []const u8, valor: []const u8 },
) ![]u8 {
    var resultado = try allocator.dupe(u8, template);

    for (variaveis) |v| {
        // Construir o placeholder: {{chave}}
        const placeholder = try std.fmt.allocPrint(allocator, "{{{{{s}}}}}", .{v.chave});
        defer allocator.free(placeholder);

        // Substituir todas as ocorrências
        var novo = std.ArrayList(u8).init(allocator);
        defer novo.deinit();

        var pos: usize = 0;
        while (pos < resultado.len) {
            if (pos + placeholder.len <= resultado.len and
                std.mem.eql(u8, resultado[pos .. pos + placeholder.len], placeholder))
            {
                try novo.appendSlice(v.valor);
                pos += placeholder.len;
            } else {
                try novo.append(resultado[pos]);
                pos += 1;
            }
        }

        allocator.free(resultado);
        resultado = try novo.toOwnedSlice();
    }

    return resultado;
}

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

    const template = "Olá, {{nome}}! Bem-vindo ao {{site}}. Seu ID é {{id}}.";
    const variaveis = [_]struct { chave: []const u8, valor: []const u8 }{
        .{ .chave = "nome", .valor = "Maria" },
        .{ .chave = "site", .valor = "Zig Brasil" },
        .{ .chave = "id", .valor = "12345" },
    };

    const resultado = try renderTemplate(allocator, template, &variaveis);
    defer allocator.free(resultado);

    try stdout.print("Template: {s}\n", .{template});
    try stdout.print("Resultado: {s}\n", .{resultado});
}

Saída esperada:

Template: Olá, {{nome}}! Bem-vindo ao {{site}}. Seu ID é {{id}}.
Resultado: Olá, Maria! Bem-vindo ao Zig Brasil. Seu ID é 12345.

Veja Também

Continue aprendendo Zig

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