Is it possible to print a number formatted with th

2019-01-26 07:00发布

问题:

For instance

println!("{}", 10_000_000);

results in

10000000

whereas I'd like to format it to look something like

10,000,000

I went through the fmt module documentation, but there's nothing to cover this particular situation. I thought something like this would work

println!("{:,i}", 10_000_000);

but it throws an error

invalid format string: expected `}`, found `,`

回答1:

There isn't, and there probably won't be.

Depending on where you are, the thousands separator may also work like 1,00,00,000, or 1.000.000,000 or some other variant.

Localization isn't the job of the stdlib, plus format! is mostly handled at compile time (though to be fair this could be placed in its runtime portion easily), and you don't want to hard-bake a locale into the program.



回答2:

Another workaround for this is to use the separator crate which implements a .separated_string() method on float, integer and size types. Here is an example:

extern crate separator;
use separator::Separatable;

fn main() {
    let x1: u16 = 12345;
    let x2: u64 = 4242424242;
    let x3: u64 = 232323232323;
    println!("Unsigned ints:\n{:>20}\n{:>20}\n{:>20}\n", x1.separated_string(), x2.separated_string(), x3.separated_string());

    let x1: i16 = -12345;
    let x2: i64 = -4242424242;
    let x3: i64 = -232323232323;
    println!("Signed ints:\n{:>20}\n{:>20}\n{:>20}\n", x1.separated_string(), x2.separated_string(), x3.separated_string());


    let x1: f32 = -424242.4242;
    let x2: f64 = 23232323.2323;
    println!("Floats:\n{:>20}\n{:>20}\n", x1.separated_string(), x2.separated_string());


    let x1: usize = 424242;
    // let x2: isize = -2323232323;  // Even though the docs say so, the traits seem not to be implemented for isize
    println!("Size types:\n{:>20}\n", x1.separated_string());        
}

Which gives you the following output:

Unsigned ints:
              12,345
       4,242,424,242
     232,323,232,323

Signed ints:
             -12,345
      -4,242,424,242
    -232,323,232,323

Floats:
         -424,242.44
     23,232,323.2323

Size types:
             424,242

Note that aligning floats like this is not easy since separated_string() returns a string. However, this is a relatively quick way to get separated numbers.



回答3:

Regarding a custom function, I played around with this and here are some ideas:

use std::str;

fn main() {
    let i = 10_000_000i;
    println!("{}", decimal_mark1(i.to_string()));
    println!("{}", decimal_mark2(i.to_string()));
    println!("{}", decimal_mark3(i.to_string()));
}

fn decimal_mark1(s: String) -> String {
    let bytes: Vec<_> = s.bytes().rev().collect();
    let chunks: Vec<_> = bytes.chunks(3).map(|chunk| str::from_utf8(chunk).unwrap()).collect();
    let result: Vec<_> = chunks.connect(" ").bytes().rev().collect();
    String::from_utf8(result).unwrap()
}

fn decimal_mark2(s: String) -> String {
    let mut result = String::with_capacity(s.len() + ((s.len() - 1) / 3));
    let mut i = s.len();
    for c in s.chars() {
        result.push(c);
        i -= 1;
        if i > 0 && i % 3 == 0 {
            result.push(' ');
        }
    }
    result
}

fn decimal_mark3(s: String) -> String {
    let mut result = String::with_capacity(s.len() + ((s.len() - 1) / 3));
    let first = s.len() % 3;
    result.push_str(s.slice_to(first));
    for chunk in s.slice_from(first).as_bytes().chunks(3) {
        if !result.is_empty() {
            result.push(' ');
        }
        result.push_str(str::from_utf8(chunk).unwrap());
    }
    result
}

Playpen: http://is.gd/UigzCf

Comments welcome, none of them feels really nice.



标签: rust