在Java控制台中打印一行(反斜杠n的反向)。

21

前言

我看到了一些关于其他语言的相关问题,并进行了相当多的研究。首先,在我们开始深入探讨之前,我必须说明为什么要这样做。我正在漂亮地打印二叉树,我找到了一种方法来做到这一点,我认为没有人发现过(在我搜索了很久的网络上找不到)。它解决了与间距和冲突相关的许多问题,我可能会在以后提供它作为漂亮打印Btree问题的答案。无论如何,做以上操作的一个重要部分是能够将换行符(光标)向上移动一行。我也会接受将回车符向上移动一行。

我对这个问题进行了三次编辑,名为“Edit 1-3”,位于底部附近。

我的具体Windows版本(可能相关):Windows 10 v1607 OS build: 14393.2035

问题??

问题是这可以很容易地使用ANSI转义字符完成

ANSI转义码:https://en.wikipedia.org/wiki/ANSI_escape_code

使用"\033[A"作为我想要实现的特定代码(从有关在Python中向上移动一行的问题中获得)。

问题

所以问题解决了,对吧?哦不,甚至没有接近。问题是,显然Windows有这个"""精彩"""更新,可能已经破坏了所有ANSI/控制台相关的内容。下面链接到一个关于此的问题,但具有不同的ANSI要求,但根本问题是相同的。

如何使用System.out.println在控制台中打印颜色?

tl:dr 对于某些原因,它在Windows上不起作用,您需要一个大型库来完成此操作或其他内容。

然后我尝试对更新本身进行深入研究,以及为什么这恰好是Windows特定的(当然),我被引导到了这里。

Windows问题的根本原因:https://github.com/Microsoft/WSL/issues/1173

简而言之,这个更新改变了cmd.exe的默认设置,而cmd.exe是Java使用的默认控制台。改变的是如何处理ANSI代码的控制台设置,对于“子应用程序”(也就是使用控制台但不是控制台本身的东西)。更新之前的默认设置是子应用程序继承了控制台的默认设置,即启用了ANSI。现在(因为先前的默认设置出现了问题),他们将其更改为子应用程序不再继承控制台的默认设置。这意味着如果要启用ANSI,Java必须自己设置此控制台模式。显然,有很多可以使用java命令来做到这一点的库,但是其中一些存在问题。首先,它们需要使用maven,而我不想使用maven,另外,我不喜欢为了做一些非常简单的事情而需要一大堆额外的库,这还会增加我的代码依赖的数量(这是不好的)。所以我试图避免像“只是使用JANSI来设置ANSI的`setConsoleMode`”这样的简单解决方案。

在此之后我尝试了什么

在我阅读/理解完之后,我尝试进入注册表编辑器并尝试更改控制台(cmd.exe)的默认设置,以始终启用ANSI,即使是子应用程序(也就是Java)。在GitHub讨论中建议这样做,但令我惊讶的是,特定的设置对我来说甚至都不在那里。(它应该在计算机 -> HKEY_CUURENT_USER -> Console之间的“TrimLeadingZeros”和“WindowAlpha”之间)。

尝试2

由于我无法编辑控制台设置而不需要大量库,其中包含我不想要的东西,因此我尝试从另一个角度思考,并在Java设置中进行了一些操作。我发现了Java确切地设置其控制台的位置,并发现我可以更改该设置以在Java中使用不同的控制台。我最近安装了Git并知道Git Bash可用,因此我尝试使用下面的问题(以及Google)作为起点。

https://superuser.com/questions/1196463/start-sh-exe-bash-with-given-path

第三次是幸运的吗?

我无法让该死的东西正常工作。 它没有返回任何错误或者做出任何新的事情,完全没有反应。它甚至设置为“当前项目”,所以当我运行我的项目时应该做出一些不同的事情,对吗?我相信问题的一部分是我的根git文件夹有问题(不在程序文件中)。这可能是因为我正在使用的计算机是工作计算机,也许那里的某些设置影响了git安装的位置,我不知道,但我知道的是当我更改控制台时,我尝试了这个路径:

C:\Users\abbotts1\AppData\Local\Programs\Git\bin\bash.exe

我也尝试过:

C:\Users\abbotts1\AppData\Local\Programs\Git\bash.exe

并且

C:\Users\abbotts1\AppData\Local\Programs\Git\bin\sh.exe

"C:\Users\abbotts1\AppData\Local\Programs\Git\bin\sh.exe" --login -i

每次更改控制台后,我都尝试了这段代码:

public class ExpressionEvaluator {

    public static void main(String[] args) {
        System.out.println("1");
        System.out.println("\033[A");
        System.out.println("2");
    }
}

我一直得到的输出结果是:

1

这里有一个额外的行

2

如果我删除了“向上移动光标”的ANSI println语句,我得到的输出结果是:

1

2

我想要的是:

2

1

(整个问题的关键在于,我能够自由地向上移动一行)

我还尝试过将它们变成打印语句,但也没有起作用。

所以我现在陷入了困境

这就是我想要的答案,类似于:

  1. 一种非侵入式的方法,用于更改cmd.exe的设置,使得像Java这样的子应用程序在运行时默认使用ANSI。
  2. 或者一个非侵入式库,不需要Maven和其他许多东西,以启用ANSI,从而可以在控制台中运行Java中的ANSI并获得所需的输出。
  3. 或者一个程序性解决方法,可以有效地将一行打印出来而不需要整个ANSI。
  4. 或者帮助配置IntelliJ控制台,以便我实际上可以使用另一个控制台并使用ANSI打印一行向上。

针对明显关注点的解答

由于1和4是超级用户问题,2是“我需要一个库”,不在主题之内,我更多的是讨论第3点。如果没有解决办法,答案属于不在主题之内的问题之一,请告诉我没有解决办法。我不知道如何提出这个问题,因为它是一个“编程特定问题”,它有许多解决方案,其中一些不是“相关主题”,因为它们不是编程解决方案。如果唯一的答案是“解决方案不在此主题中”,那么我会去适当的地方问。当我去别的地方问这个问题时,告诉我是否应该删除此问题。

我认为可能适用于此主题的解决办法

既然第3点符合主题,那就讨论一下我的想法:

也许我可以制作一些系统来仅打印到某些数组而不是上下移动行(例如:有一个数组代表行,遍历树而不是上下移动行,只需切换要打印的数组)。我认为这不是非常有效率的,它浪费了数组和处理能力,但如果它是一个解决方案,我愿意听取意见。这是我能想到的全部,但我尝试过的大部分是尝试让ANSI工作。

供日后参考:

当有多个与主题不相关的解决方案/问题解决方案时,正确的提问方式是什么?最好不要询问它们吗?涉及问题的解决方案是否构成了变色龙问题?我不想给自己带来元效应。

编辑 1:目前进展如下:

我再次尝试了整个git bash过程,这真是一个繁琐的过程。终端中实际的git bash路径需要用引号括起来,并在之后加上--login -I参数。例如:

C:\Git\bin\sh.exe --login -i

该设置针对java终端。然后,你必须将Windows环境变量命名为PATH并设置为你的java JDK。进入你的路径环境变量(有一百万个youtube视频可供参考),并设置一个新变量名称为PATH到你的JDK。例如:

C:\Program Files\Java\jdk-10.0.1\bin

这个问题:

如何使gitbash找到javac命令?

详细介绍了该过程。

Tl;dr 你需要设置一个Windows路径变量,这样git bash才能识别javac和java命令。

完成后,您需要像在任何终端中一样运行java和javac命令。顺便说一句,要小心,因为git bash中的路径需要使用两个'\'符号而不是正常的'\'符号,因此您的源目录路径可能看起来像这样:

C:\IdeaProjects\Calculator\src\

然后你只需要运行:

javac ClassName.java

java ClassName

但是它实际上并没有打印ANSI输出,而是打印了原始的转义字符。此外,我发现我使用了错误的转义序列(我使用了错误的数字来表示'esc'按钮,因为'esc'键被表示为某个数字,但我使用的十六进制数字是错误的,例如x330之类的)。我还学到了这种表示法:

'esc键十六进制数' + '[' + '由逗号分隔的参数十六进制值'

所以这可能看起来像:

\x1B [ A

实际字母和数字是十六进制值(没有明显的0x...),第一个转义十六进制值中有一个x(为什么?)。无论如何,在Java中运行它们作为字符串时,您需要使用额外的'\'转义转义字符(显然吧?),因此,例如,代码可能看起来像:

System.out.println("\\\x1B [A");

我刚刚注意到Stack Overflow也会转义这些字符,所以实际上我有三个'\',但是对于你们来说,它只显示了两个'\',很奇怪吧?无论如何,回到我要说的话题。

但是,输出仍然无法正常工作!这就是我的问题所在。我已经完成了上述步骤,并且我99.9%确定Git Bash已正确安装并运行良好,但是当我运行以下命令时:

public class ExpressionEvaluator {
    public static void main(String[] args) {
        System.out.println("1");
        System.out.println("\\x1B [A");
        System.out.println("2");
    }
}

我在Java控制台中得到了以下输出(不是Git Bash):

1

\x1B [A

2

而在Git Bash中,我得到了以下输出:

1

\x1B [A

2

我实际想要的输出是:

2

1

因为ANSI转义字符应该将我的光标/换行符/其他内容向上移动一行。如果我运行上面的代码,但使用ANSI代码“\x1B [F”,则只会输出原始的ANSI代码。我非常确定Git Bash应该是“本地支持ANSI”的,我在网站上也看到过人们这样说,所以我不知道为什么它不起作用。

我仍然不确定这是否是两个单独的控制台输出还是同一个控制台输出。我真的无法确定,所以如果有人想留下评论说“是的,这是同一个傻瓜”,我会很感激,因为我找不到明确的来源证明它是真的。我认为是这样,但除了IntelliJ中的控制台设置之外,没有其他东西表明这是真的。

我听说在Windows上需要设置或以其他方式操作的TERM变量的传言。 我使用以下命令自己检查了它:

echo $TERM

在git bash中,我得到了:

cygwin

所以我不知道这是好还是坏,因为我已经浏览了所有你能想到的搜索词,它们都会导致相同的基本结果页面,“git bash颜色无法工作”,大多数涉及Windows 7(我没有)安装maven / jansi(不想要或不应该需要它)或某种不是Java的其他语言,并使用一些不是IntelliJ的其他IDE。 一些具有我的特定要求的页面说,TERM应该是xterm或类似xterm-256的其他内容,用于“颜色”输出之类的东西。 我对这些东西非常陌生,所以我甚至不知道从哪里开始。

太长了,请给我们一个tl;dr

我需要知道为什么git bash打印原始ANSI而不是实际使用ANSI。

我知道的

我正在使用带有IntelliJ的git bash,99.9%确信我已经正确设置了路径,我能够从git bash运行我的Java类,我将其设置为IntelliJ终端,并且当前将Windows TERM变量设置为cygwin。

我不知道的事情

我不知道TERM需要什么,也无法在Web上找到它,我无法确定当我单击绿色箭头“运行”时出现的IntelliJ控制台是否与git bash控制台相同,并且我无法弄清楚是否有其他事情阻止我实际解释ANSI。

我需要什么

我需要一个简单的解释,类似于r/ELI5的东西,说明git bash有什么问题(如果有的话)以及如何修复它。如果无法简单地解释或没有问题,那么也许我会尝试另一个被称为“本地ANSI感知”的终端。我认为Powershell是另一个选项。我最好的猜测是TERM变量需要改成其他值,或者git bash从一开始就不是真正的本地ANSI感知和能力。我看到其他人也有颜色方面的同样问题,但他们的解决方法适用于旧版本/不同语言和其他事情,或者根本不起作用。我还没有找到一个好的页面,“git bash在IntelliJ中输出原始ANSI”,我已经用这些确切的词汇变体搜索了几个小时了。我能得到的只是关于与此相关的“错误”的长时间GitHub讨论,它们让我感到困惑,没有解决方案,并且可能是活跃的或根本不包含任何解决方案。

编辑2

经过更多的研究,我发现我的先前的转义代码是正确的:

\033[A

\x1B[A

应该是类似的。

我还学到了,问题并不是我使用的控制台,而是Windows本身。我现在知道这一点是因为我已经尝试在cmd.exe、git bash和powershell上进行编译和运行。要更改之前提到的默认设置(控制台不支持子应用程序的ansi),您必须通过程序本身明确启用它,而不是依赖于控制台或其他东西。

这里有一个关于Python的问题解释了这个问题:

如何在Windows 10控制台中使用对ANSI转义序列的新支持?

总之:

他们使用的方法被称为getConsoleModesetConsoleMode以及VIRTUALTERMINALPROCESSING标志。显然,您需要使用这些来实际设置控制台模式以支持ANSI。我目前不知道这些是否是Java中隐藏的东西或者(很可能)需要添加到基本Java库中的内容。我将尝试弄清楚他们如何从那个问题中获取ctypes(似乎是他们导入以使用这些方法的东西)并获取我需要的方法。一旦我做到了,除非你们中的某个人在我之前找到了答案并能更好地解释它,否则我会将其发布为答案。

显然,如果您仅从控制台使用转义序列,则可以正常工作,但是如果您使用“子应用程序”,则无法正常工作。因此,至少我们现在确切地知道了问题的根本原因。

编辑3

发现了这个,特别是控制台虚拟终端序列部分(左侧导航栏):

https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences

在底部附近有一个完整的C实现,可以启用控制台读取ANSI。显然,这根本不需要库,但要实际更改控制台默认使用此类代码的过程需要系统管理员权限、对程序文件的熟悉知识以及许多其他事情(至少在Windows 10更新之前,颜色支持已更改)。现在它仍然默认禁用,但可以启用。我还不知道如何直接从控制台尝试ANSI。我尝试了多个

echo \x1B[(插入ANSI代码)

但是任何终端(cmd、git bash、powershell)中似乎都无法运行这些命令。它们只返回原始代码

\x1B[(无论ANSI代码是什么)

我显然是新手,可能使用了错误的命令,如果是这样,请随意指导我,但我看到的示例都使用echo。无论如何,我认为直接从终端调用ANSI应该可以工作,因为它默认启用,只是不适用于子应用程序(Windows 10更新后),但也许不是这样,也许默认情况下被禁用,即使启用了,对于子应用程序(Java)仍然被禁用,除非在该子应用程序(Java)中明确更改。我将尝试查看是否可以直接从控制台启用ANSI,或者链接的C代码是否需要直接移植到Java中或在控制台中运行才能正常工作。问题是我不知道如何获取C代码使用的导入/包含项并将其用于Java代码。我宁愿不仅接受以C编写的“解决方案”,并尝试将其与Java代码一起使用,而是希望翻译并更好地理解它,并拥有自己的执行相同操作的代码。

我被告知的另一个选项是称为ANSICON的东西,它类似于某种插件,您可以使用-i标志在控制台中安装它,这应该至少在控制台中启用ANSI。我找到了这个。

https://community.liferay.com/blogs/-/blogs/enable-ansi-colors-in-windows-command-prompt

以上更详细地解释了这个过程。

我使用的具体 Windows 版本和更新版本

我还学到的一件事是,改变 ANSI 控制台行为的“更新”的具体 Windows 10 版本是类似于 Windows 10 v1151 的东西。我会尝试找到网页直接引用此信息,但目前我使用的是 Windows 10 v1607,所以应该没问题。其中包括实际的操作系统版本,我使用的是14393.2035,我认为这在我之前的某个链接中被识别为特别中间的更新(我相信它是具有关于更新的整个 GitHub 讨论的链接,你可以在此找到:https://github.com/Microsoft/WSL/issues/1173)。由于我使用的是工作计算机,因此我无法进行系统更新,因为我不是管理员,而且我怀疑 IT 不会让实习生四处更新工作电脑。

无论如何,我将继续尝试将 C 代码转换为 Java 代码,测试它,然后尝试发布答案。如果你们已经比我先进,请让我知道。


2
为什么不在输出到控制台之前构建最终输出,然后一次性打印出来呢? - daniu
4
为什么不使用一个RandomAccessList,其中包含字符串或StringBuilder,索引为行号,并且您可以根据需要添加到行或插入新行?基本上无法保证控制台将遵守您使用的任何特殊命令代码,即使它们现在这样做,也不能保证它们在未来的更新中会这样做。我认为@daniu提出的在内存中进行格式化,然后输出是可行的方式,并且将节省很多麻烦。 - xtratic
我可以创建一个字符串哈希表,遍历树时将行号附加到其中,但问题在于,我真的想要创建一个全新的内存结构并处理所有这些字符串操作和表查找,只是为了打印一棵树吗?将字符打印到控制台要容易得多,想象一下一个非常大的二叉树。我知道这似乎很荒谬,但这个 ANSI 标准本来就是为了成为标准,微软不理解这一点并不意味着我们应该放弃 ANSI 字符。在我放弃之前,我想知道是否还有其他选择。 - Redacted
你显然是在和一个人交流,而不是一台计算机,并且你试图在Windows上这样做。这引出了一个问题,为什么你还停留在90年代? - user2023577
1
你说好的可用库“需要Maven”,但是Maven从来不是必须的,只是推荐使用:你总是可以手动拉取所有所需的依赖关系。 - Pino
显示剩余5条评论
1个回答

1

在“Edit 2”中提到的python方式(https://dev59.com/LFoV5IYBdhLWcg3wCrPH#36760881)仅调用Windows特定的本地代码(kernel32,这是不可移植的),以“修复”此问题。

我想你同意它,并希望在Java中做同样的事情(即在检测到Windows时调用kernel32)...

一个非常轻量级的库,可以在Java中实现相同的功能,那就是JNA,它具有针对kernel32的开箱即用的包装器(请参见:https://java-native-access.github.io/jna/4.2.1/com/sun/jna/platform/win32/Kernel32.html

您似乎需要使用这种方法:https://java-native-access.github.io/jna/4.2.1/com/sun/jna/platform/win32/Wincon.html#SetConsoleMode-com.sun.jna.platform.win32.WinNT.HANDLE-int-
希望这对您有用。
编辑:从技术上讲,您只需要jna.jar(请参见此处的入门指南:https://github.com/java-native-access/jna/blob/master/www/GettingStarted.md),但我建议您还使用jna-platform.jar,以便不需要自己编写在运行时生成kernel32映射的代码。
JNA主页:https://github.com/java-native-access/jna
我认为添加1个(如果你添加jna-platform,则为2个)具有非常特定范围的jar包(在没有所有JNI准备开销的情况下进行本地调用)足够轻量级。您不需要生成任何标题,也不需要更改编译过程中的任何内容。通过将这些jar包添加到类路径中,它将正常工作。
您还应该在问题中澄清这是关于Windows的。可以编辑标题为:“在Windows中在Java控制台中打印一行('\n'的反转)”,因为这实际上是一个您想要用Java解决的平台特定问题。

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