Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2013 mgr
Jerzy Wałaszek
|
We współczesnych językach programowania znaki są podstawowym typem danych. W pamięci komputera znak jest przechowywany w postaci liczby, którą nazywamy kodem znaku (ang. character code). Każdy znak posiada swój własny kod. Aby różne urządzenia systemu komputerowego mogły w ten sam sposób interpretować kody znaków, opracowano kilka standardów kodowania liter. Bardzo rozpowszechniony jest standard ASCII:
ASCII – American Standard Code for Information Interchange – Amerykański Standardowy Kod do Wymiany Informacji.
Znaki są zapamiętywane w postaci 8 bitowych kodów (pierwotnie było to 7 bitów, lecz później standard ASCII został poszerzony na 8 bitów, w których znalazły się różne znaki narodowe). Taki sposób reprezentacji znaków jest dzisiaj bardzo wygodny, ponieważ podstawowa komórka pamięci komputera IBM przechowuje właśnie 8 bitów. Dzięki temu znaki dobrze mieszczą się w pamięci.
Typ ten jest w C++ reprezentowany przez char lub unsigned char. W pierwszym typie kody znaków spoza standardowego zestawu ASCII są ujemne (traktuje się je jako liczbę U2) i przyjmują wartości od -128 do -1. W drugim typie kody znaków spoza standardowego zestawu ASCII mają wartości od 128 do 255.
Przyjrzyjmy się dokładniej kodom ASCII w postaci binarnej:
Podstawowy standard ASCII definiuje kody w zakresie od 0 do 127. Bitowo jest to 7 najmłodszych bitów kodu. Pozostałe kody od 128 do 255 to rozszerzony zestaw ASCII:
0 | x | x | x | x | x | x | x | 1 | x | x | x | x | x | x | x | |
podstawowy kod ASCII |
rozszerzony kod ASCII |
0 | 0 | 0 | x | x | x | x | x |
znaki sterujące |
Np. kod 13 oznacza przejście do następnej linii, kod 7 to dźwięk dzwonka.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_007 //----------------------------------- #include <iostream> using namespace std; int main() { cout << char(7) << endl; return 0; } |
Większość z tych kodów (za wyjątkiem 0,7,8,9,10 i 13) produkuje w konsoli znakowej użyteczne znaki:
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_008 //----------------------------------- #include <iostream> #include <iomanip> using namespace std; int main() { for(int i = 0; i < 32; i++) { cout << "znak(" << setw(2) << i << ") = "; switch(i) { case 0: cout << "NUL"; break; case 7: cout << "BEL"; break; case 8: cout << "BS"; break; case 9: cout << "HT"; break; case 10: cout << "LF"; break; case 13: cout << "CR"; break; default: cout << char(i); break; } cout << endl; } return 0; } |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_009 //----------------------------------- #include <iostream> #include <iomanip> using namespace std; int main() { for(int i = 0; i < 32; i++) { for(int j = 1; j <= 3; j++) cout << "znak(" << setw(3) << i + 32 * j << ") = " << char(i + 32*j) << " "; cout << endl; } return 0; } |
Cyfry mają kody od 48 (0) do 57 (9).
0 | 0 | 1 | 1 | x | x | x | x |
cyfry |
W powyższym kodzie xxxx jest wartością binarną cyfry: 0000 = 0, 0001 = 1, 0010 = 2, ..., 1001 = 9.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_010 //----------------------------------- #include <iostream> using namespace std; int main() { for(int i = 48; i < 58; i++) cout << "znak(" << i << ") = " << char(i) << endl; return 0; } |
Litery duże mają kody od 65 (A) do 89 (Z). Litery małe mają kody od 97 (a) do 122 (z).
0 | 1 | 0 | x | x | x | x | x | 0 | 1 | 1 | x | x | x | x | x | |
litery duże | litery małe |
W powyższych kodach xxxxx oznacza binarny numer litery od 00001 = 1 do 11010 = 26. Zwróć uwagę, że litery małe różnią się od dużych bitem b5. Oznacza to, że np. kod litery 'a' jest większy o 32 od kodu litery 'A'. Aby z kodu dużej litery otrzymać kod małej, wystarczy ustawić na 1 bit b5. Podobnie, aby z kodu małej litery otrzymać kod duże, wystarczy wyzerować bit b5.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_011 //----------------------------------- #include <iostream> #include <iomanip> using namespace std; int main() { for(int i = 1; i < 27; i++) { for(int j = 0; j < 2; j++) cout << "znak(" << setw(3) << i + 64 + 32 * j << ") = " << char(i + 64 + 32 * j) << " "; cout << endl; } return 0; } |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_012 //----------------------------------- #include <iostream> using namespace std; int main() { for(int i = 0; i < 32; i++) { for(int j = 0; j < 4; j++) cout << "znak(" << 128+i+j*32 << ") = " << char(128+i+j*32) << " "; cout << endl; } return 0; } |
W polskich systemach Windows w konsoli znakowej jest zwykle wykorzystywana strona kodowa CP852. Niestety, sam system Windows wykorzystuje standard Windows 1250, w którym polskie literki posiadają inne kody niż ich odpowiedniki w konsoli znakowej.
Ą | Ć | Ę | Ł | Ń | Ó | Ś | Ź | Ż | ą | ć | ę | ł | ń | ó | ś | ź | ż | |
Windows 1250 | 165 | 198 | 202 | 163 | 209 | 211 | 140 | 143 | 175 | 185 | 230 | 234 | 179 | 241 | 243 | 156 | 159 | 191 |
CP852 | 164 | 143 | 168 | 157 | 227 | 224 | 151 | 141 | 189 | 165 | 134 | 169 | 136 | 228 | 162 | 152 | 171 | 190 |
ósemkowo | 244 | 217 | 250 | 235 | 343 | 340 | 227 | 215 | 275 | 245 | 206 | 251 | 210 | 344 | 242 | 230 | 253 | 276 |
Konsekwencją tego faktu jest to, że program napisany w edytorze pracującym w Windows nie będzie poprawnie wyświetlał polskich znaków (tablica znakowa s zawiera kody polskich znaków Windows 1250):
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_013 //----------------------------------- #include <iostream> using namespace std; int main() { unsigned char s[] = "ĄĆĘŁŃÓŚŹŻąćęłńóśźż*"; cout << "Windows 1250" << endl << endl; for(int i = 0; s[i] != '*'; i++) cout << s[i] << " : " << (int) s[i] << endl; return 0; } |
Należy dokonać odpowiedniej konwersji (tablica znakowa t zawiera poprawne kody polskich znaków dla konsoli CP852):
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Kody ASCII // PRG_014 //----------------------------------- #include <iostream> using namespace std; int main() { unsigned char s[] = "ĄĆĘŁŃÓŚŹŻąćęłńóśźż*"; unsigned char t[] = {164,143,168,157,227,224,151,141,189, 165,134,169,136,228,162,152,171,190}; cout << "Windows 1250" << endl << endl; for(int i = 0; s[i] != '*'; i++) cout << t[i] << " : " << (int) s[i] << endl; return 0; } |
Problemy te znikają, jeśli zrezygnujemy z polskich znaków w trybie konsoli lub będziemy tworzyć tylko aplikacje dla GUI w Windows. Należy wspomnieć, że w systemie Linux nie występuje ten problem, ponieważ kodowanie polskich znaków jest jednolite w całym systemie (ale z kolei jest to jeszcze inna strona kodowa – ISO-8859-2, która jest standardem ogólnoświatowym). Jak widać, polscy programiści lekko nie mają.
Teksty możemy przechowywać w tablicach znakowych:
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_015 //----------------------------------- #include <iostream> #include <iomanip> using namespace std; int main() { char s[] = "Pewien tekst"; // inicjujemy tablicę int i; cout << s << endl << endl; // wyświetlamy zawartość tablicy i = 0; // indeks pierwszego znaku w tablicy s do { cout << "s[" << setw(2) << i << "] = \"" << s[i] << "\" kod " << setw(3) << (int) s[i] << endl; } while(s[i++]); // pętlę kończymy po osiągnięciu kodu NUL cout << endl; return 0; } |
Zwróć uwagę, że przy inicjalizacji tablicy:
char s[] = "...";
automatycznie zostaje wstawiony na koniec znak NUL. Tablica przyjmuje taki rozmiar, aby pomieścić wszystkie znaki tekstu wraz ze znakiem NUL.
Tekst można odczytać z konsoli do tablicy znakowej za pomocą strumienia cin. Jednakże należy się przy tym zatroszczyć, aby tablica miała odpowiednio duży rozmiar. Prosty odczyt ze strumienia cin zwraca jeden wyraz, ponieważ spacja jest traktowana jako separator.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_016 //----------------------------------- #include <iostream> using namespace std; int main() { char n[100]; // Odpowiednio duża tablica znakowa cout << "Podaj swoje Imi\251 : "; cin >> n; cout << endl << "Witaj, " << n << endl << "Ja jestem C++" << endl; return 0; } |
Pracując w konsoli, możesz bez problemu wprowadzać polskie znaki. Musisz jedynie pamiętać, iż posiadają one kody CP852, a nie Windows 1250. Będzie to miało znaczenie przy porównywaniu znaków.
Jeśli chcesz odczytać cały wiersz znaków, to musisz skorzystać z funkcji składowej strumienia cin o nazwie getline(). Funkcja ta posiada następujące parametry:
cin.getline(s,n);
lub
cin.getline(s,n,d);
s – tablica znakowa lub wskaźnik do danych char. Tutaj
zostanie umieszczony wiersz znaków.
n – maksymalny rozmiar tablicy s. Określa liczbę
znaków wraz z NUL, które mogą być umieszczone w s.
d – znak końca wiersza. Jeśli będzie pominięty, to znakiem
tym jest '\n'. Znak jest usuwany ze strumienia, lecz nie trafia do s.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_017 //----------------------------------- #include <iostream> using namespace std; int main() { char n[100],a[100],c[100]; cout << "Podaj kolejno:" << endl << endl; cout << "Imi\251 i nazwisko : "; cin.getline(n,100); cout << "Adres zamieszkania : "; cin.getline(a,100); cout << "Kraj urodzenia : "; cin.getline(c,100); cout << endl << "Nazywasz si\251 " << n << "." << endl << "Mieszkasz na ulicy " << a << "." << endl << "Kraj twoich narodzin to " << c << "." << endl; return 0; } |
Problem może pojawić się, jeśli czytasz naprzemiennie z cin oraz z cin.getline(). Sprawdź poniższy program. Odczytuje on najpierw liczbę z cin. Następnie czyta wiersz znaków za pomocą funkcji getline(). I na koniec ponownie czyta liczbę z cin.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_018 //----------------------------------- #include <iostream> using namespace std; int main() { char s[100]; int a,b; cout << "Liczba : "; cin >> a; cout << "Wiersz : "; cin.getline(s,100); cout << "Liczba : "; cin >> b; cout << a << endl << s << endl << b << endl; return 0; } |
Co tutaj się stało? Gdy wprowadziłeś pierwszą liczbę (u mnie 20), to komputer nie zaczekał na wprowadzenie wiersza, lecz od razu przeszedł do odczytu drugiej liczby. Dlaczego? Otóż strumień cin po odczycie pierwszej liczby pozostawił w sobie znak końca wiersza '\n'. Gdy teraz wywołaliśmy funkcję getline(), to natrafiła ona od razu na ten znak i zakończyła odczyt. W efekcie użytkownik nie miał szansy wprowadzić swojego tekstu. Funkcja getline() usunęła ze strumienia znak '\n', a program przeszedł do odczytu drugiej liczby.
Jak rozwiązać ten problem? Musimy użyć funkcji ignore(), która pobiera ze strumienia cin zadaną liczbę znaków, aż do napotkania znaku ograniczającego:
cin.ignore(n,d);
n – liczba znaków do pominięcia.
d – znak ograniczający.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_019 //----------------------------------- #include <iostream> using namespace std; int main() { char s[100]; int a,b; cout << "Liczba : "; cin >> a; // czytamy pierwszą liczbę cin.ignore(1000,'\n'); // ze strumienia usuwamy znak EOL cout << "Wiersz : "; cin.getline(s,100); // czytamy wiersz cout << "Liczba : "; cin >> b; // czytamy drugą liczbę cout << a << endl // pierwsza liczba << s << endl // wiersz << b << endl; // druga liczba return 0; } |
Ze strumienia cin możemy również pobierać pojedyncze znaki oraz ich ciągi za pomocą funkcji get():
kod = cin.get(); // zwraca kod ASCII znaku
cin.get(c); // umieszcza znak w zmiennej znakowej c
cin.get(s,n); // odczytuje n-1 znaków do tablicy znakowej s, dodając na końcu NUL
cin.get(s,n,d); // jak wyżej, lecz do napotkania znaku ograniczającego d
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_020 //----------------------------------- #include <iostream> #include <iomanip> using namespace std; int main() { int code; cout << "Wpisz wiersz : "; while((code = cin.get()) != '\n') cout << (char) code << " = " << setw(3) << code << endl; return 0; } |
char c; // kody rozszerzone ASCII -1...-128 unsigned char c; // kody rozszerzone ASCII 128...256
Tekst jest utworzony z ciągu znaków zakończonych kodem NUL.
Tablica znakowa:
char s[liczba_znaków + 1]; unsigned char s[liczba_znaków + 1;
Inicjalizacja tablicy znakowej tekstem (znak NUL jest automatycznie dołączany na koniec tekstu, a tablica przyjmuje odpowiedni rozmiar):
char s[] = "dowolny tekst"; unsigned char s[] = "dowolny tekst";
Zapis do strumienia cout:
cout << (int) c; // kod znaku cout << (int)(unsigned char) c; // kod znaku rozszerzonego ASCII cout << (char) kod; // znak o danym kodzie cout << s; // zapis tablicy znakowej
Odczyt ze strumienia cin:
cin >> s; // odczyt wyrazu, spacja i /n są znakami rozdzielającymi kod = cin.get(); // kod znaku ze strumienia cin.get(c); // znak ze strumienia cin.get(s,n); // odczyt n znaków ze strumienia do tablicy s cin.get(s,n,d); // odczyt do n znaków do tablicy s, aż do napotkania znaku d cin.getline(s,n); // odczyt do n znaków do tablicy s, aż do napotkania \n cin.getline(s,n,d); // odczyt do n znaków do tablicy s, aż do napotkania znaku d cin.ignore(n,d); // usuwa ze strumienie do n znaków aż do napotkania znaku d, który również usuwa
bool IsDigit(char c); - zwraca true, jeśli znak c jest cyfrą
0...9
bool IsXDigit(char c); - zwraca true, jeśli znak c jest cyfrą
szesnastkową
bool IsAlpha(char c); - zwraca true, jeśli znak c jest literą,
również polską CP852
bool IsLower(char c); - zwraca true, jeśli znak c jest małą
literą, również polską CP852
bool IsUpper(char c); - zwraca true, jeśli znak c jest dużą
literą, również polską CP852
char ToCP852(char c); - zamienia znak w standardzie Windows
1250 na znak w standardzie CP852
char To1250(char c); - zamienia znak w standardzie CP852 na
znak w standardzie Windows 1250
char * StrToLower(char * s); - zamienia w tablicy s litery duże na
małe, w tym również polskie znaki CP852
char * StrToUpper(char * s); - zamienia w tablicy s litery małe na
duże, w tym również polskie znaki CP852
char * StrToCP852(char * s); - zamienia w tablicy s znaki w
standardzie Windows 1250 na CP852
char * StrTo1250(char * s); - zamienia w tablicy s znaki w
standardzie CP852 na Windows 1250
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Rozwiązanie zadań 1 i 2 //----------------------------------- #include <iostream> // Funkcja zwraca true, jeśli c jest cyfrą //---------------------------------------- bool IsDigit(char c) { return (c >= '0') && (c <= '9'); } // Funkcja zwraca true, jeśli c jest cyfrą // szesnastkową: 0-9, a-f, A-F //---------------------------------------- bool IsXDigit(char c) { return ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')); } // Funkcja zwraca true, jeśli c jest literą //----------------------------------------- bool IsAlpha(char c) { if(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) return true; char s[] = "\244\217\250\235\343\340\227\215\275\245\206\251\210\344\242\230\253\276"; char * p = s; while(*p) if(*p++ == c) return true; return false; } // Funkcja zwraca true, jeśli c jest małą literą //---------------------------------------------- bool IsLower(char c) { if((c >= 'a') && (c <= 'z')) return true; char s[] = "\245\206\251\210\344\242\230\253\276"; char * p = s; while(*p) if(*p++ == c) return true; return false; } // Funkcja zwraca true, jeśli c jest dużą literą //---------------------------------------------- bool IsUpper(char c) { if((c >= 'A') && (c <= 'Z')) return true; char s[] = "\244\217\250\235\343\340\227\215\275"; char * p = s; while(*p) if(*p++ == c) return true; return false; } using namespace std; int main() { char s[255]; cout << "Wpisz wiersz : "; cin.getline(s,255); for(char * p = s; *p; p++) { cout << *p << " : "; if(IsDigit(*p)) cout << "cyfra "; if(IsXDigit(*p)) cout << "szesnastkowa "; if(IsAlpha(*p)) cout << "litera "; if(IsLower(*p)) cout << "ma\210a "; if(IsUpper(*p)) cout << "du\276a "; cout << endl; } return 0; } |
int isalnum(int c) |
– | zwraca true, jeśli znak c jest literą lub cyfrą |
int isalpha(int c) |
– | zwraca true, jeśli znak c jest literą. |
int isblank(int c) |
– | zwraca true, jeśli znak c jest znakiem odstępu: spacją, tabulacją, znakiem końca wiersza. |
int iscntrl(int c) |
– | zwraca true, jeśli znak c jest znakiem sterującym o kodzie < 32 lub równym 127. |
int isdigit(int c) |
– | zwraca true, jeśli znak c jest cyfrą od 0 do 9. |
int isgraph(int c) |
– | zwraca true, jeśli znak c jest drukowalny, czyli reprezentuje jakiś kształt, który pojawi się na wydruku. Są to znaki o kodach > 32 bez kodu 127. |
int islower(int c) |
– | zwraca true, jeśli znak c jest małą literą od a do z. |
int isprint(int c) |
– | zwraca true, jeśli znak c jest drukowalny. Są to znaki o kodach > 31 bez kodu 127. |
int ispunct(int c) |
– | zwraca true, jeśli znak c jest znakiem interpunkcyjnym. Są to wszystkie znaki o kodach > 32 bez 127, które nie są literami lub cyframi. |
int isspace(int c) |
– | zwraca true, jeśli znak c jest znakiem odstępu: spacją, tabulacją, znakiem końca wiersza. |
int isupper(int c) |
– | zwraca true, jeśli znak c jest dużą literą od A do Z. |
int isxdigit(int c) |
– | zwraca true, jeśli znak c jest cyfrą szesnastkową: 0...9 lub a...f lub A...F. |
int toupper(int c) |
– | zmienia znak c z małej na dużą literę i zwraca jej kod jako wynik. |
int tolower(int c) |
– | zmienia znak c z dużej na małą literę i zwraca jej kod jako wynik. |
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe