以十六进制表示形式显示 u8 切片

Posted

技术标签:

【中文标题】以十六进制表示形式显示 u8 切片【英文标题】:Show u8 slice in hex representation 【发布时间】:2015-02-23 08:43:19 【问题描述】:

我需要将&[u8] 转换为十六进制表示。例如[ A9, 45, FF, 00 ... ]

特征std::fmt::UpperHex 没有为切片实现(所以我不能使用std::fmt::format)。 Rust 具有 serialize::hex::ToHex 特征,它将 &[u8] 转换为十六进制字符串,但我需要一个带有单独字节的表示。

我可以自己为&[u8] 实现特征UpperHex,但我不确定这将是多么规范。最规范的方法是什么?

【问题讨论】:

当你说“十六进制表示”时,你的意思是你想要一个字符串吗?还是什么类型? 顺便说一句,你不能为&[u8] 实现UpperHex,因为特征和类型都不是“你的”(它们都没有在你的板条箱中定义)。您可以为&[u8] 的新类型实现UpperHex(例如struct HexSlice<'a>(&'a [u8]),但这可能会很不方便。定义一个简单的函数会更好。 Vladimir Matveev,我的意思是创建新类型,其中包含 [u8] 并使用它。谢谢回答。 【参考方案1】:

Rust 1.26.0 及更高版本

可以使用:x?“使用十六进制整数调试”格式化程序:

let data = b"hello";
// lower case
println!(":x?", data);
// upper case
println!(":X?", data);

let data = [0x0, 0x1, 0xe, 0xf, 0xff];
// print the leading zero
println!(":02X?", data);
// It can be combined with the pretty modifier as well
println!(":#04X?", data);

输出:

[68, 65, 6c, 6c, 6f]
[68, 65, 6C, 6C, 6F]
[00, 01, 0E, 0F, FF]
[
    0x00,
    0x01,
    0x0E,
    0x0F,
    0xFF,
]

如果您需要更多控制或需要支持旧版本的 Rust,请继续阅读。

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::Debugfmt::Displayfmt::LowerHexfmt::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 choose to implement multiple traits, like Lower and UpperHex
impl fmt::Display for HexSlice<'_> 
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
        for byte in self.0 
            // Decide if you want to pad the value or have spaces inbetween, etc.
            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>

您甚至可以更花哨并创建一个扩展特征

trait HexDisplayExt 
    fn hex_display(&self) -> HexSlice<'_>;


impl<T> HexDisplayExt for T
where
    T: ?Sized + AsRef<[u8]>,

    fn hex_display(&self) -> HexSlice<'_> 
        HexSlice::new(self)
    


fn main() 
    println!("", "world".hex_display());

【讨论】:

格式字符串不应该是":02X",以确保为每个字符打印两个十六进制字符(如果需要,包括第一个0)? @phoenix 是的,如果需要的话。这就是我所说的评论“决定是否要在此处填充值”的意思。 您需要":02x?"。它仍然将其打印为逗号分隔的列表。 :-/ 是的,提醒人们如果您要将生成的字符串粘贴到十六进制编辑器等,您需要使用 :02X? 进行零填充。只是浪费了很多时间来弄清楚为什么我得到了垃圾值。【参考方案2】:

由于接受的答案在 Rust 1.0 稳定版上不起作用,这是我的尝试。应该是无分配的,因此相当快。这基本上是 [u8] 的格式化程序,但是由于一致性规则,我们必须将 [u8] 包装成自定义类型 ByteBuf(&amp;[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() 
    let data = vec![27u8, 34, 42, 56, 88];
    println!(
        "0x",
        data.iter()
            .map(|b| format!(":02X", *b))
            .collect::<Vec::<_>>()
            .join("")
    );

打印0x1B222A3858

【讨论】:

以上是关于以十六进制表示形式显示 u8 切片的主要内容,如果未能解决你的问题,请参考以下文章

以IEEE754短浮点数格式表示十进制数:-3.125 要求写出过程,并最终用十六进制缩写形式表示

C++ 查找所有基数,使得这些基数中的 P 以 Q 的十进制表示形式结尾

电脑常识二进制数表达

c语言中返回无符号整数以二进制位表示的形式

MFC 十六进制以字符串形式直接输出

Java每日一道算法--请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数