Страница 1 из 1

отображение цветом в терминале

Добавлено: 13 июн 2017, 12:58
Olej
Иногда возникает потребность выделять какие-то выводы в терминал цветом.
На то есть (описаны, используются) разные способы: ESC управляющие последовательности (использующиеся ещё со времён MS-DOS), возможности пакета ncurses и др.

Но проблема (неприятность) в том, что:
- при работе в Linux практически повсеместно используется эмулятор терминала в графическом окружении рабочего стола (DE)...
- в зависимости от вида DE (которых десяток или более: KDE, GNOME, Mate, Cinnamon, ...) + используемой программы эмулятора терминала (которых в каждом DE тоже может быть несколько штук) - отдельные ESC последовательности могут отрабатываться, а могут и нет... (у меня, пока ;-) , не отрабатываются ... или я ещё не разобрался почему)
- но ещё хуже, когда предстоит перенос кода программы в другую операционную систему, главным образом в Windows, с тем, чтобы и там в их текстовой консоли отображение делалось так же.

И вот есть тут такой проект C++ (подсказали), который позволяет управлять характеристиками вывода в терминал (не только цвет) независимо от платформы: rang.
Оно, обещано ;-) , работает равнозначно в коде Linux, Windows, MacOS и т.д.
Испытаем!

Re: отображение цветом в терминале

Добавлено: 13 июн 2017, 13:23
Olej
Olej писал(а):Испытаем!
Т.е., как оказывается, у них там есть, и единственно нужен, заголовочный файл:

Код: Выделить всё

[olej@dell rang]$ ls -l rang.hpp 
-rw-rw-r-- 1 olej olej 6540 июн 12 02:15 rang.hpp
Который всего лишь подключается #include ...
Вот их же пример кода:

Код: Выделить всё

#include <iostream>
#include "rang.hpp"
using namespace std;

int main( int argc, char** argv ) {
   cout << "No rang?"
        << rang::bg::red
        << rang::fg::gray
        << "RANG!!!"
        << rang::style::reset
        << endl;
}
И исполнение его показано на картинке.
P.S. Единственно, обращаю внимание, что компиляция требуется с указанием опциями совместимости со стандартом C++11 (2011 года):

Код: Выделить всё

[olej@dell rang]$ make rang-test
g++ -Wall -std=c++11 -O3      rang-test.cc   -o rang-test

Re: отображение цветом в терминале

Добавлено: 13 июн 2017, 19:47
Olej
Olej писал(а):Испытаем!
Этот вот новый интерес к такой теме возник из задачи (учебной, студенческой) ... вот как я её переформулирую, выбросив всё лишнее:
Программа читает текстовый файл (имя с расширением .dat, заданное в командной строке запуска).
После этого программа:
- выводит текст на экран дисплея;
- определяет количество символов в каждом слове;
- выбирает для показа самое короткое слово;
- выбранное слово выделяется цветом в выводимом тексте;
- при очередном нажатии клавиши Enter выделение перемещается на следующее по длине слово.
Вообще то говоря, никаких оговорок относительно языка текста (локализации) явно не оговаривается, но по контексту понятно, что текст там может быть, в том числе, русскоязычным. И, по хорошему, всё это должно реализовываться в типах wchar_t и wstring ... но именно разбиение на слова может нормально работать и на широких 4-байтных UTF-8 символах (это один из немногих редких случаях), и поэтому всё я сделаю в терминологии string:

Код: Выделить всё

#include <locale>
#include <iostream>
#include <fstream>
#include <vector> 
using namespace std;
#include "rang.hpp"

vector<string>::iterator current;

ostream& operator <<( ostream& out, const vector<string>& v ) {
   for( vector<string>::const_iterator i = v.begin(); i != v.end(); i++ ) 
      if( i != current ) 
         out << *i << " ";
      else 
         out << rang::fg::red << *i << rang::style::reset 
             << "[" << i->length() << "] ";
   return out;
}

bool sort_function( vector<string>::iterator f, vector<string>::iterator s ) { 
   return f->length() < s->length(); 
} 

int main( int argc, char** argv ) {
   setlocale( LC_ALL, "rus" );  
   if( argc != 2 ) {
      cerr << "не указан файл данных" << endl;
      return 1;
   }
   ifstream fin;
   fin.open( argv[ 1 ] );
   if( !fin ) {
      cerr << "файл данных не найден: " << argv[ 1 ] << endl;
      return 1;
   }
   string line;
   do {
      getline( fin, line );
      cout << line << endl;
   } while( fin );
   fin.close();
   fin.open( argv[ 1 ] );
   vector<string> vw{ istream_iterator<string>( fin ), istream_iterator<string>() }; 
   cout << "- в вашем тексте слов: " << vw.size() << endl; 
   vector<vector<string>::iterator> vp( vw.size() );
   unsigned np = 0;
   for( vector<string>::iterator w = vw.begin(); w != vw.end(); w++ )
      vp[ np++ ] = w;  
   sort( vp.begin(), vp.end(), sort_function ); 
   cout << "дальше жмите Enter ..." << endl;
   np = 0;
   while( true ) {
      current = vp[ np ];
      getline( cin, line );
      if( ++np >= vp.size() ) np = 0;
      cout << vw << endl;
   }
}

И то, как это выполняется, показано на картинке.

Re: отображение цветом в терминале

Добавлено: 13 июн 2017, 20:01
Olej
Olej писал(а):И то, как это выполняется, показано на картинке.
Там есть ещё ряд интересных деталей:
- для того, чтобы не портить входной текст (не переставлять в бессмыслице слова в векторе vw), я сортирую не сами слова в тексте (по числу байт в слове), а через указатели (итераторы) на них в другом, смежном векторе vp;
- сортировка делается вызовом стандартного алгоритма быстрой сортировки из STL библиотеки C++ (стандартная библиотека):

Код: Выделить всё

sort( vp.begin(), vp.end(), sort_function );
- я только передаю в этот стандартный алгоритм сортировки свою функцию sort_function, которая возвращает критерий сравнения элементов (предикат), по которым производится сортировка;
- я не стал специально в этом месте записывать анонимною лямбда-функцию (как делаю это часто, и что здесь наиболее уместно), а специально выписал эту функцию отдельно для понятности (... ну и для облегчения себе отладки, сознаюсь ;-) );
- сортировка сортирует указатели строк (итераторы) в vp по длине (числу байт) в строках, на которые они (итераторы) указывают.

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

Re: отображение цветом в терминале

Добавлено: 13 июн 2017, 20:18
Olej
Olej писал(а):P.S. И я предполагаю обязательно показать это, выписанное через лямбда-функцию, как наиболее уместное решение ... но позже, когда отлажу и проверю этот вариант.
И выглядит это (показываю только отличающийся оператор сортировки) как-то так:

Код: Выделить всё

   sort( vp.begin(), vp.end(), 
         []( vector<string>::iterator f, vector<string>::iterator s )-> bool { 
            return f->length() < s->length(); 
         } ); 
И выполняется, например, как на картинке.

Re: отображение цветом в терминале

Добавлено: 12 янв 2018, 21:26
Olej
Возвращаясь ещё раз к цветовой разметке вывода в терминал - на этот раз более простая и наглядная программа: разметить слова из текста, читаемого из файла, в различные цвета.

Код: Выделить всё

#include <fstream>
#include <sstream>
using namespace std;
#include "rang.hpp"
using namespace rang;

int main( int argc, char** argv ) {
   const rang::fg clrs[] = { fg::red, fg::green, fg::yellow, fg::blue, fg::magenta, fg::cyan };
   const int clsrn = sizeof( clrs ) / sizeof( clrs[ 0 ] );
   if( argc != 2 ) {
      cerr << "не указан файл данных" << endl;
      return 1;
   }
   ifstream fin;
   fin.open( argv[ 1 ] );
   if( !fin ) {
      cerr << "файл данных не найден: " << argv[ 1 ] << endl;
      return 2;
   }
   int c = 0;
   while( true ) {
      string s;
      getline( fin, s );
      if( !fin ) return 0;
      istringstream ss( s );
      string w;
      while( ss >> w )
         cout << clrs[ c++ % clsrn ] << w << rang::style::reset << " ";
      cout << endl;
   }
}
Выглядит это так:

Код: Выделить всё

[olej@dell color]$ ./color 1.txt
...
А далее - так как это на картинке.

Re: отображение цветом в терминале

Добавлено: 14 янв 2018, 18:58
Olej
И совсем для простоты (объяснений): выделить минимальное и максимальное значение в массиве каким-нибудь цветом отличающимся от остальных элементов массива.

Код: Выделить всё

#include <iostream>
#include <algorithm>
using namespace std;
#include "rang.hpp"

int main( int argc, char** argv ) {
   int array[] = { 10, 9, 11, 8, 13, 6, 14, 4, 12, 2, 8 },
       size = sizeof( array ) / sizeof( array[ 0 ] );
   auto mm = minmax_element( array, array + size );
   cout << "диапазон: '" << *mm.first << "' ... '" << *mm.second << "'" << endl;
   for( auto ia = array; ia < array + size; ia++ )
      if( ia == mm.first )
         cout << rang::fg::red << *ia << rang::style::reset << ' ';
      else if ( ia == mm.second )
         cout << rang::fg::green << *ia << rang::style::reset << ' ';
      else
         cout << *ia << ' ';
   cout << endl;
}
1. Это будет работать почти в любой операционной системе: Windows, Linux, MacOS.
2. Это нужно компилировать с опцией совместимости со стандартом C++11, так для упрощения записан код. Но это легко переписать (вместо auto записать реальный тип итераторов) так, чтобы всё было корректно и в более ранних стандартах C++.

Код: Выделить всё

[olej@dell color]$ g++ -Wall -std=c++11 -O3      minmax.cc   -o minmax

Re: отображение цветом в терминале

Добавлено: 17 янв 2018, 20:59
Olej
Новая статья по цветовой разметке терминала:
ЦВЕТА ТЕРМИНАЛА LINUX
Январь 1, 2018
Пока это только ... познавательная информация, практическая ценность которой - херня. Но там можно извлечь информацию про Esc-последовательности, в том числе, и по управлению цветом.
Подробнее Esc-последовательности - здесь: ANSI escape code.