Introdução
Parsear URLs (Uniform Resource Locators) é uma tarefa fundamental em programação web e de rede. Em Zig, a biblioteca padrão oferece std.Uri para decompor URLs em seus componentes: esquema, host, porta, caminho, query string e fragmento.
Nesta receita, você aprenderá a parsear, construir e manipular URLs em Zig.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
Parsear uma URL Completa
Use std.Uri.parse para decompor uma URL em seus componentes:
const std = @import("std");
pub fn main() !void {
const url = "https://api.exemplo.com:8443/v2/users?page=1&limit=20#resultados";
const uri = try std.Uri.parse(url);
std.debug.print("URL completa: {s}\n\n", .{url});
std.debug.print("Esquema: {s}\n", .{uri.scheme});
std.debug.print("Host: {s}\n", .{uri.host.?.percent_encoded});
std.debug.print("Porta: {?d}\n", .{uri.port});
std.debug.print("Caminho: {s}\n", .{uri.path.percent_encoded});
std.debug.print("Query: {s}\n", .{uri.query.?.percent_encoded});
std.debug.print("Fragmento: {s}\n", .{uri.fragment.?.percent_encoded});
}
Saída esperada
URL completa: https://api.exemplo.com:8443/v2/users?page=1&limit=20#resultados
Esquema: https
Host: api.exemplo.com
Porta: 8443
Caminho: /v2/users
Query: page=1&limit=20
Fragmento: resultados
Parsear Query String
Extraia parâmetros individuais da query string:
const std = @import("std");
const QueryParam = struct {
key: []const u8,
value: []const u8,
};
fn parseQueryString(
allocator: std.mem.Allocator,
query: []const u8,
) ![]QueryParam {
var params = std.ArrayList(QueryParam).init(allocator);
errdefer params.deinit();
var iter = std.mem.splitScalar(u8, query, '&');
while (iter.next()) |pair| {
if (pair.len == 0) continue;
if (std.mem.indexOfScalar(u8, pair, '=')) |eq_pos| {
try params.append(.{
.key = pair[0..eq_pos],
.value = pair[eq_pos + 1 ..],
});
} else {
try params.append(.{
.key = pair,
.value = "",
});
}
}
return params.toOwnedSlice();
}
fn getParam(params: []const QueryParam, key: []const u8) ?[]const u8 {
for (params) |p| {
if (std.mem.eql(u8, p.key, key)) {
return p.value;
}
}
return null;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const url = "https://busca.exemplo.com/search?q=zig+lang&page=2&lang=pt-BR&safe=on";
const uri = try std.Uri.parse(url);
const query = uri.query.?.percent_encoded;
const params = try parseQueryString(allocator, query);
defer allocator.free(params);
std.debug.print("Parâmetros da URL:\n", .{});
for (params) |p| {
std.debug.print(" {s} = {s}\n", .{ p.key, p.value });
}
// Acessar parâmetro específico
if (getParam(params, "q")) |valor| {
std.debug.print("\nBusca por: {s}\n", .{valor});
}
if (getParam(params, "page")) |valor| {
std.debug.print("Página: {s}\n", .{valor});
}
}
Saída esperada
Parâmetros da URL:
q = zig+lang
page = 2
lang = pt-BR
safe = on
Busca por: zig+lang
Página: 2
Construir URLs
Monte URLs a partir de componentes:
const std = @import("std");
fn buildUrl(
allocator: std.mem.Allocator,
scheme: []const u8,
host: []const u8,
port: ?u16,
path: []const u8,
query_params: ?[]const [2][]const u8,
) ![]u8 {
var url = std.ArrayList(u8).init(allocator);
errdefer url.deinit();
const writer = url.writer();
// Esquema
try writer.print("{s}://", .{scheme});
// Host
try writer.writeAll(host);
// Porta (se especificada)
if (port) |p| {
try writer.print(":{d}", .{p});
}
// Caminho
if (path.len > 0 and path[0] != '/') {
try writer.writeByte('/');
}
try writer.writeAll(path);
// Query string
if (query_params) |params| {
try writer.writeByte('?');
for (params, 0..) |param, i| {
if (i > 0) try writer.writeByte('&');
try writer.print("{s}={s}", .{ param[0], param[1] });
}
}
return url.toOwnedSlice();
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const params = [_][2][]const u8{
.{ "formato", "json" },
.{ "idioma", "pt-BR" },
.{ "limite", "50" },
};
const url = try buildUrl(
allocator,
"https",
"api.exemplo.com",
8443,
"/v2/dados",
¶ms,
);
defer allocator.free(url);
std.debug.print("URL construída: {s}\n", .{url});
}
Saída esperada
URL construída: https://api.exemplo.com:8443/v2/dados?formato=json&idioma=pt-BR&limite=50
Decodificar URL Percent-Encoding
Decodifique caracteres especiais em URLs:
const std = @import("std");
fn urlDecode(allocator: std.mem.Allocator, input: []const u8) ![]u8 {
var result = std.ArrayList(u8).init(allocator);
errdefer result.deinit();
var i: usize = 0;
while (i < input.len) {
if (input[i] == '%' and i + 2 < input.len) {
const high = std.fmt.charToDigit(input[i + 1], 16) catch {
try result.append(input[i]);
i += 1;
continue;
};
const low = std.fmt.charToDigit(input[i + 2], 16) catch {
try result.append(input[i]);
i += 1;
continue;
};
try result.append(@as(u8, high) << 4 | low);
i += 3;
} else if (input[i] == '+') {
try result.append(' ');
i += 1;
} else {
try result.append(input[i]);
i += 1;
}
}
return result.toOwnedSlice();
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const encoded = "Ol%C3%A1+mundo%21+Programa%C3%A7%C3%A3o+em+Zig";
const decoded = try urlDecode(allocator, encoded);
defer allocator.free(decoded);
std.debug.print("Codificado: {s}\n", .{encoded});
std.debug.print("Decodificado: {s}\n", .{decoded});
}
Saída esperada
Codificado: Ol%C3%A1+mundo%21+Programa%C3%A7%C3%A3o+em+Zig
Decodificado: Olá mundo! Programação em Zig
Validar URLs
Verifique se uma string é uma URL válida:
const std = @import("std");
fn isValidUrl(url: []const u8) bool {
const uri = std.Uri.parse(url) catch return false;
// Verificar se tem esquema
if (uri.scheme.len == 0) return false;
// Verificar se tem host
if (uri.host == null) return false;
// Verificar esquemas permitidos
const valid_schemes = [_][]const u8{ "http", "https", "ftp", "ws", "wss" };
for (&valid_schemes) |scheme| {
if (std.mem.eql(u8, uri.scheme, scheme)) return true;
}
return false;
}
pub fn main() void {
const urls = [_][]const u8{
"https://www.exemplo.com/pagina",
"http://localhost:8080",
"ftp://arquivos.exemplo.com/doc.pdf",
"não é uma url",
"mailto:email@exemplo.com",
"ws://chat.exemplo.com/ws",
};
for (&urls) |url| {
const valid = isValidUrl(url);
std.debug.print("{s}: {s}\n", .{
url,
if (valid) "VÁLIDA" else "INVÁLIDA",
});
}
}
Dicas e Boas Práticas
Use
std.Uri.parse: Não tente parsear URLs manualmente; a biblioteca padrão trata casos complexos.Cuidado com encoding: URLs podem conter caracteres percent-encoded. Sempre decodifique antes de usar.
Valide antes de usar: Sempre verifique se a URL é válida antes de tentar conectar.
Porta padrão: Se a porta não estiver na URL, use a porta padrão do esquema (80 para HTTP, 443 para HTTPS).
Receitas Relacionadas
- Como fazer requisições HTTP GET em Zig - Usar URLs parseadas
- Como fazer requisições HTTP POST em Zig - Enviar dados para URLs
- Como fazer lookup DNS em Zig - Resolver o host da URL
- Como usar Base64 em Zig - Encoding relacionado