Возможно ли напечатать номер, отформатированный с разделителем тысяч в Rust?

Например,

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

приводит к

10000000

тогда как я хотел бы отформатировать его, чтобы он выглядел примерно как

10,000,000

Я просмотрел документацию к модулю fmt, но не было ничего, что могло бы охватить эту конкретную ситуацию. Я думал, что-то вроде этого будет работать

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

но выдает ошибку

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

Ответ 1

num_format crate решит эту проблему для вас. Добавьте свою локаль, и она сделает волшебство.

Ответ 2

Нет, и, вероятно, этого не будет.

В зависимости от того, где вы находитесь, разделитель тысяч может также работать как 1,00,00,000 или 1.000.000,000 или какой-либо другой вариант.

Локализация - это не работа stdlib, плюс format! в основном обрабатывается во время компиляции (хотя, честно говоря, это может быть легко помещено в ее часть времени выполнения), и вы не хотите сильно испечь locale в программу.

Ответ 3

Другим обходным решением для этого является использование separator crate, который реализует метод .separated_string() для типов с плавающей запятой, целых чисел и размеров. Вот пример:

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

Что дает следующий вывод:

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

Обратите внимание, что выравнивание плавающих объектов не так просто, так как separated_string() возвращает строку. Тем не менее, это относительно быстрый способ получить отдельные числа.

Ответ 4

Что касается пользовательской функции, я играл с ней, и вот несколько идей:

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
}

Детский манеж: http://is.gd/UigzCf

Комментарии приветствуются, никто из них не чувствует себя очень хорошо.

Ответ 5

Вот наивная реализация для целых чисел

fn pretty_print_int(i: isize) {
    let mut s = String::new();
    let i_str = i.to_string();
    let a = i_str.chars().rev().enumerate();
    for (idx, val) in a {
        if idx != 0 && idx % 3 == 0 {
            s.insert(0, ',');
        }
        s.insert(0, val);
    }
    println!("{}", s);
}

pretty_print_int(10_000_000);
// 10,000,000

Если вы хотите сделать это немного более общим для целых чисел, вы можете использовать черту num :: Integer trait

extern crate num;

use num::Integer;

fn pretty_print_int<T: Integer>(i: T) {
    ...
}