Flutter ANSI转义码处理插件ansi_escape_codes的使用
Flutter ANSI转义码处理插件ansi_escape_codes的使用
在Flutter开发中,处理终端文本格式化时常常需要用到ANSI转义码。ansi_escape_codes
插件可以帮助开发者轻松处理这些转义码。本文将详细介绍如何使用 ansi_escape_codes
插件来处理ANSI转义码。
1. 控制功能常量和预定义值
1.1 控制代码(C0集)
控制代码(C0集)由0x00到0x1F范围内的代码表示。以下是部分控制代码:
常量 | 代码 | 描述 |
---|---|---|
NUL | \x00 |
空字符 |
BEL | \x07 |
铃声(终端可以阻止铃声) |
BS | \b 或 \x08 |
退格 |
HT | \t 或 \x09 |
水平制表符 |
LF | \n 或 \x0A |
行结束符 |
FF | \f 或 \x0C |
分页符 |
CR | \r 或 \x0D |
回车 |
ESC | \x1B |
转义(用于扩展代码) |
import 'package:ansi_escape_codes/controls.dart';
...
print('\t\r\n' == '$HT$CR$LF'); // true
1.2 控制功能 ESC Fe(C1集)
这些控制功能由形式为 ESC Fe 的两个字符转义序列表示,其中 ESC 由代码 0x1B 表示,Fe 由代码 0x40 到 0x5F 表示。以下是一些C1集中的控制功能:
常量 | 代码 | 描述 |
---|---|---|
CSI | ESC [ |
控制序列引入器 |
ST | ESC \ |
字符串终止符 |
OSC | ESC ] |
操作系统命令 |
HTS | ESC H |
字符制表符设置 |
import 'package:ansi_escape_codes/controls.dart';
...
// 清除屏幕
print('Erase screen${CSI}2JScreen erased');
// 设置新的制表符位置
print('$HTS $HTS $HTS $HTS');
print('1\t2\t3\t4'); // 1 2 3 4
print('${CSI}3g'); // 重置制表符位置为默认
1.3 控制序列(CSI)
控制序列是一个以控制功能 CSI 开头的字符串,后面跟着表示参数的一个或多个字节(如果有),再后面跟着一个或多个字节表示控制功能。CSI 本身是C1集中的元素。以下是一些CSI集中的控制功能:
常量 | 代码 | 描述 |
---|---|---|
CUU | CSI n A |
光标向上移动 n 行 |
CUD | CSI n B |
光标向下移动 n 行 |
CUF | CSI n C |
光标向右移动 n 个字符 |
CUB | CSI n D |
光标向左移动 n 个字符 |
CUP | CSI n;m H |
光标定位到第 n 行,第 m 列 |
ED | CSI s J |
清除页面(显示)(s=2 - 整个屏幕) |
DCH | CSI n P |
删除 n 个字符 |
ECH | CSI n X |
清除 n 个字符 |
TBC | CSI n g |
制表符清除(s=3 - 清除所有字符制表符) |
SM | CSI s h |
设置模式(s=4 - 插入替换模式) |
RM | CSI s l |
重置模式 |
SGR | CSI s… m |
选择图形表现 |
import 'package:ansi_escape_codes/controls.dart';
...
// 光标向左移动4个字符
// 删除1个字符('1')
// 光标向右移动1个字符
// 清除1个字符('3')
print('1234${CSI}4$CUB$CSI$DCH$CSI$CUF$CSI$ECH'); // '2 4'
// 插入模式
print('${CSI}4${SM}tree${CSI}3${CUB}h'); // three
print('${CSI}4${RM}tree${CSI}3${CUB}h'); // thee
// 斜体文本
print('${CSI}3$SGR Italicized text ${CSI}0$SGR');
1.4 预定义值
预定义值用Dart风格替换控制功能的使用。以下是一些常用的预定义值:
目标 | 模板 | 函数 | 默认常量 | 描述 |
---|---|---|---|---|
光标上移 | ${cursorUpOpen}$n$cursorUpClose |
cursorUpN(int n) |
cursorUp |
光标上移 n 行(默认1行) |
光标下移 | ${cursorDownOpen}$n$cursorDownClose |
cursorDownN(int n) |
cursorDown |
光标下移 n 行(默认1行) |
光标前移 | ${cursorRightOpen}$n$cursorRightClose |
cursorRightN(int n) |
cursorRight |
光标右移 n 个字符(默认1个字符) |
光标后移 | ${cursorLeftOpen}$n$cursorLeftClose |
cursorLeftN(int n) |
cursorLeft |
光标左移 n 个字符(默认1个字符) |
光标下一行 | ${cursorNextLineOpen}$n$cursorNextLineClose |
cursorNextLineN(int n) |
cursorNextLine |
光标移动到下一行的开始处(默认1行) |
光标上一行 | ${cursorPrevLineOpen}$n$cursorPrevLineClose |
cursorPrevLineN(int n) |
cursorPrevLine |
光标移动到上一行的开始处(默认1行) |
光标水平位置 | ${cursorHPosOpen}$n$cursorHPosClose |
cursorHPosN(int n) |
cursorHPos |
光标移动到第 n 列(默认1列) |
光标位置 | ${cursorPosOpen}$row;$col$cursorPosClose |
cursorPosTo(int row, int col) |
cursorPos |
光标移动到第 row 行,第 col 列 |
光标水平和垂直位置 | ${cursorHVPosOpen}$row;$col$cursorHVPosClose |
cursorHVPosTo(int row, int col) |
cursorHVPos |
与光标位置相同,但有一些差异 |
print('${CSI}4$CUU' == cursorUpN(4)); // true
print('${CSI}4$CUU' == '${cursorUpOpen}4$cursorUpClose'); // true
print('$CSI$CUU' == cursorUp); // true
2. 分析和解析
2.1 AnsiParser
AnsiParser
可以用来分析包含转义码的文本:
const text = '$bold Bold $fgCyan Bold+cyan $resetBoldAndFaint Cyan ';
final parser = AnsiParser(text);
parser.matches.forEach(print);
// Match(start: 0, end: 4, entity: Sgr(bold), state: SgrState(bold))
// Match(start: 4, end: 10, entity: Text(' Bold '), state: SgrState(bold))
// Match(start: 10, end: 15, entity: Sgr(fgCyan), state: SgrState(bold, foreground: Color16(Colors.cyan)))
// Match(start: 15, end: 26, entity: Text(' Bold+cyan '), state: SgrState(bold, foreground: Color16(Colors.cyan)))
// Match(start: 26, end: 31, entity: Sgr(resetBoldAndFaint), state: SgrState(foreground: Color16(Colors.cyan)))
// Match(start: 31, end: 37, entity: Text(' Cyan '), state: SgrState(foreground: Color16(Colors.cyan)))
2.2 快速分析
你可以通过使用扩展来快速分析字符串,而无需使用 AnsiParser
:
import 'package:ansi_escape_codes/extensions.dart';
...
const text = '${fgRed}ERROR$reset';
print(text.hasEscapeCodes); // true
print(text.hasCsi); // true
print(text.hasSgr); // true
print(text.hasForeground); // true
print(text.hasBackground); // false
print(text.showEscapeCodes()); // [CSI 31 SGR]ERROR[CSI 0 SGR]
2.3 AnsiPrinter
AnsiPrinter
可以用来设置默认值并替换转义码:
const text = ' Default text '
'$bgWhite$fgBlack Highlighted text '
'$resetBg$resetFg Default text again $reset';
final printer = AnsiPrinter(
defaultState: SgrPlainState(
background: ColorRgb(44, 43, 124),
foreground: ColorRgb(224, 192, 64),
),
);
printer.print(text);
2.4 堆叠式 AnsiPrinter
堆叠式 AnsiPrinter
可以累积状态变化并依次禁用它们,将当前状态转换为标准转义序列输出:
const text = '$bold 1 $bold 2 $bold 3 $resetBoldAndFaint 2 $resetBoldAndFaint 1 $resetBoldAndFaint';
final printer1 = AnsiPrinter();
final printer2 = AnsiPrinter(stacked: true);
printer1.print(text); // '[bold] 1 2 3 [resetBoldAndFaint] 2 1 '
printer2.print(text); // '[bold] 1 2 3 2 1 [resetBoldAndFaint]'
示例代码
以下是一个完整的示例代码,展示了如何使用 ansi_escape_codes
插件进行ANSI转义码处理:
import 'dart:io';
import 'package:ansi_escape_codes/ansi_escape_codes.dart';
import 'package:ansi_escape_codes/controls.dart';
void main() {
{
const text1 = '$fgGreen(fgGreen)$resetFg'
' $fgHighGreen(fgHighGreen)$resetFg'
' $fg256Green(fg256Green)$resetFg'
' $fg256Open$HIGH_GREEN$fg256Close(fg256Open highGreen fg256Close)$resetFg'
' $fg256Rgb050(fg256Rgb050)$resetFg'
' ${fgRgbOpen}0;255;0$fgRgbClose(fgRgbOpen 0;255;0 fgRgbClose)$resetFg';
const text2 = '$fgGreen'
'$bgYellow(bgYellow)$resetBg'
' $bgHighYellow(bgHighYellow)$resetBg'
' $bg256Yellow(bg256Yellow)$resetBg'
' $bg256Open$HIGH_YELLOW$bg256Close(bg256Open highYellow bg256Close)$resetBg'
' $bg256HighYellow(bg256HighYellow)$resetBg'
' $bg256Rgb550(bg256Rgb550)$resetBg'
' ${bgRgbOpen}255;255;0$bgRgbClose(bgRgbOpen 255;255;0 bgRgbClose)$resetBg'
'$resetFg';
const text3 = '$negative$text2$resetNegative';
const text4 = 'default $bold(bold)$resetBoldAndFaint'
' $faint(faint)$resetBoldAndFaint'
' $italicized(italicized)$resetItalicized'
' $underlined(underlined)$resetUnderlined'
' $doublyUnderlined(doublyUnderlined)$resetUnderlined'
' $crossedOut(crossedOut)$resetCrossedOut'
' $concealed(concealed)$resetConcealed'
' $slowlyBlinking(slowlyBlinking)'
' $rapidlyBlinking(rapidlyBlinking)$resetBlinking';
const text5 = '$negative$text4$resetNegative';
const texts = [text1, text2, text3, text4, text5];
for (final (index, text) in texts.indexed) {
print('${index + 1}: $text (${text.length})');
}
print('');
for (final (index, text) in texts.indexed) {
final parser = AnsiParser(text);
final textWithoutEscapeCodes = parser.removeAll();
print('${index + 1}: $textWithoutEscapeCodes (${parser.length})');
}
// showControlFunctions.
print('');
final parser = AnsiParser(text4);
print(
'4: '
'${parser.showControlFunctions(
open: '$faint[',
close: ']$resetBoldAndFaint',
)}',
);
// matches.
print('');
final buf = StringBuffer('4: ');
for (final m in parser.matches) {
switch (m.entity) {
case Text(:final string):
buf.write(string);
case EscapeCode():
// no-op
}
}
print(buf);
}
// Invalid values.
{
print('');
print('Invalid values:');
print(
AnsiParser(
'$CSI$FOREGROUND;$COLOR_256;256$SGR'
'$CSI$UNDERLINE_COLOR;3;1;2;3$SGR'
'$CSI$BACKGROUND;$COLOR_RGB;100$SGR'
'$CSI$BACKGROUND;$COLOR_RGB;200$SGR'
'$CSI$BACKGROUND;$COLOR_RGB;200;$SGR'
'$CSI$BACKGROUND;$COLOR_RGB;200;;$SGR'
'$CSI$BACKGROUND;$COLOR_RGB;200;0$SGR'
'$CSI$BACKGROUND;$COLOR_RGB;200;0;$SGR'
'${CSI}256%',
).showControlFunctions(),
);
}
// stateAtPos.
{
print('');
const text =
'${bold}bold ${italicized}bold+italic ${resetBoldAndFaint}italic$resetItalicized';
final parser = AnsiParser(text);
print(text);
final stateAtPos0 = parser.stateAtPos(0);
print(
'^-- isBold=${stateAtPos0.isBold}'
', isItalicized=${stateAtPos0.isItalicized}',
);
final stateAtPos5 = parser.stateAtPos(5);
print(
'${' ' * 5}^-- isBold=${stateAtPos5.isBold}'
', isItalicized=${stateAtPos5.isItalicized}',
);
final stateAtPos17 = parser.stateAtPos(17);
print(
'${' ' * 17}^-- isBold=${stateAtPos17.isBold}'
', isItalicized=${stateAtPos17.isItalicized}',
);
final stateAtPos23 = parser.stateAtPos(23);
print(
'${' ' * 23}^-- isBold=${stateAtPos23.isBold}'
', isItalicized=${stateAtPos23.isItalicized}',
);
}
// substring.
{
print('');
final parser = AnsiParser(
'$fgWhite${bold}Lorem$resetBoldAndFaint '
'$bgHighRed$fgHighWhite${italicized}ipsum dolor sit$resetItalicized$resetBg$resetFg'
'$fgWhite amet, consectetur $fgRed${underlined}adipiscing$resetUnderlined'
'$fgWhite elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.$reset',
);
for (var i = 0; i < 40; i += 2) {
print('"${parser.substring(i, maxLength: 40)}"');
}
}
// AnsiPrinter.
{
print('');
const text = ' default colors'
' $fg256Rgb550$bg256Rgb031 yellow on green'
' $resetFg$resetBg default colors ';
print('Standart output (the default colors are set by the terminal):');
print(text);
const defaultState = SgrPlainState(
foreground: Color256(Colors.rgb555),
background: Color256(Colors.rgb320),
);
print('');
print('With AnsiPrinter (the default state is overrided):');
final printer = AnsiPrinter(
defaultState: defaultState,
);
printer.print(text);
print('');
print('With runZonedAnsiPrinter (the default state is overrided):');
runZonedAnsiPrinter(
defaultState: defaultState,
() {
print(text);
},
);
}
// Stacked AnsiPrinter.
{
const importantNote = '${italicized}Important note$resetItalicized';
const text1 = 'Normal text $importantNote Normal text';
const text2 =
'${italicized}Important text $importantNote Important text$resetItalicized';
print('');
print(text1); // Normal text <i>Important</i> note Normal text
print('');
print('Insertion breaks the style of the text:');
print(text2); // <i>Important text Important note</i> Important text
print('');
print('The state is accumulated in the stack:');
final stackedPrinter = AnsiPrinter(stacked: true);
stackedPrinter
.print(text2); // <i>Important text Important note Important text</i>
}
// Tabs.
if (stdout.hasTerminal) {
print('');
const text = '0\t1\t2\t3\t4\t5\t6\t7\t8\t9';
print(text);
tabs(defaultTab: 12);
print(text);
tabs();
}
}
更多关于Flutter ANSI转义码处理插件ansi_escape_codes的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter ANSI转义码处理插件ansi_escape_codes的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中,ansi_escape_codes
是一个用于处理ANSI转义码的插件。ANSI转义码通常用于在终端中设置文本颜色、背景颜色、文本样式等。ansi_escape_codes
插件可以帮助你在Flutter应用中解析和处理这些转义码。
安装插件
首先,你需要在 pubspec.yaml
文件中添加 ansi_escape_codes
依赖:
dependencies:
flutter:
sdk: flutter
ansi_escape_codes: ^1.0.0 # 请检查最新版本
然后运行 flutter pub get
来安装依赖。
使用插件
ansi_escape_codes
插件提供了一些工具函数来解析和处理ANSI转义码。以下是一些基本的使用示例:
1. 解析ANSI转义码
import 'package:ansi_escape_codes/ansi_escape_codes.dart';
void main() {
String ansiString = '\x1B[31mThis is red text\x1B[0m';
List<AnsiCode> codes = parseAnsiCodes(ansiString);
for (var code in codes) {
print('Code: ${code.code}, Text: ${code.text}');
}
}
在这个例子中,parseAnsiCodes
函数会将包含ANSI转义码的字符串解析为一个 AnsiCode
对象的列表,每个对象包含转义码和对应的文本。
2. 移除ANSI转义码
如果你只想移除字符串中的ANSI转义码,可以使用 stripAnsiCodes
函数:
import 'package:ansi_escape_codes/ansi_escape_codes.dart';
void main() {
String ansiString = '\x1B[31mThis is red text\x1B[0m';
String plainText = stripAnsiCodes(ansiString);
print(plainText); // 输出: This is red text
}
3. 应用ANSI转义码
你还可以使用 applyAnsiCodes
函数将ANSI转义码应用到文本上:
import 'package:ansi_escape_codes/ansi_escape_codes.dart';
void main() {
String text = 'This is red text';
String ansiString = applyAnsiCodes(text, [AnsiCode(31, '')]);
print(ansiString); // 输出: \x1B[31mThis is red text\x1B[0m
}