Показывает все непечатаемые символы ANSI и метасимволы в строке Javascript

#javascript #regex #string

#javascript #регулярное выражение #строка

Вопрос:

Я получаю вывод стандартного вывода из множества довольно случайных процессов оболочки, все в качестве входных данных (stdin) на одном node.js процесс. Для отладки и синтаксического анализа мне нужно обрабатывать различные специальные коды символов, которые передаются в процесс. Это действительно помогло бы мне увидеть невидимые символы (в основном для отладки) и соответствующим образом с ними разобраться, как только я определю шаблоны, в которых они используются.

Учитывая строку javascript со специальными символами ANSI u001b* и / или метасимволами, такими как n , t , r и т.д., Как можно выявить эти специальные символы, чтобы они фактически не отображались, а скорее отображались как их кодовое значение.

Например, допустим, у меня есть следующая строка, напечатанная зеленым цветом (не могу отобразить зеленый цвет на SO):

     This is a string.
We are now using the green color.
  

Я хотел бы иметь возможность выполнить console.log (например) для этой строки и заменить непечатаемые символы, метасимволы / новые строки, цветовые коды и т. Д. На их коды ANSI:

 "u001b[32mtThis is a string.nWe are now using the green color.n"
  

Я могу сделать что-то вроде следующего, но это слишком специфично, жестко запрограммировано и неэффективно:

 line = line.replace(/[f]/g, '\n');
line = line.replace(/u0008/g, '\b');
line = line.replace(/u001b|u001B/g, '\u001b');
line = line.replace(/rn|r|n/g, '\n');
...
  

Комментарии:

1. Раскрывает каким образом? (кроме того, регулярные выражения не являются хорошим инструментом для этой задачи.)

2. Почему бы просто не перебирать строку по одному символу за раз и не просматривать каждое значение в строке, чтобы увидеть, является ли это чем-то, что вы хотите рассматривать как специальный символ, а затем создавать новую, измененную строку по ходу работы.

3. Хммм, это звучит очень медленно … это нужно сделать как можно эффективнее. И почему регулярное выражение не является хорошим инструментом для такого рода вещей?

4. escape или decodeURIComponent изменят не словесные символы на несколько символов…

5. Прежде всего, вы должны описать, в чем заключается реальная проблема , которую вы пытаетесь решить здесь.

Ответ №1:

Попробуйте это:

 var map = {  // Special characters
    '\': '\',
    'n': 'n',
    'r': 'r',
    't': 't'
};
str = str.replace(/[\nrt]/g, function(i) {
    return '\' map[i];
});
str = str.replace(/[^ -~]/g, function(i){
    return '\u' ("000"   i.charCodeAt(0).toString(16)).slice(-4);
});
  

Комментарии:

1. Как вы думаете, как это работает? Вы создаете N объектов регулярных выражений и выполняете N операций замены регулярных выражений. Это не похоже на рецепт хорошей производительности, который, по словам OP, был важен.

2. @jfriend00 Но они заменяют только специальные символы, которые должны быть экранированы по-другому ( n , t , …), их не должно быть много. Большинство символов будут экранированы за одну операцию замены.

3. OP показывает не менее 10 символов со специальными именами, которые они хотят показать, и подразумевает, что их может быть намного больше.

4. @jfriend00 обновлено, теперь это всего две операции замены.

5. @Oriol Большое вам спасибо за ваш ответ! На самом деле это ближе всего к тому, что я просил на общем уровне. Но jfriend00 имеет смысл отметить, что это не самый эффективный общий алгоритм. Но секретный соус в вашем ответе ( charCodeAt бит) можно объединить с его ответом для суперэффективного, динамичного решения. Я сделаю jsperf для своего окончательного решения завтра утром 🙂 Еще раз спасибо!

Ответ №2:

Вот версия, которая перебирает строку, проверяет, является ли это обычным печатным символом, и, если нет, ищет его в специальной таблице для вашего собственного представления этого символа, и если он не найден в таблице, отображает любое представление по умолчанию, которое вы хотите:

 var tagKeys = {
    'n': 'New Line n',
    'u0009': 'Tab',
    'u2029': 'Line Separator'
    /* and so on */
};

function tagSpecialChars(str) {
    var output = "", ch, replacement;
    for (var i = 0; i < str.length; i  ) {
        ch = str.charAt(i);
        if (ch < ' ' || ch > '~') {
            replacement = tagKeys[ch];
            if (replacement) {
                ch = replacement;
            } else {
                // default value
                // could also use charCodeAt() to get the numeric value                     
                ch = '*****';
            }
        }
        output  = ch;
    }
    return output;
}
  

Демонстрация: http://jsfiddle.net/jfriend00/bCYa4 /

Очевидно, что это не какое-то причудливое решение для регулярных выражений, но вы сказали, что производительность важна, и вы редко находите наиболее эффективную операцию с использованием регулярного выражения, и, конечно, нет, если вы собираетесь использовать целую кучу из них. Плюс каждая замена регулярного выражения в любом случае должна проходить через всю строку.

Это решение, похожее на workman, просто перебирает входную строку один раз и позволяет настроить преобразование отображения для любого нужного вам непечатаемого символа, а также определить, что вы хотите отобразить, если это непечатаемый символ, для которого у вас нет специального представления на дисплее.

Комментарии:

1. Я выбрал этот ответ из-за его преимущества в производительности. Спасибо! Улучшением этого может быть создание tagKeys dynamic и добавление к нему, когда он обнаруживает новое значение кода символа в ответе ala Oriol charCodeAt . На самом деле это лучше всего подходит для того, что я просил, но это лучший общий алгоритм, который можно использовать для достижения этой цели. Я ценю, что вы нашли время ответить на это, несмотря на мой неоднозначный вопрос. 🙂

2. @jsfriend jsperf.com/regex-vs-brute-force/3 Я немного изменил ваш код, включив бит charCodeAt из Oriol, чтобы создать динамический список, который растет при появлении новых кодов. Пожалуйста, скажите мне, что вы думаете!

3. @ArjunMehta — Чтобы вы понимали, ввод динамически генерируемых имен tagKeys выполняется исключительно как кэш по соображениям производительности. Если бы специальные символы не повторялись, это было бы на самом деле медленнее. Если некоторые из них повторяются, кэш может фактически повысить вашу производительность, сэкономив вам некоторое время на создании строкового представления для повторяющихся символов — выигрыш / потеря в производительности будет зависеть от характера входной строки. Но это не обязательно, чтобы заставить код делать то, что вы хотите, поскольку вы можете просто сгенерировать имя в любое время, когда встречается код.

4. привет! Да, я это понимаю. Это полезно для моего конкретного случая, поскольку строки будут постоянно меняться и будут совершенно непредсказуемыми в том, сколько и какие специальные символы могут использоваться, и приложение будет иметь увеличенное время безотказной работы. таким образом, наличие кэша специальных символов для сопоставления будет очень полезно для долгосрочной производительности. Еще раз спасибо!

5. @ArjunMehta — Хорошо, тогда все в порядке. Рад, что вы получили то, что вам было нужно.