• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Kolor tekstu w konsoli Windows - jakość mojego kodu

VPS Starter Arubacloud
0 głosów
226 wizyt
pytanie zadane 20 maja 2022 w C i C++ przez Jacek0325 Obywatel (1,010 p.)
edycja 28 maja 2022 przez Jacek0325

Witam.

Nie jestem bardzo zaawansowany w C++. Trochę drogą własnego próbowania znalazłem sposób na zmienianie koloru tekstu w konsoli Windows - tak, żeby w głównym kodzie było łatwo widać, na jaki kolor się zmieniło. Powstała klasa, z której tworzę jeden obiekt w głównym kodzie i dwoma metodami mogę albo po prostu zmienić kolor, albo w nieco szybszy sposób wyświetlić podanego stringa w określonym kolorze.

#include <iostream>
#include <windows.h>

using std::cout;

class Color {	
	HANDLE hOut;
	std::string curr_color;
	
	public:
	
	Color() {hOut=GetStdHandle(STD_OUTPUT_HANDLE);}
	
	/* zmień kolor wyświetlania */
	void chcolor(std::string c) {
		int nr=7;
		if(c=="DB") nr=1;		// Ciemny niebieski
		else if(c=="DG") nr=2;	// Ciemny zielony
		else if(c=="DC") nr=3;	// Ciemny błękitny
		else if(c=="DR") nr=4;	// Ciemny czerwony
		else if(c=="DV") nr=5;	// Ciemny fioletowy
		else if(c=="DY") nr=6;	// Ciemny żółty
		else if(c=="S") nr=7;	// Standardowy
		else if(c=="DS") nr=8;	// Ciemny szary
		else if(c=="B") nr=9;	// Niebieski
		else if(c=="G") nr=10;	// Zielony
		else if(c=="C") nr=11;	// Błękitny
		else if(c=="R") nr=12;	// Czerwony
		else if(c=="V") nr=13;	// Fioletowy
		else if(c=="Y") nr=14;	// Żółty
		else if(c=="W") nr=15;	// Biały
		SetConsoleTextAttribute(hOut, nr);
		curr_color=c;
	}
	/* wyświetl dany tekst w określonym kolorze */
	void color(std::string c, std::string txt) {
		std::string pc=curr_color;
		chcolor(c); cout<<txt; chcolor(pc);
	}
};

Pytanie jest takie, czy wg Was można ten problem rozwiązać w lepszy sposób. Pewnie można jakoś usprawnić przypisywanie wartości zmiennej nr w zależności od argumentu c.

EDIT: dodaję przykład wykorzystania tej klasy - co prawda w odpowiedzi pojawiły się już lepsze rozwiązania, ale może ktoś będzie zainteresowany:

#include <iostream>
#include "color.h"

using std::cout;

int main() {
	Color c;
	
	cout<<"Ten tekst ma standardowy kolor, ";
	c.chcolor("B");
	cout<<"a ten jest już niebieski."<<'\n';
	cout<<"W tym niebieskim tekście można też zrobić ";
	c.color("Y", "żółte wyróżnienie");
	cout<<", po którym tekst znów jest niebieski."<<'\n';
	
	c.chcolor("S");
	system("pause");
	return 0;
}

 

1 odpowiedź

+2 głosów
odpowiedź 20 maja 2022 przez j23 Mędrzec (194,920 p.)

Nie wiem, czy o to konkretnie pytasz, ale zrobiłbym to tak:

struct color { 
    color(const char *cl) 
    {
        // tu ustawiasz kolor konsoli
    }
};

std::ostream& operator<< (std::ostream &os, color c)
{
    return os;
}

int main(void)
{
    std::cout << color("R") << "tekst" << '\n';
}

a jeśli kolor nie musi być w formie tekstowej, to tak nawet lepiej:

enum struct color {
    DB = 1,  // Ciemny niebieski
    DG = 2,  // Ciemny zielony
    DC = 3,  // Ciemny błękitny
    DR = 4,  // Ciemny czerwony
    DV = 5,  // Ciemny fioletowy
    DY = 6,  // Ciemny żółty
    // itd.
    
};

std::ostream& operator<< (std::ostream &os, color c)
{
    // tu ustawiasz kolor konsoli
    
    return os;
}


int main(void)
{
    std::cout << color::DY << "tekst" << '\n';
}

 

komentarz 23 maja 2022 przez Jacek0325 Obywatel (1,010 p.)

Dzięki. Wykorzystałem drugą Twoją propozycję i stworzyłem taki przykładowy program:

#include <windows.h>

enum struct color {
	DB = 1,  // Ciemny niebieski
	DG = 2,  // Ciemny zielony
	DC = 3,  // Ciemny błękitny
	DR = 4,  // Ciemny czerwony
	DV = 5,  // Ciemny fioletowy
	DY = 6,  // Ciemny żółty
	S  = 7,  // Standardowy
	DS = 8,  // Ciemny szary
	B  = 9,  // Niebieski
	G  = 10, // Zielony
	C  = 11, // Błękitny
	R  = 12, // Czerwony
	V  = 13, // Fioletowy
	Y  = 14, // Żółty
	W  = 15, // Biały
};
	
HANDLE hOut=GetStdHandle(STD_OUTPUT_HANDLE);
	
std::ostream& operator<< (std::ostream &os, color c) {
	SetConsoleTextAttribute(hOut, (WORD)c);
	return os;
}
#include <iostream>
#include "color.h"

using std::cout;

int main() {
	cout<<"Wyświetlanie kolorowego tekstu "<<color::G<<"działa"<<color::S<<'\n';
	system("pause");
	return 0;
}

Teraz plik color.h mogę sobie podpinać do różnych projektów. Czy wg Was w tym kodzie można by jeszcze coś poprawić?

komentarz 23 maja 2022 przez Oscar Nałogowiec (29,290 p.)
Pobawie się w smerfa marudę.

Metoda z manipulatorem do strumienia jest fajna, ale co będzie jak ten obiekt zastosujemy do ofstream? Może trzeba użyć tego manipulatora na bardziej określonym strumieniu a nie na tak ogólnej klasie jak ostream?
1
komentarz 24 maja 2022 przez j23 Mędrzec (194,920 p.)
edycja 24 maja 2022 przez j23

@Jacek0325, 

Czy wg Was w tym kodzie można by jeszcze coś poprawić?

Linię z GetStdHandle przenieś do operatora <<. Nie ma powodu, by uchwyt hOut był zmienną globalną.

Na początku color.h daj #pragma once

@Oscar,

ale co będzie jak ten obiekt zastosujemy do ofstream?

W sumie nic. Manipulator nie ma wpływy na zawartość tego, co wpychane jest do strumienia. No i trzeba założyć, że programista wie, co robi.

Może trzeba użyć tego manipulatora na bardziej określonym strumieniu

std::cout nie ma jakiegoś określonego typu, by można było napisać specjalnie dla niego manipulator. Trzeba by zrobić nową klasę pochodną od std::ostream i klasę bufora, która wysyłałaby zawartość na standardowe wyjście. No i później używać jej zamiast std::cout

Ciut za dużo zabawy z powodu takiej błahostki.

--- dodane ---

Przypomniało mi się, że przecież można bufor wziąć z std::cout, a to sprawę upraszcza:

namespace cl {

class color_ostream : public std::ostream
{
public:
    color_ostream() : std::ostream(std::cout.rdbuf()) {}
};

color_ostream cout; // to powinno być extern, a zdefiniowane gdzieś w pliku cpp

}



int main(void)
{
    cl::cout << "Hello world\n";
}

 

komentarz 24 maja 2022 przez Jacek0325 Obywatel (1,010 p.)

@j23,

Linię z GetStdHandle przenieś do operatora <<. Nie ma powodu, by uchwyt hOut był zmienną globalną.

Pomyślałem, że wówczas wykonanie tej funkcji i przypisanie wyniku do hOut będzie mi się powtarzać za każdym razem, kiedy użyję <<, a to wydaje się niepotrzebne. Słyszałem też, że czasem używanie zmiennych globalnych nie jest zalecane, więc może można to zrobić w jeszcze inny sposób?

1
komentarz 24 maja 2022 przez j23 Mędrzec (194,920 p.)

a to wydaje się niepotrzebne.

Nie wydaje mi się, by był to problem, zważywszy że funkcja ta nie tworzy nowego obiektu systemowego, a jedynie zwraca uchwyt strumienia, który został przypisany podczas inicjacji procesu. Druga sprawa, że sposób, w jaki zdefiniowałeś tą zmienną, jest nieprawidłowy - może powodować błędy kompilacji (wielokrotna definicja tej samej zmiennej).

może można to zrobić w jeszcze inny sposób?

Najprościej: zrobić uchwyt wewnątrz operatora statycznym. Minus tego rozwiązania jest taki, że jak zmienisz uchwyt standardowego wyjścia po pierwszym użyciu operatora, to operator tego nie zauważy i będzie operować na starym uchwycie.

Inny sposób to użycie przykładowej klasy color_ostream, z którą zaprzyjaźniasz operator wyjścia. W klasie tej robisz pole z uchwytem, które ustawiasz w konstruktorze. Plus tego rozwiązania jest taki, że możesz zrobić metodę, która umożliwi  zmianę uchwytu.

komentarz 24 maja 2022 przez Jacek0325 Obywatel (1,010 p.)

Ok. Rzeczywiście przeniosę tą linię do operatora, choć pomyślałem, że przy większych programach, Twoje propozycje na te inne sposoby mogłyby zwiększyć jego wydajność. Ale ja na razie takich dużych nie piszę smiley Może zresztą kiedyś sprawię sobie przyjemność - poprawię tylko coś w color.h i już mam szybszy program laugh

1
komentarz 24 maja 2022 przez j23 Mędrzec (194,920 p.)
Raczej nie. W tym kodzie nie ma nic, co mogłoby jakoś zauważalnie przyspieszyć program. Takie mikro optymalizacje są bez sensu w miejscach, gdzie nie ma jakiegoś szczególnego reżimu czasowego (w rozumieniu walki o każdą mikro/milisekundę).
komentarz 25 maja 2022 przez Jacek0325 Obywatel (1,010 p.)
W sumie racja. Szczególnie, jeśli optymalizacja nie idzie w parze z prostotą kodu. Potem będzie to czytał inny programista, zobaczy taką dodatkową klasę i będzie musiał nieco główkować, jeśli nie widział takiego rozwiązania ileś tam razy wcześniej.
komentarz 25 maja 2022 przez j23 Mędrzec (194,920 p.)
Akurat ta klasa niczego nie optymalizuje, a jedynie ogranicza działanie modyfikatora do jednego konkretnego przypadku użycia. Ma raczej rolę porządkującą kod.

Podobne pytania

0 głosów
5 odpowiedzi 8,137 wizyt
pytanie zadane 1 maja 2015 w C i C++ przez ErDek19 Użytkownik (870 p.)
0 głosów
1 odpowiedź 387 wizyt
pytanie zadane 6 lutego 2016 w C# przez Andrzej Początkujący (320 p.)
0 głosów
1 odpowiedź 126 wizyt
pytanie zadane 23 sierpnia 2015 w HTML i CSS przez Lukaspar Użytkownik (890 p.)

92,453 zapytań

141,262 odpowiedzi

319,088 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...