使用.NET进行ANSI颜色控制台输出

13

我尝试使用以下最小化的 C# 程序生成带有 ANSI 转义码 的彩色控制台输出:

using System;

// test.cs
class foo {
    static void Main(string[] args) {
        Console.WriteLine("\x1b[36mTEST\x1b[0m");
    }
}

我在 Windows 7 x64 上运行 Ansicon v1.66,并使用 csc.exe(Microsoft (R) Visual C# Compiler version 4.6.0081.0)。

在这个配置下,彩色输出正常工作;Ansicon本身也完美运行。

为了交叉检查,我使用一个与 C# 程序完全等效的 node.js 单行程序:

// test.js
console.log("\x1b[36mTEST\x1b[0m");

而更基本的是,一个手工制作的文本文件:

text file hex editor screenshot

两者都能正确执行预期操作:打印一个青色的字符串"TEST":

enter image description here

只有我用csc构建的test.exe打印出了其他东西。为什么?

@RonBeyer 当然,这是一个有效的转义代码。它是ASCII ESC字符。 - Tomalak
@Tomalak 你希望 ESC 字符如何打印? - Fᴀʀʜᴀɴ Aɴᴀᴍ
1
我所说的无效是指不可打印的字符,任何十六进制数字都是“有效”的。 - Ron Beyer
我理解颜色代码,也明白你在这里的意图,但是C#编译器在编译过程中会将\x1b替换为ESC代码,因此它基本上会在输出到控制台之前将其转换为Unicode不可打印字符。我不确定这个版本是否与JS版本以相同的方式传递到控制台。 - Ron Beyer
显示剩余2条评论
3个回答

21
我创建了一个小插件(可在NuGet上获得),它允许您轻松地将字符串包装在ANSI颜色代码中。支持前景色和背景色。

enter image description here

它通过扩展String对象工作,语法非常简单:

String对象被扩展,语法非常简单:

"colorize me".Pastel("#1E90FF");

之后,该字符串已准备好打印到控制台。

Console.WriteLine("colorize me".Pastel("#1E90FF"));

如果有人想在C# Windows窗体应用程序中嵌入控制台应用程序,请查看https://github.com/dwmkerr/consolecontro。 - Jonas
1
哇,它就能工作。 - Vinod Srivastav

10

如果你使用AnsiCon x64环境,你的程序需要编译为/platform:x64,如果你使用AnsiCon x86/32位版本,则需要编译为/platform:x86。确切的原因是个谜...

最初我认为你需要这些:

你需要获取StandardOutput,并让Console.WriteLine相信你是在写入文件而不是控制台,并使用ASCII编码。

以下是它的工作方式:

 var stdout = Console.OpenStandardOutput();
 var con = new StreamWriter(stdout, Encoding.ASCII);
 con.AutoFlush = true;
 Console.SetOut(con);

 Console.WriteLine("\x1b[36mTEST\x1b[0m");

.Net的Console.WriteLine使用内部__ConsoleStream,检查Console.Out是否为文件句柄或控制台句柄。默认情况下,它使用控制台句柄,并通过调用WriteConsoleW向控制台写入。在备注中,您会发现:
尽管应用程序可以使用ANSI模式的WriteConsole编写ANSI字符,但控制台不支持ANSI转义序列。但是,一些函数提供了等效的功能。有关更多信息,请参见SetCursorPos、SetConsoleTextAttribute和GetConsoleCursorInfo。
要直接将字节写入控制台而不受WriteConsoleW干扰,只需使用简单的文件句柄/流即可,这可以通过调用OpenStandardOutput来实现。通过将该流包装在StreamWriter中,以便我们可以使用Console.SetOut再次设置它,我们就完成了。字节序列被发送到OutputStream,并被AnsiCon拾取。
请注意,这仅适用于适用的终端仿真器,例如AnsiCon,如下所示:

enter image description here


很遗憾,它似乎不起作用。在更改后,您的程序输出对我来说仍然是相同的[36m...TEST...[0m(加上\x1b的可见替换字符)。 - Tomalak
@Tomalak 嗯,也许这与默认编码有关。在昨晚的实验中,我首先尝试获取测试字符串的ASCII编码字节数组,然后执行Console.OpenStandardOutput().WriteByte(array,0, array.Length); 当它起作用时,我将其重新设计为我的工作示例。也许streamwriter需要一个编码? - rene
我现在正在工作,没有准备好的开发环境,我今晚会重新访问这个问题。 - rene
@Tomalak 我找到问题了。我必须再次向streamwriter添加Encoding.ASCII。我可能错过了我删除编码但没有重新编译的步骤,对此很抱歉。 - rene
不行,还是不行。看起来 AnsiCon 将被完全规避。当我在一个未武装的 cmd.exe 上运行时,输出完全相同。你能发布一下对你有效的确切源代码吗? - Tomalak
显示剩余4条评论

0
今天我遇到了这个问题,但是我无法让被接受的答案起作用。经过自己的一些研究,我找到了一个可以解决问题的答案。
很遗憾,我们需要在这个问题上降到非常低的层面,并直接调用Windows API。为此,我使用了PInvoke.Kernel32 NuGet以方便使用,但如果它对您来说太重了,您可以自己创建P\Invoke映射。
以下方法说明了如何激活ANSI代码:
bool TryEnableAnsiCodesForHandle(Kernel32.StdHandle stdHandle)
{
    var consoleHandle = Kernel32.GetStdHandle(stdHandle);
    if (Kernel32.GetConsoleMode(consoleHandle, out var consoleBufferModes) &&
        consoleBufferModes.HasFlag(Kernel32.ConsoleBufferModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING))
        return true;
    
    consoleBufferModes |= Kernel32.ConsoleBufferModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    return Kernel32.SetConsoleMode(consoleHandle, consoleBufferModes);
}

要启用它以用于StdOut,您可以这样调用:

TryEnableAnsiCodesForHandle(Kernel32.StdHandle.STD_OUTPUT_HANDLE);

如果该方法返回 true,则启用 ANSI 代码,否则不启用。
该解决方案使用非常低级别的 Windows API GetConsoleMode 和 SetConsoleMode 来检查控制缓冲区模式 ENABLE_VIRTUAL_TERMINAL_PROCESSING 是否设置,如果未设置,则尝试设置该模式。

听起来你没有使用Ansicon,而是使用本地的Windows控制台着色? - Tomalak
实际上,我对解决方案的内部一无所知。但在进一步的程序中,我使用了Console.WriteLineConsole.Error.WriteLine,它们能正常工作。 - sxleixer
我检查了PowerShell、CMD和Rider终端。它们都可以使用这个解决方案。 - sxleixer
他们可能不会在Windows 7的cmd.exe(这个问题特别涉及)中支持,因为后来Windows添加了对颜色代码的本地支持。所以这可能解决了你的问题,但它并不是问题的答案,因为你的设置是不同的。 - Tomalak
请看这个答案,https://stackoverflow.com/a/76005078/3057246,它是一个颜色混合器,不仅限于标准集。 - Vinod Srivastav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接