• 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

0 głosów
117 wizyt
pytanie zadane 20 maja w C i C++ przez Jacek0325 Użytkownik (800 p.)
edycja 28 maja 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 przez j23 Mędrzec (174,640 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 przez Jacek0325 Użytkownik (800 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 przez Oscar Nałogowiec (25,590 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 przez j23 Mędrzec (174,640 p.)
edycja 24 maja 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 przez Jacek0325 Użytkownik (800 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 przez j23 Mędrzec (174,640 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 przez Jacek0325 Użytkownik (800 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 przez j23 Mędrzec (174,640 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 przez Jacek0325 Użytkownik (800 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 przez j23 Mędrzec (174,640 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 6,714 wizyt
pytanie zadane 1 maja 2015 w C i C++ przez ErDek19 Użytkownik (870 p.)
0 głosów
1 odpowiedź 298 wizyt
pytanie zadane 6 lutego 2016 w C# przez Andrzej Początkujący (320 p.)
0 głosów
1 odpowiedź 86 wizyt
pytanie zadane 23 sierpnia 2015 w HTML i CSS przez Lukaspar Użytkownik (900 p.)

88,311 zapytań

136,904 odpowiedzi

305,517 komentarzy

58,593 pasjonatów

Motyw:

Akcja Pajacyk

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

Sklep oferujący ćwiczenia JavaScript, PHP, rozmowy rekrutacyjne dla programistów i inne materiały

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...