Introdução
A codificação hexadecimal representa cada byte como dois caracteres (0-9, a-f), sendo essencial para exibir hashes, endereços de memória, cores CSS e dados binários. Zig oferece suporte a hex via std.fmt para formatação e funções para parsing.
Nesta receita, você aprenderá a converter entre bytes e hexadecimal em ambas as direções.
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
Bytes para Hexadecimal
const std = @import("std");
pub fn main() !void {
const dados = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE };
// Usando std.fmt (minúsculas)
std.debug.print("Hex (lower): ", .{});
for (dados) |b| {
std.debug.print("{x:0>2}", .{b});
}
std.debug.print("\n", .{});
// Usando std.fmt (maiúsculas)
std.debug.print("Hex (upper): ", .{});
for (dados) |b| {
std.debug.print("{X:0>2}", .{b});
}
std.debug.print("\n", .{});
// Com separador
std.debug.print("Hex (sep): ", .{});
for (dados, 0..) |b, i| {
if (i > 0) std.debug.print(":", .{});
std.debug.print("{X:0>2}", .{b});
}
std.debug.print("\n", .{});
// Usando fmtSliceHexLower
std.debug.print("Slice hex: {s}\n", .{std.fmt.fmtSliceHexLower(&dados)});
std.debug.print("Slice HEX: {s}\n", .{std.fmt.fmtSliceHexUpper(&dados)});
}
Saída esperada
Hex (lower): deadbeefcafebabe
Hex (upper): DEADBEEFCAFEBABE
Hex (sep): DE:AD:BE:EF:CA:FE:BA:BE
Slice hex: deadbeefcafebabe
Slice HEX: DEADBEEFCAFEBABE
Hexadecimal para Bytes
const std = @import("std");
fn hexParaBytes(hex_str: []const u8, output: []u8) ![]u8 {
if (hex_str.len % 2 != 0) return error.InvalidLength;
if (output.len < hex_str.len / 2) return error.BufferTooSmall;
var i: usize = 0;
while (i < hex_str.len) : (i += 2) {
const high = try charParaNibble(hex_str[i]);
const low = try charParaNibble(hex_str[i + 1]);
output[i / 2] = (high << 4) | low;
}
return output[0 .. hex_str.len / 2];
}
fn charParaNibble(c: u8) !u4 {
return switch (c) {
'0'...'9' => @intCast(c - '0'),
'a'...'f' => @intCast(c - 'a' + 10),
'A'...'F' => @intCast(c - 'A' + 10),
else => return error.InvalidHexChar,
};
}
pub fn main() !void {
const hex_str = "48656c6c6f20576f726c6421";
var buf: [256]u8 = undefined;
const bytes = try hexParaBytes(hex_str, &buf);
std.debug.print("Hex: {s}\n", .{hex_str});
std.debug.print("Texto: {s}\n", .{bytes});
std.debug.print("Bytes: ", .{});
for (bytes) |b| std.debug.print("{X:0>2} ", .{b});
std.debug.print("\n", .{});
}
Converter Cores CSS
const std = @import("std");
const Cor = struct {
r: u8,
g: u8,
b: u8,
pub fn fromHex(hex: []const u8) !Cor {
const offset: usize = if (hex[0] == '#') 1 else 0;
const h = hex[offset..];
if (h.len != 6) return error.InvalidColor;
return .{
.r = try parsearByte(h[0..2]),
.g = try parsearByte(h[2..4]),
.b = try parsearByte(h[4..6]),
};
}
fn parsearByte(hex: []const u8) !u8 {
return std.fmt.parseInt(u8, hex, 16) catch return error.InvalidHex;
}
pub fn toHex(self: Cor, buf: []u8) []u8 {
return std.fmt.bufPrint(buf, "#{X:0>2}{X:0>2}{X:0>2}", .{ self.r, self.g, self.b }) catch "";
}
};
pub fn main() !void {
const cores_hex = [_][]const u8{
"#FF0000", // Vermelho
"#00FF00", // Verde
"#0000FF", // Azul
"#FFA500", // Laranja
"#800080", // Roxo
};
std.debug.print("{s:<10} {s:>5} {s:>5} {s:>5}\n", .{ "Hex", "R", "G", "B" });
std.debug.print("{s}\n", .{"-" ** 28});
for (&cores_hex) |hex| {
if (Cor.fromHex(hex)) |cor| {
std.debug.print("{s:<10} {d:>5} {d:>5} {d:>5}\n", .{ hex, cor.r, cor.g, cor.b });
} else |_| {
std.debug.print("{s:<10} (inválida)\n", .{hex});
}
}
}
Hex Dump de Dados
const std = @import("std");
fn hexDump(dados: []const u8) void {
var offset: usize = 0;
while (offset < dados.len) {
// Offset
std.debug.print("{X:0>8} ", .{offset});
// Hex bytes
const end = @min(offset + 16, dados.len);
for (offset..end) |i| {
std.debug.print("{X:0>2} ", .{dados[i]});
if (i == offset + 7) std.debug.print(" ", .{});
}
// Padding se linha incompleta
if (end - offset < 16) {
const faltando = 16 - (end - offset);
for (0..faltando) |_| std.debug.print(" ", .{});
if (end - offset <= 8) std.debug.print(" ", .{});
}
// ASCII
std.debug.print(" |", .{});
for (dados[offset..end]) |b| {
const c: u8 = if (b >= 32 and b < 127) b else '.';
std.debug.print("{c}", .{c});
}
std.debug.print("|\n", .{});
offset = end;
}
}
pub fn main() !void {
const texto = "Zig é uma linguagem de programação de sistemas!";
std.debug.print("Hex dump de: \"{s}\"\n\n", .{texto});
hexDump(texto);
}
Dicas e Boas Práticas
fmtSliceHexLower/Upper: A forma mais simples de converter um slice para hex.Sempre use padding
{X:0>2}: Garante que bytes menores que 0x10 tenham zero à esquerda.Valide o comprimento: Strings hex devem ter comprimento par.
Case insensitive no parse: Aceite tanto
a-fquantoA-Fao decodificar.
Receitas Relacionadas
- Base64 Encoding/Decoding - Codificação Base64
- Converter entre Bases Numéricas - Conversão de bases
- Hashing SHA256/MD5 - Hashes em hex
- Formatar Strings com std.fmt - Formatação