【问题标题】:Parsing ANSI escape codes?解析 ANSI 转义码?
【发布时间】:2011-05-05 01:58:14
【问题描述】:

我正在用 C# 构建一个 telnet 应用程序(用于在老式 BBS 系统上编写门游戏脚本,例如 Wildcat),但似乎无法为 ANSI 转义码(例如光标移动、着色等)构建一个有效的解析器 - 几乎所有我测试过的系统会发送违反任何“标准”的未定义序列。关于这件事的资源似乎也很少,Wikipedia has the most in-depth list I've found so far 但即使他们说它不完整 - 我遇到的大多数其他网站都只是复制/粘贴了维基百科的文章。

我的问题:那里有图书馆吗?如果没有,一些解析代码/正则表达式怎么样?至少为ESC[!_ 之类的东西提供一些适当的文档会非常有帮助。

我真的觉得我在重新发明轮子,尤其是看到 Telnet 或多或少相当于互联网的轮子(至少在年龄方面;)

编辑:添加了一个奇怪的例子:

00000075h: 1B 5B 73 1B 5B 32 35 35 42 1B 5B 32 35 35 43 08 ; .[s.[255B.[255C.
00000085h: 5F 1B 5B 36 6E 1B 5B 75 1B 5B 21 5F 02 02 3F 48 ; _.[6n.[u.[!_..?H
00000095h: 54 4D 4C 3F 1B 5B 30 6D 5F 1B 5B 32 4A 1B 5B 48 ; TML?.[0m_.[2J.[H
000000a5h: 0C 0D 0A                                        ; ...
The mysterious part is '21' in line 2 ---^^

【问题讨论】:

  • 正确的文档是ECMA-48。不过,它所涵盖的内容不仅仅是移动光标并让内容变得丰富多彩。
  • 您不太可能找到与维基百科不同的单一规范资源,因为每个终端处理 ANSI 代码的方式都不同。官方参考是ANSI standard(维基百科链接到),但这只会告诉你每个人都同意做什么,而不是实际做了什么。

标签: c# parsing telnet ansi-escape


【解决方案1】:

正确的答案取决于人们打算如何使用该库。任何终端仿真器都会读取这些序列并根据它们执行动作。但即使是一个简单的终端模拟器也能理解大约一百个序列。

您的示例可能更易读,如下所示:

\E[s \E[255B \E[255C\t_ \E[6n \欧盟 \E[!_^B^B?HTML? \E[0m_ \E[2J \E[H\f\r \n

使用unmap(使转义字符\E 并显示所有个字符可打印——并为转义字符开始新的一行)。

ECMA-48 描述了格式

  • 单字节控制字符,并且
  • 多字节控制序列(以转义字符开头)。

控制序列的内容(参数)仅限于某些字符,例如数字和分隔符,例如 ';'。控制序列也有一个明确的结尾,称为 final 字符。序列\E[!_^B^B? 不遵循这些规则。正如评论中所建议的那样,终端对光标位置请求\E[6n的响应可能使您的录音感到困惑。

有了这么多的上下文:

  • 终端仿真器执行的某些操作会修改显示(\E[2J 清除显示)
  • 终端仿真器执行的一些操作告诉主机关于显示(\E[6n 询问终端光标在哪里
  • 终端模拟器执行的一些操作会修改终端的行为(\E[s\E[u 保存光标位置并稍后恢复)

简而言之,您可能会看到,要处理终端接收到的控制序列,您确实需要一个终端程序来完成所有这些工作。然而,并非所有终端仿真器都是相同的。有些使用一系列 case 语句来处理转义、括号、数字等的连续阶段。但是您的程序应该记住,单字节控件可以出现在多字节控制序列的中间。由于它们的编码不同,因此没有冲突。但这会使程序比您想象的要复杂得多,因为一次只读取一个序列。

xterm 使用了一些 case-statements(对于 final 字符,基本上),但是解码控制序列时的大多数状态转换是使用一组表完成的。它们非常重复,但构造起来并不明显:Paul Williams 指出,对于 VT100,它们应该是对称的(本质上将输入视为 7 位 ASCII)。一些状态被视为错误,并忽略;无论如何,格式良好的序列都是最重要的。理论上,您可以重用状态表并添加“小”解析。这些表有 8500 行(每行一个状态)。

除了 (a) 读取现有的终端仿真器并在较小的范围内模仿它们,或者 (b) 修改终端仿真器......您还可以调查 libvterm

实现 VT220 或类似 xterm 的终端仿真器的抽象 C99 库。它不使用任何特定的图形工具包或输出系统,而是调用其嵌入程序应提供的回调函数指针以代表它进行绘制。它避免了在正常运行状态下调用 malloc(),允许它在嵌入式内核情况下使用。

然而,这不在C# 中(源文档)。不过,它只有 5500 行代码。

进一步阅读:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 2014-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多