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)
}
注意事项
- 某些终端特殊字符可能无法准确计算宽度
- 组合字符的宽度计算可能因环境而异
- 对于非常复杂的ANSI序列,建议在实际终端中测试
ansi-width
是终端UI开发中的实用工具,特别适合需要精确控制文本对齐和布局的场景。