将文本装饰添加到控制台输出

14
我有一个使用StreamWriter将文本写入控制台的c# .net 3.5应用程序。是否有一种方法可以向打印到控制台的文本添加下划线和删除线等文本装饰?可能使用ANSI转义序列?
TextWriter writer = new StreamWriter(Console.OpenStandardOutput());
writer.WriteLine("some underlined text");

谢谢, PaulH

6个回答

24

在Windows 10版本16257及更高版本中:

using System;
using System.Runtime.InteropServices;

class Program
{
    const int STD_OUTPUT_HANDLE = -11;
    const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr GetStdHandle(int nStdHandle);

    [DllImport("kernel32.dll")]
    static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);

    [DllImport("kernel32.dll")]
    static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);

    static void Main()
    {
        var handle = GetStdHandle(STD_OUTPUT_HANDLE);
        uint mode;
        GetConsoleMode(handle, out mode);
        mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(handle, mode);

        const string UNDERLINE = "\x1B[4m";
        const string RESET = "\x1B[0m";
        Console.WriteLine("Some " + UNDERLINE + "underlined" + RESET + " text");
    }
}

带有下划线文本的控制台


抱歉,对我来说没有起作用。我错过了什么吗? - Alexandre Daubricourt
[编辑] 成功了,问题是我把常量改成了十六进制,要注意习惯!谢谢 :) - Alexandre Daubricourt
1
你知道除了下划线之外,我在哪里可以找到所有其他格式化功能吗? - Matt
不够优雅的答案。作为一个.NET问题,解决方案不应该依赖于操作系统,即使它被暗示为.NET Framework。 - Christopher J Smith
2
我认为不可能以独立于操作系统的方式解决这个问题。某些操作系统中的终端(例如早期版本的Windows)根本没有处理ANSI代码的能力。Windows 10中的控制台支持它们,但是这种功能可以以特定于操作系统的方式启用和禁用。 https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E7%A0%81#DOS.2C_OS.2F2.2C_and_Windows - Vladimir Reshetnikov

8

我使用这段代码。这是Vladimir Reshetnikov的答案的修正版本,使用了正确的重置转义码。

    private static void WriteUnderline(string s)
    {
        var handle = GetStdHandle(STD_OUTPUT_HANDLE);
        uint mode;
        GetConsoleMode(handle, out mode);
        mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(handle, mode);
        Console.WriteLine($"\x1B[4m{s}\x1B[24m");
    }

这将会产生下划线文本,并且不会重置您已经设置的任何颜色,具有这样的好处。


7
Windows控制台不支持ANSI转义序列。据我所知,改变输出字符的属性的唯一方法是在写入字符之前调用SetConsoleTextAttribute。或者,在.NET中修改Console.ForegroundColorConsole.BackgroundColor属性。
可能可以使用类型转换将这些属性设置为自定义值(即非ConsoleColor定义的值)。但我不知道这样做有什么好处。
我不知道我是否曾在Windows控制台上看到删除线文本,而且已经多年没有看到下划线了。我想这是可能的,但我不知道如何实现。

1
现在使用 Microsoft Windows [Version 10.0.19044.2728],例如请参见 https://stackoverflow.com/a/76005078/3057246。 - Vinod Srivastav

6
简短的回答是不行;控制台不允许在输出中使用下划线字符。
更长的答案是:控制台使用的屏幕缓冲区只是一个字节数组。每个光标位置都是一个字节或一个字符。要创建下划线,您需要两个字符重叠(这在控制台中不可能),或者您需要访问一个代码页,该代码页使用上128个字符值作为下划线或删除线版本的下128个字符值(我不知道有哪个)。
如果您愿意为具有下划线的行“双倍间隔”,则可以解决此问题。字符代码0x00AF(十进制175)是一个“文本艺术”字符,表示跨越字符空间顶部的边框。如果您在文本下面使用它们,那么就可以得到下划线了。

3

我发现了这个问题,并想为在使用 kernel32 函数的非 Windows 10 终端的答案中增加内容。

using System;
using System.Runtime.InteropServices;

namespace color_console
{
    class Class1
    {
        static void Main(string[] args)
        {
            Class1 c = new Class1();
            c.change();
        }

        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, CharacterAttributes wAttributes);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetStdHandle(int nStdHandle);

        [DllImport("kernel32.dll")]
        public static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput,
                ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);


        void change()
        {
            const int STD_OUTPUT_HANDLE = -11;
            IntPtr hOut;
            hOut = GetStdHandle(STD_OUTPUT_HANDLE);

            CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo = new CONSOLE_SCREEN_BUFFER_INFO();
            GetConsoleScreenBufferInfo(hOut, ref ConsoleInfo);
            CharacterAttributes originalAttributes = (CharacterAttributes)ConsoleInfo.wAttributes;

            //write some text
            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_BLUE);
            Console.WriteLine("Blue text");

            SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_RED);
            Console.WriteLine("Red background");

            SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN);
            Console.WriteLine("Green background");

            SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN | CharacterAttributes.BACKGROUND_RED);
            Console.WriteLine("Yellow background");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED | CharacterAttributes.COMMON_LVB_UNDERSCORE);
            Console.WriteLine("Red underlined text");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
                                   | CharacterAttributes.FOREGROUND_BLUE);
            Console.WriteLine("Purple text");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
                                    | CharacterAttributes.FOREGROUND_BLUE
                                    | CharacterAttributes.FOREGROUND_INTENSITY);
            Console.WriteLine("Purple text intense");
           
            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
                                    | CharacterAttributes.FOREGROUND_BLUE
                                    | CharacterAttributes.COMMON_LVB_REVERSE_VIDEO);
            Console.WriteLine("Aqua reversed text ");

            SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
                                    | CharacterAttributes.FOREGROUND_BLUE
                                    | CharacterAttributes.COMMON_LVB_REVERSE_VIDEO
                                    | CharacterAttributes.FOREGROUND_INTENSITY);
            Console.WriteLine("Aqua reversed intense text ");

            SetConsoleTextAttribute(hOut, CharacterAttributes.COMMON_LVB_GRID_LVERTICAL
                                    | CharacterAttributes.FOREGROUND_GREEN);
            Console.WriteLine("What does this do");

            SetConsoleTextAttribute(hOut, originalAttributes);
            Console.WriteLine("Back to the shire");
        }

        public enum CharacterAttributes
        {
            FOREGROUND_BLUE =   0x0001,
            FOREGROUND_GREEN =  0x0002,
            FOREGROUND_RED =    0x0004,
            FOREGROUND_INTENSITY = 0x0008,
            BACKGROUND_BLUE = 0x0010,
            BACKGROUND_GREEN = 0x0020,
            BACKGROUND_RED = 0x0040,
            BACKGROUND_INTENSITY = 0x0080,
            COMMON_LVB_LEADING_BYTE = 0x0100,
            COMMON_LVB_TRAILING_BYTE = 0x0200,
            COMMON_LVB_GRID_HORIZONTAL = 0x0400,
            COMMON_LVB_GRID_LVERTICAL = 0x0800,
            COMMON_LVB_GRID_RVERTICAL = 0x1000,
            COMMON_LVB_REVERSE_VIDEO = 0x4000,
            COMMON_LVB_UNDERSCORE = 0x8000
        }

 

        [StructLayout(LayoutKind.Sequential)]
        public struct CONSOLE_SCREEN_BUFFER_INFO
        {
            public COORD dwSize;
            public COORD dwCursorPosition;
            public int wAttributes;
            public SMALL_RECT srWindow;
            public COORD dwMaximumWindowSize;
        }

 

        // Standard structures used for interop with kernel32
        [StructLayout(LayoutKind.Sequential)]
        public struct COORD
        {
            public short x;
            public short y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SMALL_RECT

        {
            public short Left;
            public short Top;
            public short Right;
            public short Bottom;
        }
    }
}

我的电脑上的输出

在此输入图片描述


1

改变控制台的前景/背景颜色非常容易:http://www.dotnetperls.com/console-color,但据我所知,没有办法放置一些粗体文本。不过我并没有真正尝试过实现这一点,所以我不确定。


4
FYI:在Windows API调用中,“ConsoleColor”所称的“白色”实际上是“粗灰色”。基本上,8种“明亮”的颜色都是具有设置了FOREGROUND_INTENSITY或BACKGROUND_INTENSITY位的“暗”颜色。请参见http://msdn.microsoft.com/en-us/library/ms682088(v=VS.85).aspx#_win32_character_attributes。 - Jim Mischel
1
不,你可以使用\x1B[1m来加粗文本,更多信息请参见https://stackoverflow.com/a/76005078/3057246。 - Vinod Srivastav

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