代码之家  ›  专栏  ›  技术社区  ›  Meltinglava

是否有内置函数可以将数字转换为任意基中的字符串?

  •  8
  • Meltinglava  · 技术社区  · 6 年前

    我想更换内部 match 语句,并在字母表用完之前处理所有值。我知道我可以自己写,但我想使用内置函数。

    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
            )),
        }
    }
    
    3 回复  |  直到 6 年前
        1
  •  8
  •   Boiethios    6 年前

    现在, you cannot do it using the standard library ,但您可以:

    • 使用我的板条箱 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");
      }
      
        2
  •  2
  •   Meltinglava    6 年前

    如果您想提高一点性能,可以创建一个结构并实现 Display Debug 为了它。这样可以避免分配 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 大堆
    • 初始化阵列时不是零

    因为这些途径需要 unsafe 或外部板条箱,如 arraybuf ,我没有把它们包括在内。您可以在中看到示例代码 internal implementation details of the standard library

        3
  •  -2
  •   Zombo    3 年前

    这比另一个答案更快:

    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)
    }
    

    注意:我也尝试了这些,但速度较慢: