起因
某天我在终端里敲命令,突然想:能不能让输出更炫一点?
比如把普通的 HELLO 变成这种大字:
╔═════════════════════════════════════════════════════╗
║ ║
║ ██╗ ██╗ ███████╗ ██╗ ██╗ ██████╗ ║
║ ██║ ██║ ██╔════╝ ██║ ██║ ██╔═══██╗ ║
║ ███████║ █████╗ ██║ ██║ ██║ ██║ ║
║ ██╔══██║ ██╔══╝ ██║ ██║ ██║ ██║ ║
║ ██║ ██║ ███████╗ ███████╗ ███████╗ ╚██████╔╝ ║
║ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚══════╝ ╚═════╝ ║
║ ║
╚═════════════════════════════════════════════════════╝
说干就干,用 Rust 实现一个!
核心思路
其实原理特别简单,就三步:
- 准备字库:把每个字符(A-Z、0-9等)都用 Unicode 方块字符画好,存成一个映射表
- 逐字转换:输入文字后,查表把每个字符转成对应的 ASCII 艺术字
- 按行拼接:把所有字符的每一行拼在一起,最后加个漂亮的边框
项目结构
项目很小,就三个文件:
src/
├── main.rs # 命令行入口,处理参数
├── banner.rs # 核心转换逻辑
└── font.rs # 字库定义
字库设计
首先,我们需要定义每个字符长什么样。用 Unicode 方块字符(█、╗、╔ 等)来画,比传统的 # 或 * 更美观。
每个字符高度固定为 6 行,比如字母 A:
vec![
" █████╗ ", // 第1行
" ██╔══██╗ ", // 第2行
" ██║ ██║ ", // 第3行
" ███████║ ", // 第4行
" ██╔══██║ ", // 第5行
" ╚═╝ ╚═╝ ", // 第6行
]
用一个 HashMap<char, Vec<&str>> 存所有字符,查起来很方便。
核心转换逻辑
关键函数 text_to_ascii,把输入文字转成多行 ASCII 艺术:
pub fn text_to_ascii(text: &str) -> Option<Vec<String>> {
let font_map = get_font_map();
let mut result: Vec<String> = vec![String::new(); FONT_HEIGHT]; // 6个空字符串
for c in text.to_uppercase().chars() {
if let Some(lines) = font_map.get(&c) {
// 把每个字符的每一行追加到结果中
for (i, line) in lines.iter().enumerate() {
result[i].push_str(line);
result[i].push_str(" "); // 字符间距
}
}
}
Some(result)
}
举个例子,输入 "AB":
- 先查
A,得到 6 行字符串,分别追加到 result[0]~result[5] - 再查
B,同样追加到每行后面 - 最终得到 6 行拼好的大字
加个边框更帅气
用 Unicode 制表符画边框:
pub fn generate_banner(text: &str, padding: usize) -> String {
let ascii_lines = text_to_ascii(text)?;
// 画上边框
let top = format!("╔{}╗", "═".repeat(width));
// 画内容行(左右加空格 padding)
// ...
// 画下边框
let bottom = format!("╚{}╝", "═".repeat(width));
}
一个小坑:Unicode 宽度
用普通 len() 计算字符串长度会翻车!因为 █ 这种字符占 3 个字节,但显示宽度只有 1。
所以用了 unicode-width 这个 crate:
use unicode_width::UnicodeWidthStr;
let width = "█████╗".width(); // 正确得到显示宽度
命令行参数处理
没有用 clap 这样的库,直接手写简单处理:
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 || args.contains(&"--help".to_string()) {
print_help();
return;
}
let no_box = args.contains(&"--no-box".to_string());
// ...
}
使用效果
# 编译
cargo build --release
# 运行
./target/release/ascii-banner "RUST"
# 不要边框
./target/release/ascii-banner "2026" --no-box
输出:
╔══════════════════════════════════════════════╗
║ ║
║ ██████╗ ██╗ ██╗ ███████╗ ████████╗ ║
║ ██╔══██╗ ██║ ██║ ██╔════╝ ╚══██╔══╝ ║
║ ██████╔╝ ██║ ██║ ██████╗ ██║ ║
║ ██╔══██╗ ██║ ██║ ╚════██╗ ██║ ║
║ ██║ ██║ ╚██████╔╝ ███████║ ██║ ║
║ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ║
║ ║
╚══════════════════════════════════════════════╝
总结
这个项目虽然简单,但涵盖了几个有趣的点:
- Unicode 方块字符 — 让终端输出更美观
- HashMap 字库 — 灵活可扩展的字符映射
- Unicode 宽度计算 — 处理多字节字符的正确姿势
- 按行拼接思路 — 把二维数组转成一维输出
完整代码已开源:github.com/lispking/ascii-banner
如果你也想让终端输出更酷,不妨试试这个小工具!
Ext Link: https://github.com/lispking/ascii-banner
1
共 1 条评论, 1 页
评论区
写评论要是可以支持中文就很棒了!!!这主要是中文字体的问题,用 ascii 字符在终端中显示中文!!