Существует ли заголовок для цветов консоли в C?

Вопрос или проблема

Я не нашел заголовок для основных цветов текста в C, поэтому написал его в рамках программы, которая создает цветную ASCII диаграмму. Программа компилировалась без ошибок как в Visual Studio 2022 с любым из параметров компилятора (/TC) или по умолчанию C++, так и в GCC. Программа включает в себя AsciiChart.c и concolor.h и, кажется, работает хорошо. Оба файла содержат исчерпывающие комментарии. У меня вопрос: можно ли эти файлы написать более эффективно?

concolor.h

#ifndef CONCOLOR_H  // это тест 'guard', предотвращающий перезапись другого заголовка с таким именем
#define CONCOLOR_H  // если нет другого заголовка с таким именем, определите его

/*  
    Следующие 'defines' связывают каждую строку ANSI кода с читаемым именем.
    Чтобы использовать эти цвета/стили, просто введите заглавное слово в своих 'printf' выражениях
    после строки форматирования и перед литеральными строками или переменными,
    за которым следует запятая (примеры в файле 'asciichart.c').
    Не забудьте добавить еще '%s' в строку форматирования в качестве заполнителя для строки цвета.
    Есть способы уменьшить этот файл до массивов и/или указателей, но это легче понять.
*/


//Сброс цвета
#define CREST "\x1B[0m"

//Обычный текст
#define NBLK "\x1B[0;30m"
#define NRED "\x1B[0;31m"
#define NGRN "\x1B[0;32m"
#define NYEL "\x1B[0;33m"
#define NBLU "\x1B[0;34m"
#define NMAG "\x1B[0;35m"
#define NCYN "\x1B[0;36m"
#define NWHT "\x1B[0;37m"

//Текст высокой интенсивности
#define HNBLK "\x1B[0;90m"
#define HNRED "\x1B[0;91m"
#define HNGRN "\x1B[0;92m"
#define HNYEL "\x1B[0;93m"
#define HNBLU "\x1B[0;94m"
#define HNMAG "\x1B[0;95m"
#define HNCYN "\x1B[0;96m"
#define HNWHT "\x1B[0;97m"

//Жирный текст
#define BBLK "\x1B[1;30m"
#define BRED "\x1B[1;31m"
#define BGRN "\x1B[1;32m"
#define BYEL "\x1B[1;33m"
#define BBLU "\x1B[1;34m"
#define BMAG "\x1B[1;35m"
#define BCYN "\x1B[1;36m"
#define BWHT "\x1B[1;37m"

//Жирный текст высокой интенсивности
#define HBBLK "\x1B[1;90m"
#define HBRED "\x1B[1;91m"
#define HBGRN "\x1B[1;92m"
#define HBYEL "\x1B[1;93m"
#define HBBLU "\x1B[1;94m"
#define HBMAG "\x1B[1;95m"
#define HBCYN "\x1B[1;96m"
#define HBWHT "\x1B[1;97m"

//Бледный текст
#define FBLK "\x1B[2;30m"
#define FRED "\x1B[2;31m"
#define FGRN "\x1B[2;32m"
#define FYEL "\x1B[2;33m"
#define FBLU "\x1B[2;34m"
#define FMAG "\x1B[2;35m"
#define FCYN "\x1B[2;36m"
#define FWHT "\x1B[2;37m"

//Бледный текст высокой интенсивности
#define HFBLK "\x1B[2;90m"
#define HFRED "\x1B[2;91m"
#define HFGRN "\x1B[2;92m"
#define HFYEL "\x1B[2;93m"
#define HFBLU "\x1B[2;94m"
#define HFMAG "\x1B[2;95m"
#define HFCYN "\x1B[2;96m"
#define HFWHT "\x1B[2;97m"

//Курсивный текст
#define IBLK "\x1B[3;30m"
#define IRED "\x1B[3;31m"
#define IGRN "\x1B[3;32m"
#define IYEL "\x1B[3;33m"
#define IBLU "\x1B[3;34m"
#define IMAG "\x1B[3;35m"
#define ICYN "\x1B[3;36m"
#define IWHT "\x1B[3;37m"

//Курсивный текст высокой интенсивности
#define HIBLK "\x1B[3;90m"
#define HIRED "\x1B[3;91m"
#define HIGRN "\x1B[3;92m"
#define HIYEL "\x1B[3;93m"
#define HIBLU "\x1B[3;94m"
#define HIMAG "\x1B[3;95m"
#define HICYN "\x1B[3;96m"
#define HIWHT "\x1B[3;97m"

//Подчеркнутый текст
#define UBLK "\x1B[4;30m"
#define URED "\x1B[4;31m"
#define UGRN "\x1B[4;32m"
#define UYEL "\x1B[4;33m"
#define UBLU "\x1B[4;34m"
#define UMAG "\x1B[4;35m"
#define UCYN "\x1B[4;36m"
#define UWHT "\x1B[4;37m"

//Подчеркнутый текст высокой интенсивности
#define HUBLK "\x1B[4;90m"
#define HURED "\x1B[4;91m"
#define HUGRN "\x1B[4;92m"
#define HUYEL "\x1B[4;93m"
#define HUBLU "\x1B[4;94m"
#define HUMAG "\x1B[4;95m"
#define HUCYN "\x1B[4;96m"
#define HUWHT "\x1B[4;97m"

//Медленно мигающий текст
#define LBLK "\x1B[5;30m"
#define LRED "\x1B[5;31m"
#define LGRN "\x1B[5;32m"
#define LYEL "\x1B[5;33m"
#define LBLU "\x1B[5;34m"
#define LMAG "\x1B[5;35m"
#define LCYN "\x1B[5;36m"
#define LWHT "\x1B[5;37m"

//Медленно мигающий текст высокой интенсивности
#define HLBLK "\x1B[5;90m"
#define HLRED "\x1B[5;91m"
#define HLGRN "\x1B[5;92m"
#define HLYEL "\x1B[5;93m"
#define HLBLU "\x1B[5;94m"
#define HLMAG "\x1B[5;95m"
#define HLCYN "\x1B[5;96m"
#define HLWHT "\x1B[5;97m"

//Быстро мигающий текст
#define QBLK "\x1B[6;30m"
#define QRED "\x1B[6;31m"
#define QGRN "\x1B[6;32m"
#define QYEL "\x1B[6;33m"
#define QBLU "\x1B[6;34m"
#define QMAG "\x1B[6;35m"
#define QCYN "\x1B[6;36m"
#define QWHT "\x1B[6;37m"

//Быстро мигающий текст высокой интенсивности
#define HQBLK "\x1B[6;90m"
#define HQRED "\x1B[6;91m"
#define HQGRN "\x1B[6;92m"
#define HQYEL "\x1B[6;93m"
#define HQBLU "\x1B[6;94m"
#define HQMAG "\x1B[6;95m"
#define HQCYN "\x1B[6;96m"
#define HQWHT "\x1B[6;97m"

//Обратный текст
#define RBLK "\x1B[7;30m"
#define RRED "\x1B[7;31m"
#define RGRN "\x1B[7;32m"
#define RYEL "\x1B[7;33m"
#define RBLU "\x1B[7;34m"
#define RMAG "\x1B[7;35m"
#define RCYN "\x1B[7;36m"
#define RWHT "\x1B[7;37m"

//Обратный текст высокой интенсивности
#define HRBLK "\x1B[7;90m"
#define HRRED "\x1B[7;91m"
#define HRGRN "\x1B[7;92m"
#define HRYEL "\x1B[7;93m"
#define HRBLU "\x1B[7;94m"
#define HRMAG "\x1B[7;95m"
#define HRCYN "\x1B[7;96m"
#define HRWHT "\x1B[7;97m"

//Текст с зачеркиванием
#define SBLK "\x1B[9;30m"
#define SRED "\x1B[9;31m"
#define SGRN "\x1B[9;32m"
#define SYEL "\x1B[9;33m"
#define SBLU "\x1B[9;34m"
#define SMAG "\x1B[9;35m"
#define SCYN "\x1B[9;36m"
#define SWHT "\x1B[9;37m"

//Текст с зачеркиванием высокой интенсивности
#define HSBLK "\x1B[9;90m"
#define HSRED "\x1B[9;91m"
#define HSGRN "\x1B[9;92m"
#define HSYEL "\x1B[9;93m"
#define HSBLU "\x1B[9;94m"
#define HSMAG "\x1B[9;95m"
#define HSCYN "\x1B[9;96m"
#define HSWHT "\x1B[9;97m"


//Обычный фон
#define NBLKB "\x1B[0;40m"
#define NREDB "\x1B[0;41m"
#define NGRNB "\x1B[0;42m"
#define NYELB "\x1B[0;43m"
#define NBLUB "\x1B[0;44m"
#define NMAGB "\x1B[0;45m"
#define NCYNB "\x1B[0;46m"
#define NWHTB "\x1B[0;47m"

//Фон высокой интенсивности 
#define HBLKB "\x1B[0;100m"
#define HREDB "\x1B[0;101m"
#define HGRNB "\x1B[0;102m"
#define HYELB "\x1B[0;103m"
#define HBLUB "\x1B[0;104m"
#define HMAGB "\x1B[0;105m"
#define HCYNB "\x1B[0;106m"
#define HWHTB "\x1B[0;107m"

#endif  //CONCOLOR_H


asciichart.c
    
/*  Эта программа будет отображать ASCII диаграмму в командном окне
    для каждого соответствующего десятичного, шестнадцатеричного и восьмеричного числа.
    Первая страница - стандартный набор ASCII, вторая - расширенный набор.
    Страницы будут переключаться обратно и вперед при нажатии любой клавиши, пока командное окно не будет закрыто.
    Если вы щелкните правой кнопкой мыши в верхней части окна cmd и выберите 'настройки', затем
    выберите 'размер запуска' и установите столбцы на 120 и строки на 36, и запустите снова, это будет лучше подходить.
    Эта программа скомпилирована без ошибок или предупреждений в Visual Studio 2022 с использованием компилятора 'C Code (/TC)'.
    Чтобы создать эту программу, убедитесь, что этот файл и 'concolor.h' находятся в одной директории и компилируйте его,
    добавляя оба имени файлов в строку ввода компилятора и имя файла (.exe) по вашему выбору для выходной строки, 
    если вы используете компилятор командной строки, такой как GCC.
*/

#include       //включен заголовок C, чтобы функция 'printf' работала
#include      //включен заголовок C, чтобы функция 'system' работала
#include "concolor.h"   //заголовок, созданный для определения многих настроек цветов консоли
                        //обратитесь к этому заголовку (просто список 'defines') для выбора цветовых ключевых слов
                        //не все ключевые слова могут работать в вашей консоли, но большинство из них работает

char s[][4] = { "NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL","BS ","TAB",
                  "LF ","VT ","FF ","CR ","SO ","SI ","DLE","DC1","DC2","DC3",
                  "DC4","NAK","SYN","ETB","CAN","EM ","SUB","ESC","FS ","GS ",
                  "RS ","US " };  //массив символов стандартных имен управляющих символов ASCII

char e[][4] = {"\\0","   ", "   ", "   ", "   ", "   ", "   ", "\\a", "\\b", "\\t", 
                "\\n", "\\v", "\\f", "\\r", "   ", "   ", "   ", "   ", "   ", "   ", 
                "   ", "   ", "   ", "   ", "   ", "   ", "   ", "\\e", "   ", "   ", 
                "   ", "   ", };  //массив символов последовательностей экранирования с другими пробелами для выравнивания

void DataList(int k);   //прототип для функции 'DataList'

int main(void)  //обязательная функция 'main' (программа начнет здесь)
{
    for (;;) {  //бесконечный цикл до закрытия окна cmd

        DataList(0);    //вызов функции с передачей 0 для стандартного набора ASCII

        DataList(128);  //вызов функции с передачей 128 для расширенного набора ASCII        
    }
    return 0;   //включен, потому что 'main' может вернуть целое число командной строке, если это необходимо
}

void DataList(int k) {   //начало функции, которая выполняет всю работу с переданным значением, присвоенным 'k'
                           //'k' хранит десятичное значение каждого символа ASCII

    char i, j;       //'i' и 'j' являются итераторами для строк и столбцов

    system("cls");  //очистка экрана консоли
    printf("\n");   //добавляет отступ сверху окна
    for (j = 0; j < 4; j++) {    //итерация по заголовкам столбцов
        printf(" %s%s  %s   %s   ", IWHT, "Dec", "Hex", "Oct");  //выводит каждый заголовок столбца
        if (!k && !j) printf("%s%s\t", IWHT, "CTRL  ESC");  //выводит это для последнего текста заголовка для столбца 1, страница 1
        else printf("%s%s\t\t", IWHT, "Char");  //выводит это для последнего текста заголовка для всех других столбцов
    }
    printf("\n");   //начинает первую строку данных
    for (i = 32; i > 0; i--) {   //итерация по каждой строке
        for (j = 4; j > 0; j--) {    //итерация по столбцу
            printf(" %s%3d   %s%02X   %s%3o   ", NYEL, k, NGRN, k, NCYN, k);     //данные для каждого столбца с именами управляющих символов и цветами
            if (k < 32) {    //тест для первого столбца               
                printf("%s%s   %s%s\t", NRED, s[k], HNMAG, e[k]);     //это выберет из массива имен управляющих символов
            }                                                           //И из массива последовательностей экранирования на странице 1
            else {  //если не первый столбец, то...  
                if (k == 32) printf("%s%s\t\t", NYEL, "space");     //вывести 'space' для ASCII 32
                else if (k == 127) printf("%s%s\t\t", NRED, "DEL");  //вывести 'DEL' для ASCII 127
                else if (k == 255) printf("%s%s\t\t", NRED, "NBSP");   //вывести 'NBSP' для ASCII 255
                else printf("%s%c\t\t", NWHT, k);      //это выведет фактический символ ASCII
            }
            k += 32;    //добавить 32 к десятичному числу для следующего столбца
        }
        printf("\n");   //начать следующую строку
        k -= 127;   //вычесть (96 + 32) из десятичного числа для следующей строки
    }
    printf(CREST);     //сбросить к цветам консоли по умолчанию
    printf("\n");   //добавить отступ внизу окна
    system("pause");    //ждать, пока не будет нажата клавиша, чтобы переключиться между страницами
}

Большинство примеров, которые я видел, показывают вставку цветового ключевого слова в стороне форматирования выражения printf. Это не было проблемой для компилятора C++ в Visual Studio, но при компиляции с GCC он просто напечатал текст последовательности escape повсюду, без цветовых изменений. Это было исправлено в этих файлах.

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

Ответ или решение

Вопрос о заголовке для цветного вывода в консоли на языке C вполне актуален. Действительно, стандартная библиотека C не предоставляет встроенных функций для управления цветом текста в консоле, и пользователи часто создают свои собственные заголовочные файлы для упрощения работы с ANSI-кодами. Вы уже написали такой заголовок (concolor.h), который определяет макросы для различных цветов и стилей. Однако, как вы заметили, существует возможность сделать код более компактным и структурированным.

Возможное улучшение файла concolor.h

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

Например:

#ifndef CONCOLOR_H
#define CONCOLOR_H

#define COLOR_RESET   "\x1B[0m"

#define ANSI_COLOR(fg, bg, style) "\x1B[" style ";" bg ";" fg "m"

#define COLOR_BLACK   "30"
#define COLOR_RED     "31"
#define COLOR_GREEN   "32"
#define COLOR_YELLOW  "33"
#define COLOR_BLUE    "34"
#define COLOR_MAGENTA "35"
#define COLOR_CYAN    "36"
#define COLOR_WHITE   "37"

#define BOLD   "1"
#define DIM    "2"
#define ITALIC "3"
#define UNDERLINE "4"
#define REVERSE "7"
#define STRIKETHROUGH "9"

// Пример для обычного красного текста
#define RED_TEXT ANSI_COLOR(COLOR_RED, "", "")

// Пример для выделенного зеленого текста с фоном
#define GREEN_BG ANSI_COLOR(COLOR_WHITE, "42", BOLD)

#endif  // CONCOLOR_H

Упрощение программы asciichart.c

Ваша программа может быть значительно упрощена, используя написанный выше заголовок. Например, приведу пример модификации функции DataList с использованием макроса ANSI_COLOR:

void DataList(int k) {
    char i, j;
    system("cls"); 
    printf("\n");

    // Заголовки
    for (j = 0; j < 4; j++) {
        printf(" %s%s  %s   %s   ", RED_TEXT, "Dec", "Hex", "Oct");
        printf(j == 0 && k == 0 ? "%s%s\t" : "%s%s\t\t", RED_TEXT, j == 0 ? "CTRL  ESC" : "Char");
    }
    printf("\n");

    // Данные
    for (i = 32; i > 0; i--) {
        for (j = 4; j > 0; j--) {
            printf(" %s%3d   %s%02X   %s%3o   ", ANSI_COLOR(COLOR_YELLOW, "", ""), k, 
                    ANSI_COLOR(COLOR_GREEN, "", ""), k, 
                    ANSI_COLOR(COLOR_CYAN, "", ""), k);
            // Дальнейшая логика...
        }
        printf("\n");
        k -= 127;  
    }
    printf(COLOR_RESET); 
}

Заключение

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

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

Оцените материал
Добавить комментарий

Капча загружается...