Poniższe porady dotyczą programowania w językach: C / C++. Porady testowane m. in. w programach: Geany oraz Qt Creator w systemie operacyjnym GNU/Linux, powinny działać w innych zintegrowanych środowiskach programowania dla wymienionych wyżej języków programowania.
Spis treści:
Oto tabela priorytetów operatorów w języku C++:
Źródła wiedzy:
http://www.cplusplus.com/doc/tutorial/operators/#precedence,
https://en.cppreference.com/w/cpp/language/operator_precedence,
https://www.learncpp.com/cpp-tutorial/operator-precedence-and-associativity/.
W tym celu można utworzyć własną funkcję:
#include <iostream>
using namespace std;
int utf8_to_unicode(string utf8_code);
string unicode_to_utf8(int unicode);
string unicode_table(int min, int max, string separator, int elements_in_row);
int main()
{
cout << unicode_table(32, 10240, "", 32) << endl;
cout << unicode_to_utf8(36) << '\t';
cout << unicode_to_utf8(162) << '\t';
cout << unicode_to_utf8(8364) << '\t';
cout << unicode_to_utf8(128578) << endl;
cout << unicode_to_utf8(0x24) << '\t';
cout << unicode_to_utf8(0xa2) << '\t';
cout << unicode_to_utf8(0x20ac) << '\t';
cout << unicode_to_utf8(0x1f642) << endl;
cout << utf8_to_unicode("$") << '\t';
cout << utf8_to_unicode("¢") << '\t';
cout << utf8_to_unicode("€") << '\t';
cout << utf8_to_unicode("🙂") << endl;
cout << utf8_to_unicode("\x24") << '\t';
cout << utf8_to_unicode("\xc2\xa2") << '\t';
cout << utf8_to_unicode("\xe2\x82\xac") << '\t';
cout << utf8_to_unicode("\xf0\x9f\x99\x82") << endl;
return 0;
}
int utf8_to_unicode(string utf8_code)
{
unsigned utf8_size = utf8_code.length();
int unicode = 0;
for (unsigned p=0; p<utf8_size; ++p)
{
int bit_count = (p? 6: 8 - utf8_size - (utf8_size == 1? 0: 1)),
shift = (p < utf8_size - 1? (6*(utf8_size - p - 1)): 0);
for (int k=0; k<bit_count; ++k)
unicode += ((utf8_code[p] & (1 << k)) << shift);
}
return unicode;
}
string unicode_to_utf8(int unicode)
{
string s;
if (unicode>=0 and unicode <= 0x7f) // 7F(16) = 127(10)
{
s = static_cast<char>(unicode);
return s;
}
else if (unicode <= 0x7ff) // 7FF(16) = 2047(10)
{
unsigned char c1 = 192, c2 = 128;
for (int k=0; k<11; ++k)
{
if (k < 6) c2 |= (unicode % 64) & (1 << k);
else c1 |= (unicode >> 6) & (1 << (k - 6));
}
s = c1; s += c2;
return s;
}
else if (unicode <= 0xffff) // FFFF(16) = 65535(10)
{
unsigned char c1 = 224, c2 = 128, c3 = 128;
for (int k=0; k<16; ++k)
{
if (k < 6) c3 |= (unicode % 64) & (1 << k);
else if (k < 12) c2 |= (unicode >> 6) & (1 << (k - 6));
else c1 |= (unicode >> 12) & (1 << (k - 12));
}
s = c1; s += c2; s += c3;
return s;
}
else if (unicode <= 0x1fffff) // 1FFFFF(16) = 2097151(10)
{
unsigned char c1 = 240, c2 = 128, c3 = 128, c4 = 128;
for (int k=0; k<21; ++k)
{
if (k < 6) c4 |= (unicode % 64) & (1 << k);
else if (k < 12) c3 |= (unicode >> 6) & (1 << (k - 6));
else if (k < 18) c2 |= (unicode >> 12) & (1 << (k - 12));
else c1 |= (unicode >> 18) & (1 << (k - 18));
}
s = c1; s += c2; s += c3; s += c4;
return s;
}
else if (unicode <= 0x3ffffff) // 3FFFFFF(16) = 67108863(10)
{
; // i tak nie ma jeszcze kodów 5-bajtowych
}
else if (unicode <= 0x7fffffff) // 7FFFFFFF(16) = 2147483647(10)
{
; // i tak nie ma jeszcze kodów 6-bajtowych
}
else ; // błędny kod (< 0 albo > 2147483647)
return "";
}
string unicode_table(int min, int max, string separator, int elements_in_row)
{
string s;
cout << string(min%elements_in_row, ' ');
for (int k=min; k<=max; ++k)
s += unicode_to_utf8(k)
+ ((k + 1) % elements_in_row > 0? separator: "\n");
if ((max - min) % elements_in_row > 0)
s += '\n';
return s;
}
Testowane w programie:
Geany 1.32 (Xubuntu 18.04 LTS - 64-bitowy, g++ 7.4.0).
Źródła wiedzy:
W tym celu najprościej użyć wskaźnika do tablicy wskaźników "typ **" oraz rzutowania typu: "reinterpret_cast<typ **>" oraz "reinterpret_cast<int (*)[b]>". Oto przykład:
#include <iostream>
using namespace std;
void wypisz_tablice_2d(int **t, int a, int b)
{
int (*tab)[b] = reinterpret_cast<int (*)[b]>(t);
for (int p=0; p<a; ++p)
{
for (int q=0; q<b; ++q)
cout << "tab[" << p << "][" << q << "]: "
<< tab[p][q] << " (" << &tab[p][q] << ") ";
cout << endl;
}
}
int main()
{
int a=3, b=2, tab[a][b];
for (int p=0; p<a; ++p)
for (int q=0; q<b; ++q)
cin >> tab[p][q];
wypisz_tablice_2d(reinterpret_cast<int **>(tab), a, b);
return 0;
}
Testowane w programie:
Geany 1.32 (Xubuntu 18.04 LTS - 64-bitowy, g++ 7.4.0).
Źródło wiedzy:
https://forum.pasja-informatyki.pl/400499/tablica-wielowymiarowa-jako-argument-funkcji.
W tym celu najprościej użyć funkcji składowej "rdbuf". Oto przykład:
#include <iostream>
#include <fstream>
using namespace std;
void czytaj_wiersz()
{
string tekst;
cout << "Podaj tekst: ";
getline(cin, tekst);
cout << tekst << endl;
}
int main()
{
ifstream ip("in.txt");
auto cinbuf = cin.rdbuf(ip.rdbuf()); // cin -> "in.txt"
ofstream op("out.txt");
auto coutbuf = cout.rdbuf(op.rdbuf()); // cout -> "out.txt"
czytaj_wiersz(); // wszystko działa wewnątrz innych funkcji
string slowo;
cin >> slowo;
cout << slowo << endl;
// Poniższe instrukcje przywracają standardowe ustawienia
// i automatycznie zamykają powyższe pliki
cin.rdbuf(cinbuf); // cin -> klawiatura
cout.rdbuf(coutbuf); // cout -> ekran
czytaj_wiersz();
cout << "Podaj jedno słowo: ";
cin >> slowo;
cout << slowo << endl;
return 0;
}
Testowane w programie:
Geany 1.32 (Xubuntu 18.04 LTS - 64-bitowy, g++ 7.4.0).
Źródło wiedzy:
https://stackoverflow.com/questions/10150468/how-to-redirect-cin-and-cout-to-files.
Jest na to kilka sposób, ale najprostszym sposobem jest sprawdzenie statystyk pliku (gdy plik nie istnieje, funkcja "stat" zwraca błąd - liczbę różną od 0).
Oto przykładowy program wykorzystujący tą cechę funkcji "stat":
#include <iostream> // cout, endl
#include <sys/stat.h> // stat
using namespace std;
bool file_exists(string file_name)
{
struct stat tmp;
/* Odczytywanie statystyk pliku */
return (stat(file_name.c_str(), &tmp) == 0);
}
int main()
{
cout << boolalpha << file_exists("plik.txt") << endl;
return 0;
}
Testowane w programie:
Geany 1.32 (Xubuntu 18.04 LTS - 64-bitowy, g++ 7.4.0).
Źródło wiedzy:
https://4programmers.net/C/FAQ/Jak_sprawdzi%C4%87,_czy_dany_plik_istnieje.
W programach konsolowych kursor przesuwa się w prawo lub w dół. Aby przesunąć kursor w lewo można użyć kodu:
cout << "\b";
albo
cout << "\r";
Ale gdy chcemy przesunąć kursor w górę trzeba użyć innego rozwiązania - zależnego od systemu operacyjnego. W Linuksie można wykorzystać "ANSI Escape Sequences". Standard ten ma o wiele więcej możliwości opisanych szeroko w źródłach wiedzy. Poniżej kilka przykładów pokazujących potęgę "sekwencji ucieczki ANSI":
#include <iostream>
using namespace std;
int main()
{
cout << "AAAAAAAAAAAA" << endl;
system("sleep 1");
cout << "\e[2J" << endl; // czyści cały ekran
cout << "BBBBBBBBBBBB" << endl;
cout << "\e[1A\e[2C\e[1;4;31;47mczerwony" << endl; // przesuwa kursor w górę o 1 wiersze i ustawia nową czcionkę tekstu
cout << "\e[2A\e[0mCCCCCCCCCCCC" << endl << endl; // przesuwa kursor w górę o 2 wiersze i ustawia nową czcionkę tekstu
cout << "DDDDDDDDDDDD" << endl;
}
Standard ma o wiele większe możliwości. Można za jego pomocą m. in.:
sterować pozycją i widocznością kursora;
zapisywać i przywracać pozycję kursora;
czyścić cały ekran lub jego fragmenty (w tym pojedyncze wiersze i ich fragmenty);
ustawiać kolor czcionki i kolor tła.
Testowane w programie:
Geany 1.32 (Xubuntu 18.04 LTS - 64-bitowy, g++ 7.3.0).
Źródła wiedzy:
https://shiroyasha.svbtle.com/escape-sequences-a-quick-guide-1,
https://stackoverflow.com/questions/3277058/how-to-rollback-lines-from-cout,
Jeśli wystarczy nam czas w sekundach, wystarczy użyć funkcji clock() dostępnej po dodaniu nagłówka #include<ctime>.
Oto przykład:
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
int czas = clock(); // czas rozpoczęcia programu
for (int k=0; k<10000000; ++k) ; // przykładowa zawartość programu
czas = clock() - czas; // czas trwania programu
cout << 1.0*czas/CLOCKS_PER_SEC << " s" << endl; // wyświetlenie czasu trwania programu
return 0;
}
Jeśli potrzebujemy dokładności w milisekundach, należy użyć funkcji std::chrono::steady_clock::now() dostępnej po dodaniu nagłówka #include <chrono>.
Oto przykład:
#include <iostream>
#include <chrono>
int main()
{
auto start = std::chrono::steady_clock::now( );
system("sleep 1");
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
std::cout << "Czas trwania programu: " << elapsed.count() << " ms." << std::endl;
}
Testowane w programie:
Geany 1.32 (Kubuntu GNU/Linux 18.04 LTS - 64-bitowy, g++ 7.3.0, Xubuntu 18.04 LTS - 64-bitowy, g++ 7.3.0).
Źródła wiedzy:
Tekst zapisany za pomocą kodowania UTF-8 może składać się z 1-6 bajtów. Zapis binarny pierwszego bajtu znaku to "0xxxxxxx" lub "11xxxxxx", a zapis binarny kolejnych bajtów znaku to zawsze "10xxxxxx". Wystarczy zliczać tylko bajty, których zapis binarny jest różny od "10xxxxxx" i otrzymamy liczbę znaków w tekście UTF-8.
Aby odszukać i-ty znak w tekście UTF-8, należy pominąć znaki poprzedzające szukany znak w sposób opisany powyżej, a następnie w analogiczny sposób należy odczytać wszystkie bajty szukanego znaku.
#include <iostream>
using namespace std;
// Zwraca ilość znaków w tekście UTF-8
int utf8_length(string s)
{
const char *ws = s.c_str(); int length=0;
// Zlicza tylko te pierwsze bajty każdego znaku
while (*ws) length += ((*ws++) & 0xc0) != 0x80;
return length;
}
// Zwraca z tekstu UTF-8 znak o indeksie "index"
string utf8_at(string s, int index)
{
const char *ws = s.c_str(); string sout = "";
int current=0;
// Przeskakuje przez wszystkie znaki poprzedzające szukany znak
while (*ws && current<index+1) current += ((*ws++) & 0xc0) != 0x80;
--ws;
// Zapisuje wszystkie kolejne bajty szukanego znaku
sout += *ws++;
while (*ws && (*ws & 0xc0) == 0x80) sout += *ws++;
return sout;
}
int main()
{
string s = "Zażółć gęślą jaźń";
// Wyświetla kolejne znaki w tekście UTF-8
for (int k=0; k<utf8_length(s); ++k)
cout << "Litera nr " << k+1 << " to '" << utf8_at(s, k) << "'" << endl;
return 0;
}
Testowane w programie:
Qt Creator 3.5.1 bazujący na Qt 5.5.1 (GCC 5.2.1 20151129, 64 bitowy, Xubuntu GNU/Linux 16.04 LTS),
Code::Blocks 13.12 (Xubuntu GNU/Linux 16.04 LTS - 64-bitowy).
Źródła wiedzy:
Przykładowe rozwiązanie problemu w języku C++:
#include <iostream>
using namespace std;
//! @brief Read anything from a stream.
//!
//! This functions tries to read formatted data from input stream.
//! If data is read successfully (i.e. stream is still good), but
//! next character isn't whitespace, then we treat this as a failure.
//! When reading fails (but not because of EOF), the stream state and
//! buffer are cleared.
//!
//! @param istr Stream to read from.
//! @param result Variable to read to.
//!
//! @return @c true if reading succeeded, @c false otherwise.
template < typename T, typename CharT, typename Traits >
bool read( std::basic_istream < CharT, Traits >& istr, T & result )
{
using namespace std;
istr >> result;
if( istr.good() )
{ // stream has still goodbit set, check whether next character is white.
const ctype < CharT >& ctype_facet = use_facet < ctype < CharT > >( istr.getloc() );
if( ctype_facet.is( ctype < CharT >::space, Traits::to_char_type( istr.peek() ) ) )
{ // it is, so everything is okay.
return true;
}
else
{
string null;
cin >> null;
return false;
}
}
else if( istr.eof() )
{ // we reached end of file, don't clear state nor buffer.
return false;
}
// reading failed or next character isn't white, clear stream state and buffer
istr.clear();
istr.sync();
string null;
cin >> null;
return false;
}
int main()
{
cout << "Podaj liczbę całkowitą i liczbę zmiennoprzecinkową, np. 2 5.2): ";
int i;
bool ok = read(cin, i);
cout << "Pobrano liczbę całkowitą " << i
<< (ok? "": " (podczas pobierania wykryto błąd)") << "." << endl;
double d;
ok = read(cin, d);
cout << "Pobrano liczbę zmiennoprzecinkową " << d
<< (ok? "": " (podczas pobierania wykryto błąd)") << "." << endl;
}
Testowane w programie:
Qt Creator 3.5.1 bazujący na Qt 5.5.1 (GCC 5.2.1 20151129, 64 bitowy, Xubuntu GNU/Linux 16.04 LTS).
Źródło wiedzy: