Rust字符串宽度计算库ansi-width的使用,支持ANSI转义字符的高效文本宽度测量

Rust字符串宽度计算库ansi-width的使用,支持ANSI转义字符的高效文本宽度测量

ansi-width是一个用于测量字符串在终端显示时宽度的Rust库。它能够正确处理ASCII、Unicode字符以及ANSI转义序列。

功能特点

  • 对于ASCII字符串,宽度等于字符串的字节长度
  • 正确处理多列宽度的Unicode字符(如CJK字符、emoji等)
  • 忽略ANSI转义代码,只计算可见字符的宽度
  • 基于unicode-width crate构建,增加了对ANSI转义代码的支持

局限性

  • 无法确定终端模拟器中TAB字符的宽度
  • 退格键也被视为零宽度

示例代码

use ansi_width::ansi_width;

// ASCII字符串
assert_eq!(ansi_width("123456"), 6);

// 重音字符
assert_eq!(ansi_width("café"), 4);

// Emoji (2个螃蟹emoji)
assert_eq!(ansi_width("🦀🦀"), 4);

// CJK字符("Nǐ hǎo"或中文"你好")
assert_eq!(ansi_width("你好"), 4);

// ANSI颜色
assert_eq!(ansi_width("\u{1b}[31mRed\u{1b}[0m"), 3);

// ANSI超链接
assert_eq!(
    ansi_width("\x1b]8;;http://example.com\x1b\\This is a link\x1b]8;;\x1b\\"),
    14
);

完整示例demo

use ansi_width::ansi_width;

fn main() {
    // 测试ASCII字符串
    let ascii_str = "Hello, world!";
    println!("'{}' 的宽度: {}", ascii_str, ansi_width(ascii_str));
    
    // 测试带重音字符的字符串
    let accented_str = "café au lait";
    println!("'{}' 的宽度: {}", accented_str, ansi_width(accented_str));
    
    // 测试emoji
    let emoji_str = "🦀 Rust 🚀";
    println!("'{}' 的宽度: {}", emoji_str, ansi_width(emoji_str));
    
    // 测试CJK字符
    let cjk_str = "你好世界";
    println!("'{}' 的宽度: {}", cjk_str, ansi_width(cjk_str));
    
    // 测试带ANSI颜色代码的字符串
    let colored_str = "\u{1b}[31m红色\u{1b}[0m文字";
    println!("'{}' 的宽度: {}", colored_str, ansi_width(colored_str));
    
    // 测试带ANSI超链接的字符串
    let linked_str = "\x1b]8;;https://rust-lang.org\x1b\\Rust官网\x1b]8;;\x1b\\";
    println!("'{}' 的宽度: {}", linked_str, ansi_width(linked_str));
}

安装

在项目目录中运行以下Cargo命令:

cargo add ansi-width

或者在Cargo.toml中添加以下行:

ansi-width = "0.1.0"

替代方案

  • str::len: 仅返回字节长度,仅适用于ASCII字符
  • unicode-width: 不支持ANSI转义字符
  • textwrap::core::display_width: 功能类似,但需要引入整个textwrap库
  • console::measure_text_width: 功能类似,但内部实现不同

ansi-width库特别适合需要在终端中精确计算带有ANSI转义序列的字符串显示宽度的场景。


1 回复

ansi-width: Rust中支持ANSI转义字符的字符串宽度计算库

ansi-width是一个专门用于计算包含ANSI转义序列的字符串显示宽度的Rust库。它特别适合终端应用程序开发,能够正确处理彩色文本和各种控制字符的宽度计算。

功能特点

  • 支持ANSI转义序列(如颜色代码)
  • 正确处理Unicode字符宽度
  • 高效计算字符串显示宽度
  • 忽略不影响显示的控制字符

安装方法

在Cargo.toml中添加依赖:

[dependencies]
ansi-width = "0.1"

基本使用方法

use ansi_width::ansi_width;

fn main() {
    let text = "\x1b[31m红色\x1b[0m文本";
    let width = ansi_width(text);
    println!("字符串的显示宽度: {}", width); // 输出: 4
}

高级用法

处理多行文本

use ansi_width::ansi_width;

let multiline = "第一行\n\x1b[32m第二行\x1b[0m\n第三行";
for line in multiline.lines() {
    println!("行宽度: {}", ansi_width(line));
}

结合unicode-width处理复杂字符

use ansi_width::ansi_width;

let complex_text = "\x1b[33m🦀\x1b[0m Rust";
println!("宽度: {}", ansi_width(complex_text)); // 输出: 6 (🦀占2个字符宽度)

性能考虑

对于需要频繁计算宽度的场景,可以先将ANSI转义序列剥离:

use ansi_width::{ansi_width, ansi_strip};

let text = "\x1b[1;34m重要信息\x1b[0m";
let stripped = ansi_strip(text);
let width = stripped.chars().map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0)).sum::<usize>();

实际应用示例

use ansi_width::ansi_width;

fn align_right(text: &str, width: usize) -> String {
    let actual_width = ansi_width(text);
    if actual_width >= width {
        return text.to_string();
    }
    let spaces = " ".repeat(width - actual_width);
    format!("{}{}", spaces, text)
}

let colored_text = "\x1b[35m对齐文本\x1b[0m";
println!("[{}]", align_right(colored_text, 10));

完整示例demo

下面是一个结合多种功能的完整示例:

use ansi_width::{ansi_width, ansi_strip};

fn main() {
    // 基本使用示例
    let colored_text = "\x1b[31mHello\x1b[0m \x1b[32mWorld\x1b[0m";
    println!("基本示例:");
    println!("文本: {}", colored_text);
    println!("显示宽度: {}", ansi_width(colored_text));
    
    // 多行文本处理
    println!("\n多行文本处理:");
    let multiline = "Line1\n\x1b[33mLine2\x1b[0m\nLine3";
    for (i, line) in multiline.lines().enumerate() {
        println!("行{} 宽度: {}", i+1, ansi_width(line));
    }
    
    // 复杂字符处理
    println!("\n复杂字符处理:");
    let emoji_text = "\x1b[34m😊\x1b[0m 表情符号";
    println!("文本: {}", emoji_text);
    println!("显示宽度: {}", ansi_width(emoji_text));
    
    // 性能优化示例
    println!("\n性能优化示例:");
    let performance_text = "\x1b[1;35m性能测试\x1b[0m";
    let stripped = ansi_strip(performance_text);
    println!("原始文本: {}", performance_text);
    println!("剥离ANSI后: {}", stripped);
    
    // 文本对齐应用
    println!("\n文本对齐应用:");
    let align_text = "\x1b[36m对齐演示\x1b[0m";
    println!("[{}]", right_align(align_text, 20));
}

fn right_align(text: &str, width: usize) -> String {
    let actual_width = ansi_width(text);
    if actual_width >= width {
        return text.to_string();
    }
    let spaces = " ".repeat(width - actual_width);
    format!("{}{}", spaces, text)
}

注意事项

  1. 某些终端特殊字符可能无法准确计算宽度
  2. 组合字符的宽度计算可能因环境而异
  3. 对于非常复杂的ANSI序列,建议在实际终端中测试

ansi-width是终端UI开发中的实用工具,特别适合需要精确控制文本对齐和布局的场景。

回到顶部