rust 将数字格式化/转换为任意基数的字符串(包括十进制或十六进制以外的基数)

1cklez4t  于 2023-02-12  发布在  其他
关注(0)|答案(4)|浏览(222)

目前,我使用下面的代码返回二进制(基数为2)、八进制(基数为8)或十六进制(基数为16)字符串形式的数字。

fn convert(inp: u32, out: u32, numb: &String) -> Result<String, String> {
    match isize::from_str_radix(numb, inp) {
        Ok(a) => match out {
            2 => Ok(format!("{:b}", a)),
            8 => Ok(format!("{:o}", a)),
            16 => Ok(format!("{:x}", a)),
            10 => Ok(format!("{}", a)),
            0 | 1 => Err(format!("No base lower than 2!")),
            _ => Err(format!("printing in this base is not supported")),
        },
        Err(e) => Err(format!(
            "Could not convert {} to a number in base {}.\n{:?}\n",
            numb, inp, e
        )),
    }
}

现在我想替换内部的match语句,这样我就可以将数字作为任意基数的字符串(例如,base 3)返回。是否有内置函数可以将数字转换为任意给定的基数,类似于JavaScript的Number.toString() method

bt1cpqcv

bt1cpqcv1#

目前,您无法使用标准库执行此操作,但可以:

  • 使用我的板条箱radix_fmt
  • 或者推出您自己的实现:
fn format_radix(mut x: u32, radix: u32) -> String {
    let mut result = vec![];

    loop {
        let m = x % radix;
        x = x / radix;

        // will panic if you use a bad radix (< 2 or > 36).
        result.push(std::char::from_digit(m, radix).unwrap());
        if x == 0 {
            break;
        }
    }
    result.into_iter().rev().collect()
}

fn main() {
    assert_eq!(format_radix(1234, 10), "1234");
    assert_eq!(format_radix(1000, 10), "1000");
    assert_eq!(format_radix(0, 10), "0");
}
oyt4ldly

oyt4ldly2#

如果你想增加一点性能,你可以创建一个struct并为它实现DisplayDebug。这样就避免了分配String。为了最大限度的过度设计,你也可以用一个堆栈分配的数组来代替Vec
以下是应用了这些更改的Boiethios' answer

struct Radix {
    x: i32,
    radix: u32,
}

impl Radix {
    fn new(x: i32, radix: u32) -> Result<Self, &'static str> {
        if radix < 2 || radix > 36 {
            Err("Unnsupported radix")
        } else {
            Ok(Self { x, radix })
        }
    }
}

use std::fmt;

impl fmt::Display for Radix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut x = self.x;
        // Good for binary formatting of `u128`s
        let mut result = ['\0'; 128];
        let mut used = 0;
        let negative = x < 0;
        if negative {
            x*=-1;
        }
        let mut x = x as u32;
        loop {
            let m = x % self.radix;
            x /= self.radix;

            result[used] = std::char::from_digit(m, self.radix).unwrap();
            used += 1;

            if x == 0 {
                break;
            }
        }

        if negative {
            write!(f, "-")?;
        }

        for c in result[..used].iter().rev() {
            write!(f, "{}", c)?;
        }

        Ok(())
    }
}

fn main() {
    assert_eq!(Radix::new(1234, 10).to_string(), "1234");
    assert_eq!(Radix::new(1000, 10).to_string(), "1000");
    assert_eq!(Radix::new(0, 10).to_string(), "0");
}

这仍然可以通过以下方式优化:

  • 创建ASCII数组而不是char数组
  • 不为零-初始化数组

由于这些方法需要unsafearraybuf这样的外部机箱,所以我没有将它们包括在内,您可以在标准库的内部实现细节中看到示例代码。

g6baxovj

g6baxovj3#

第一个注解的扩展解决方案,未将参数x绑定为u32

fn format_radix(mut x: u128, radix: u32) -> String {
    let mut result = vec![];

    loop {
        let m = x % radix as u128;
        x = x / radix as u128;

        // will panic if you use a bad radix (< 2 or > 36).
        result.push(std::char::from_digit(m as u32, radix).unwrap());
        if x == 0 {
            break;
        }
    }
    result.into_iter().rev().collect()
}
mzillmmw

mzillmmw4#

这比另一个答案要快:

use std::char::from_digit;

fn encode(mut n: u32, r: u32) -> Option<String> {
   let mut s = String::new();
   loop {
      if let Some(c) = from_digit(n % r, r) {
         s.insert(0, c)
      } else {
         return None
      }
      n /= r;
      if n == 0 {
         break
      }
   }
   Some(s)
}

注意我也试过这些,但是它们比较慢:

  • https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.push_front
  • https://doc.rust-lang.org/std/string/struct.String.html#method.push
  • https://doc.rust-lang.org/std/vec/struct.Vec.html#method.insert

相关问题