Показать фрагмент u8 в шестнадцатеричном представлении

Мне нужно преобразовать &[u8] в шестнадцатеричное представление. Например [ A9, 45, FF, 00 ... ].

Признак std::fmt::UpperHex не реализован для срезов (поэтому я не могу использовать std::fmt::format). Rust имеет свойство serialize::hex::ToHex, которое преобразует &[u8] в шестнадцатеричную строку, но мне нужно представление с отдельными байтами.

Я могу реализовать черту UpperHex для &[u8] самостоятельно, но я не уверен, каково это было бы. Каков самый канонический способ сделать это?

Ответ 1

Руст 1.26.0 и выше

The :x? Форматер "debug with шестнадцатеричные целые" может использоваться:

let data = b"hello";
println!("{:x?}", data);
println!("{:X?}", data);
[68, 65, 6c, 6c, 6f]
[68, 65, 6C, 6C, 6F]

Его также можно комбинировать с модификатором pretty:

let data = b"hello";
println!("{:#x?}", data);
println!("{:#X?}", data);
[
    0x68,
    0x65,
    0x6c,
    0x6c,
    0x6f
]
[
    0x68,
    0x65,
    0x6C,
    0x6C,
    0x6F
]

Если вам нужно больше контроля или поддержка старых версий Rust, продолжайте читать.

Руст 1.0 и выше

use std::fmt::Write;

fn main() {
    let mut s = String::new();
    for &byte in "Hello".as_bytes() {
        write!(&mut s, "{:X} ", byte).expect("Unable to write");
    }

    println!("{}", s);
}

Это можно представить, реализовав одну из особенностей форматирования (fmt::Debug, fmt::Display, fmt::LowerHex, fmt::UpperHex и т.д.) В структуре и имея небольшой конструктор:

use std::fmt;

struct HexSlice<'a>(&'a [u8]);

impl<'a> HexSlice<'a> {
    fn new<T>(data: &'a T) -> HexSlice<'a> 
        where T: ?Sized + AsRef<[u8]> + 'a
    {
        HexSlice(data.as_ref())
    }
}

// You can even choose to implement multiple traits, like Lower and UpperHex
impl<'a> fmt::Display for HexSlice<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for byte in self.0 {
            // Decide if you want to pad out the value here
            write!(f, "{:X} ", byte)?;
        }
        Ok(())
    }
}

fn main() {
    // To get a 'String'
    let s = format!("{}", HexSlice::new("Hello"));

    // Or print it directly
    println!("{}", HexSlice::new("world"));

    // Works with 
    HexSlice::new("Hello");              // string slices (&str)
    HexSlice::new(b"Hello");             // byte slices (&[u8])
    HexSlice::new(&"World".to_string()); // References to String
    HexSlice::new(&vec![0x00, 0x01]);    // References to Vec<u8>  
}

Ответ 2

Так как принятый ответ не работает на Rust 1.0 stable, вот моя попытка. Должен быть без выделения и, следовательно, достаточно быстрым. Это в основном форматтер для [u8], но из-за правил согласованности мы должны обернуть [u8] к самоопределяемому типу ByteBuf(&[u8]), чтобы использовать его:

struct ByteBuf<'a>(&'a [u8]);

impl<'a> std::fmt::LowerHex for ByteBuf<'a> {
    fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        for byte in self.0 {
            try!( fmtr.write_fmt(format_args!("{:02x}", byte)));
        }
        Ok(())
    }
}

Использование:

let buff = [0_u8; 24];
println!("{:x}", ByteBuf(&buff));

Ответ 3

Здесь есть ящик: hex-slice.

Например:

extern crate hex_slice;
use hex_slice::AsHex;

fn main() {
    let foo = vec![0u32, 1, 2 ,3];
    println!("{:02x}", foo.as_hex());
}

Ответ 4

Исполняемый шаблон о принятом ответе с байтовым массивом.

fn main() {
    // Template
    let bytes = [
        177, 147, 134, 198, 163, 82, 51, 22,
        41, 118, 161, 105, 142, 86, 191, 146,
        250, 11, 152, 10, 58, 75, 238, 24,
        21, 46, 68, 18, 3, 112, 162, 50,
    ];

    // String to extend.
    let mut hex = String::new();
    hex.extend(bytes.iter().map(|byte| format!("{:02x}", byte)));

    // Print it.
    println!("{:}", hex);
}