在 C# 中使用 ANSI 转义码 —— Console.Write、Spectre.Console、原始 \x1b
C# 字符串字面量原生支持 `\x1b` —— `Console.Write("\x1b[31merror\x1b[0m")` 在 macOS / Linux 以及 Windows 10 1709+ / Windows Terminal / Windows 11(Conhost 默认解析 VT)上均按字节透传。需要更丰富功能时,`Spectre.Console` 是现代 .NET 事实标准 TUI / 样式控制台库(标记语法 `[red]error[/]`、表格、进度条、提示),`Pastel` 提供单行扩展方法(`"hello".Pastel(Color.Red)`)。在 1607 之前的旧 Windows 上,首次写入前需通过 `SetConsoleMode` P/Invoke 启用 `ENABLE_VIRTUAL_TERMINAL_PROCESSING`。设置 `Console.OutputEncoding = Encoding.UTF8` 可保持制表符与表情符号完整。
推荐库
- Spectre.Console
事实标准的现代 .NET 控制台工具包 —— 标记语法(`[red]error[/]`、`[bold yellow on blue]warn[/]`)、表格、树、进度、提示符、异常渲染。自动检测终端能力(TrueColor / 256 / 16 / 无色)并优雅降级。底层仍发出标准 ANSI。
- Pastel
轻量字符串扩展 API:`"error".Pastel(Color.Red).PastelBg(Color.Black)`。返回内嵌 ANSI 字节的字符串 —— 任何接受字符串的 `Console.WriteLine` 都能直接使用。无状态、无需初始化、支持 TrueColor。
- Crayon
流式颜色 DSL:`Output.Red("error")`、`Output.Bright.Blue("info")`。可组合、低分配、通过 `NO_COLOR` 做能力门控。不需要 Spectre 完整 TUI 机制时的轻量选择。
- Sharprompt
交互提示库 —— `Prompt.Select("Pick one", choices)`、`Prompt.Input<string>("Name")`、密码、多选、自动补全。底层使用与本站记录相同的 ANSI / VT 原语;可与 Spectre.Console 组合构建更丰富的界面。
常用写法
using System;
using System.Text;
class Program
{
static void Main()
{
// Box-drawing + emoji safety — defaults vary by OS / culture.
Console.OutputEncoding = Encoding.UTF8;
// \x1b is the ESC byte; C# string literals accept it verbatim.
Console.WriteLine("\x1b[1;31merror:\x1b[0m permission denied");
Console.WriteLine("\x1b[33mwarn:\x1b[0m deprecated flag");
Console.WriteLine("\x1b[32mok:\x1b[0m 142 tests passed");
}
}using Spectre.Console;
// dotnet add package Spectre.Console
AnsiConsole.MarkupLine("[bold red]error:[/] permission denied");
AnsiConsole.MarkupLine("[yellow]warn:[/] deprecated flag");
AnsiConsole.MarkupLine("[green]ok:[/] 142 tests passed");
// Truecolor — RGB triplet or hex:
AnsiConsole.MarkupLine("[#ff8000]orange truecolor[/]");
AnsiConsole.MarkupLine("[rgb(255,128,0)]same, RGB form[/]");
// Tables and progress are first-class:
var table = new Table().AddColumns("File", "Status");
table.AddRow("app.log", "[green]ok[/]");
table.AddRow("db.log", "[red]missing[/]");
AnsiConsole.Write(table);using System;
static bool AnsiCapable()
{
// The Unix-standard kill switch — honour first.
if (Environment.GetEnvironmentVariable("NO_COLOR") is not null) return false;
// Piped or redirected — don't poison logs / files with escape bytes.
if (Console.IsOutputRedirected) return false;
return true;
}
static string Style(string text, string sgr)
=> AnsiCapable() ? $"\x1b[{sgr}m{text}\x1b[0m" : text;
Console.WriteLine(Style("OK", "32"));
Console.WriteLine(Style("FAIL", "1;31"));using System;
using System.Runtime.InteropServices;
// Windows 10 1607+ / Windows Terminal / Conhost on 1709+ parse VT by
// default — this block is only needed if you target older builds. On
// non-Windows the P/Invoke just no-ops and falls through.
const int STD_OUTPUT_HANDLE = -11;
const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
static void EnableVtOnWindows()
{
if (!OperatingSystem.IsWindows()) return;
var handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (!GetConsoleMode(handle, out var mode)) return;
SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
EnableVtOnWindows();
Console.WriteLine("\x1b[32mvt ready\x1b[0m");