如何在不破坏ANSI转义码的情况下将字符串截断为最大长度

时间:2014-10-07 14:35:19

标签: javascript node.js ansi-escape

我在Node.js中有一个子进程,它生成ANSI-escape彩色输出,有时比我的终端更宽。

我想截断长行,但不打破ANSI颜色代码,也不计算不可见的字符(这样有很多转义的行也不会太短

2 个答案:

答案 0 :(得分:1)

首先,您需要确保处理来自stdout的完整行,这可能会在行之间提供片段:

var acc = '';
child.stdout.setEncoding('utf8');       // get strings rather than buffers
child.stdout.on('data', function(data){ // when output is written by child

    // split lines, including linebreaks:
    data.split(/(\r?\n)/).forEach(function(frag) {
        if (frag == '\r\n' || frag == '\n') {        // if linebreak,
            console.log(filterAnsi(acc, width));     //   output filtered completed line
            acc = '';                                //   clear accumulator line
        } else {                                     // if not linebreak,
            acc += frag;                             //   add fragment to accumulator
        }
    });
});

接下来是过滤器本身:

// Crop the length of lines, ANSI escape code aware
// Always outputs every escape char, regardless of length (so we always end up with a sane state)
// Visible characters are filtered out once length is exceeded
function filterAnsi(str, len) {
    if (!len || len < 10) return str; // probably not a valid console -- send back the whole line
    var count = 0,        // number of visible chars on line so far
        esc = false,      // in an escape sequence
        longesc = false;  // in a multi-character escape sequence
    var outp = true;      // should output this character
    return str.split('').filter(function(c){ // filter characters...
        if (esc && !longesc && c == '[') longesc = true; // have seen an escape, now '[', start multi-char escape
        if (c == '\x1b') esc = true; // start of escape sequence

        outp = (count < len || esc); // if length exceeded, don't output non-escape chars
        if (!esc && !longesc) count++; // if not in escape, count visible char

        if (esc && !longesc && c != '\x1b') esc = false; // out of single char escape
        if (longesc && c != '[' && c >= '@' && c <= '~') {esc=false; longesc=false;} // end of multi-char escape

        return outp; // result for filter
    }).join(''); // glue chars back into string
}

答案 1 :(得分:1)

您可能想尝试这个微小的NPM库:printable-characters

它处理包含零宽度字符和ANSI转义码的字符串的所有负担。

在不破坏ANSI代码的情况下获得前N个符号:

const { first } = require ('printable-characters')

const s = '\u001b[22mfoobar\u001b[22m'

first (s, 0) // === '\u001b[22m\u001b[22m'
first (s, 1) // === '\u001b[22mf\u001b[22m'
first (s, 3) // === '\u001b[22mfoo\u001b[22m'
first (s, 6) // === '\u001b[22mfoobar\u001b[22m'