Вопрос или проблема
Я не нашел заголовок для основных цветов текста в 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-коды. В большинстве современных терминалов это не является проблемой, но стоит уточнить данное ограничение.
Вместо многочисленных макросов используйте более абстрактные решения, чтобы улучшить читаемость и поддержку вашего кода. Это позволит вашему проекту развиваться, не вызывая излишних трудностей в дальнейшем.