如何在控制台窗口打印控制字符

3

我在ASCII(09)或Unicode中分配了制表符


char ch = '\x09';
(or)
char ch = '\u0009';

我应该如何在控制台窗口中打印出'\t'?以下任何一种方法都不起作用。(可能会打印一个制表符,但不是 '\t' 的规范表示方式)
Console.Write(ch);
Console.Write(ch.ToString())

猜测,Console.Write()不是正确的方法


您需要所有控件的表示,还是只需U+0009的表示? - Jon Hanna
所有控制字符。我可以轻松地在Python中获取它,或者甚至可以在mono解释器或VS的Roslyn插件中获取它。但就是拿不到它在控制台窗口中的输出。 - Antony Thomas
4个回答

7
控制字符不会显示,因为这是控制字符的整个意义所在。毕竟,在文本中放置制表符的原因就是要有一个制表符。
理想情况下,我们可以使用标准符号␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␤␥␦,但它们的字体支持不太好(就我目前看到的情况而言,删除表二和替代表二的符号未正确显示),在控制台上更糟糕。
另外,你在问题中没有明确表示你想要规范化表示(U+0009)还是C#转义(\t),因为你一问完一个又问另一个(“'\t'的规范化表示”)。
假设你想要一个形式,然后可以直接在C#中使用,我们可以这样做:
由于C#仅为8个控制字符提供了这样的快捷方式转义,该过程还需要我们对\进行转义,原因与C#相同——否则我们如何检测\t是指制表符,还是指后面跟着t\呢?
public static class StringEscaper
{
    public static string EscapeForCSharp(this string str)
    {
        StringBuilder sb = new StringBuilder();
        foreach(char c in str)
            switch(c)
            {
                case '\'': case '"': case '\\':
                    sb.Append(c.EscapeForCSharp());
                    break;
                default:
                    if(char.IsControl(c))
                        sb.Append(c.EscapeForCSharp());
                    else
                        sb.Append(c);
                    break;
            }
        return sb.ToString();
    }
    public static string EscapeForCSharp(this char chr)
    {
        switch(chr)
        {//first catch the special cases with C# shortcut escapes.
            case '\'':
                return @"\'";
            case '"':
                return "\\\"";
            case '\\':
                return @"\\";
            case '\0':
                return @"\0";
            case '\a':
                return @"\a";
            case '\b':
                return @"\b";
            case '\f':
                return @"\f";
            case '\n':
                return @"\n";
            case '\r':
                return @"\r";
            case '\t':
                return @"\t";
            case '\v':
                return @"\v";
            default:
                //we need to escape surrogates with they're single chars,
                //but in strings we can just use the character they produce.
                if(char.IsControl(chr) || char.IsHighSurrogate(chr) || char.IsLowSurrogate(chr))
                    return @"\u" + ((int)chr).ToString("X4");
                else
                    return new string(chr, 1);
        }
    }
}

现在我们可以用字符串和单个字符来测试它。
单个字符:
Console.WriteLine('\t'.EscapeForCSharp());

输出:

\t

字符串:

string str = "The following string contains all the \"C0\" and \"C1\" controls, escaped with \\ as per C# syntax: "
  + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u007F\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F";
Console.WriteLine(str.EscapeForCSharp());

输出:

The following string contains all the \"C0\" and \"C1\" controls, escaped with \\ as per C# syntax: \0\u0001\u0002\u0003\u0004\u0005\u0006\a\b\t\n\v\f\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u007F\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F

我原以为规范化的规范形式是'\t',参考[1],但如果我用词不当,请原谅。无论如何,我理解您所说的非可打印字符无法打印的观点。我发起这篇帖子的原因是进行了这个简单的实验(由超出范围的原因驱动); 在Python解释器中,我键入a='\x09'str(a)或只是a,它会显示'\t'。然后在mono解释器中,我键入char ch = '\x09'; ch;,它也显示'\t'。但我就是无法在C#控制台中得到它。无论如何,感谢您详细的回答。 - Antony Thomas
[1] 关于canonical的链接https://dev59.com/2UjSa4cB1Zd3GeqPGYwr - Antony Thomas
1
在mono控制台中,您也可以获得真正的制表符。解释器是为了帮助您调试和尝试各种操作,因此\t很有用,但控制台是用于实际工作的地方,在大多数情况下\t是无用的(那么您如何实际输出制表符呢?)。“规范”意味着“将同一数据的等效有效表示的选择限制为单个无选择格式的方法”。我上面提供的U+0009是Unicode语言无关表示字符的规范形式,独立于其字形或缺失(因此它对于制表符、A、B或C同样有效)... - Jon Hanna
没有标准的 C# 转义字符,因为 C# 的设计初衷是供人类编写而非作为源文件进行生产和交换,所以没有必要说 \t\u0009 或甚至比源代码中的 更加标准。 - Jon Hanna
谢谢你的观点。我很愚蠢地争取了控制台0.000001%的使用率。 - Antony Thomas
并不是愚蠢,只是专注于自己的问题,这在大多数情况下是一件好事(你会喜欢一个不关注眼前问题的程序员吗?),但这可能会让行为看起来很奇怪,实际上却是合理的。 - Jon Hanna

2

没有内置的方法可以将字符打印为C#转义序列。

只需使用字典将字符映射到所需的字符串即可:

var map = new Dictionary<char, string>{ {'\t', @"\t"}}

并在输出过程中使用它来替换字符(如果存在于映射中)。


这太有趣了。于是我启动了mono解释器,输入char ch = '\x09'; ch;,然后,瞬间,我得到了输出为'\t'的结果。不确定为什么在控制台窗口中会这么复杂。 - Antony Thomas
这更加复杂,因为控制台窗口是设计用于实际输出而不仅仅是调试。99.99999%的情况下,有人将选项卡输出到控制台窗口,是因为他们想要一个制表符,而不是制表符的转义字符。 - Jon Hanna
2
@AntonyThomas,VS 的行为类似(你可以通过大量的“如何从字符串中删除转义序列”的问题轻松发现)——即时窗口和调试工具提示/变量视图显示编码字符串,但实际的控制台输出不会显示(因为您真的不想在命令提示符或文件中看到所有字符都被编码的文本)。 - Alexei Levenkov
是的,在这里往往有一种讽刺的现象,我们经常得到完全相反的问题。 - Jon Hanna

0

\t有特殊含义(制表符),因此如果要打印它,则需要转义“\”。

Console.Writeline("\tHello World");
prints:     Hello World

Console.Writeline("\\tHello World");
prints: \tHello World

你也可以使用@-语法来去除\t、\n、\'等特殊含义...


0

试试这个:

    string tab = "\u0009";
    Console.Write(tab.Replace(tab, "\\t"));

我不可能为其他控制字符概括它。我应该把这一点说明清楚。 - Antony Thomas

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