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

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 13 июн 2017, 12:58

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

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

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

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 13 июн 2017, 13:23

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
Вложения
r1.png
rang.hpp
(6.39 КБ) 93 скачивания
rang-test.cc
(262 байт) 99 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 13 июн 2017, 19:47

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;
   }
}

И то, как это выполняется, показано на картинке.
Вложения
r2.png
task213s.cc
(2.33 КБ) 97 скачиваний
213_2.dat
(63 байт) 92 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 13 июн 2017, 20:01

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

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

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

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

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 13 июн 2017, 20:18

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

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

   sort( vp.begin(), vp.end(), 
         []( vector<string>::iterator f, vector<string>::iterator s )-> bool { 
            return f->length() < s->length(); 
         } ); 
И выполняется, например, как на картинке.
Вложения
r3.png
task213sa.cc
(1.61 КБ) 88 скачиваний
213_1.dat
(611 байт) 97 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 12 янв 2018, 21:26

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

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

#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
...
А далее - так как это на картинке.
Вложения
color.tgz
(152.53 КБ) 80 скачиваний
clr.png

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 14 янв 2018, 18:58

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

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

#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
Вложения
minmax.png

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

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

Непрочитанное сообщение Olej » 17 янв 2018, 20:59

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

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 8 гостей