Łańcuchy Znaków

  Dział ten jest poświęcony poradom dotyczącym łańcuchów znakowych, zmiennym typu String, int, float i double. Znajdują się tutej informacje i przykłady dotyczące konwersji znaków na liczby i odwrotnie, oraz sposoby ich używania.

Menu
 Korzystanie z funkcji zamieszczonych w tym dziale wymaga dodania do programu biblioteki #include <stdio.h>

 


Konwersja zmiennej char do String.

  Konwersja char do String może być przeprowadzona przez zwykłe przypisanie:

char tekst[20];
sprintf(tekst, "%s", "Tekst przykładowy");
String abc = tekst; // przypisanie zmiennej char do zmiennej String

...można też tak:

char tekst[20];
sprintf(tekst, "%s", "Tekst przykładowy");
String abc = (String)tekst;

...powrót do menu.

Konwersja zmiennej String do char.

  Konwersji zmiennej String do char nie można przeprowadzić za pomocą przypisania, ponieważ to nie zadziała i kompilator zwróci błąd. Konwersję należy przeprowadzić za pomocą funkcji 'c_str' będącą właściwością obiektu AnsiString:

String abc = "Przykładowy tekst";
char tekst[20];
sprintf(tekst, "%s", abc.c_str());

...i nie ma innego sposobu.

...powrót do menu.

Konwersja zmiennej String do int.

  Konwersję można przeprowadzić za pomocą funkcji 'StrToInt':

String abc = "100";
int x = StrToInt(abc);

Konwersja zostanie przeprowadzona tylko wtedy, gdy łańcuch String zawiera tylko liczbę, w przeciwnym razie kompilator zwróci komunikat, np.: "'100A' is not a valid integer value".
Można wykorzystać również funkcję 'ToInt':

String abc = "100";
int x = abc.ToInt();

...powrót do menu.

Konwersja zmiennej int do String.

  Konwersję liczby całkowitej na tekst można przeprowadzić za pomocą funkcji 'IntToStr':

int x = 100;
String abc = IntToStr(x);

...można również zastosować zwykłe przypisanie, ale to nie jest dobry pomysł, ponieważ w pewnych sytuacjach mogą wystąpić błędy:

int x = 100;
String abc = x; //zmiennej String przypisano wartość zmiennej int.

...powrót do menu.

Konwersja zmiennej String do float i double.

  Konwersja do zmiennych float i double odbywa się w ten sam sposób. Można tutej wykorzystać funkcję 'StrToFloat':

String abc = "1000,3";
float x = StrToFloat(abc);
double y = StrToFloat(abc);

Ważna uwaga! Zmienna String musi jako tekst zawierać tylko liczby, dopuszczalny jest tylko przecinek rozdzielający liczbę całkowitą od części ułamkowej (nie można stosować kropki).
Można również zastosować funkcję 'ToDouble':

String abc = "1000,3";
float x = abc.ToDouble();
double y = abc.ToDouble();

...powrót do menu.

Konwersja zmiennej float lub double do String.

  Konwersji zmiennych float lub double na tekst można dokonać za pomocą metody 'FloatToStr':

float x = 1000.3; // do rozdzielenia części całkowitej od ułamkowej należy użyć kropki.
double y = 1000.3;
String abc = FloatToStr(x);
String bcd = FloatToStr(y);

Istnieje również bardziej wyspecjalizowana funkcja 'FloatToStrF' umożliwiająca formatowanie konwertowanej liczby, np.:

float x = 1000.3;
String abc = FloatToStrF(x, ffNumber, 7, 2);

 

W podanym przykładzie liczba konwertowana (1000.3) zostanie dodatkowo sformatowana i w efekcie uzyskamy liczbę z separatorem tysiąca. Funkcja 'FloatToStrF' pobiera cztery parametry: liczbę do sformatowania (w przykładzie: x), pożądany typ formatowania (w przykładzie: ffNumber), dokładność (w przykładzie: 7), i liczbę miejsc dziesiętnych (w przykładzie: 2).
  Na szczególną uwagę zasługuje tutej fakt, że funkcja 'FloatToStrF' sforamtuje liczbę używającą przecinków (typ formatu ffNumber), lub wyrażającą pieniądze (typ foramtu ffCurrency), i zrobi to używając separatorów i symboli waluty lokalnej zdefiniowanej w Windows.
Dostępne typy formatów: ffGeneral, ffNumber, ffExponent, ffFixed, ffCurrency.
Ważna uwaga! Jeżeli sformatujemy liczbę używając typu ffNumber w efekcie uzyskamy liczbę z separatorem tysiąca, czyli przerwę miedzy cyframi np.: 1 000.3 (ta przerwa to nie jest spacja). Ponowne przekonwertowanie tak sformatowanej liczby z tekstu na liczbę za pomocą funkcji 'StrToFloat' nie będzie możliwe, ponieważ tekst zawiera niedozwolony znak (separator tysiąca) i nie ma funkcji, która umożliwiałaby taką konwersję więc, żeby dokonać konwersji należy usunąć z tekstu separator tysiąca. W tym celu należy posłużyć się funkcją API 'StringReplace', która zamienia wystąpienie określonego znaku w tekście na inny znak:


float x = 1000000.3;
String abc = FloatToStrF(x, ffNumber, 7, 2);

//W efekcie uzyskamy wartość String: 1 000 000,30.

String temp = StringReplace(abc, " ", "", TReplaceFlags() << rfReplaceAll)
float y = StrToFloat(temp);

W podany przykładzie funkcja 'StringReplace' usunęła z tekstu abc wszystkie wystąpienia separatora tysiąca (" ").

...powrót do menu.

Sprawdzanie długości tekstu.

  Do sprawdzania długości tekstu służy metoda 'Length', która zwraca wartość int:

String abc = "Przykładowy tekst";
int x = abc.Length();

W efekcie zmienna x przyjmie wartość 17 ponieważ tyle znaków przechowuje zmienna abc.

...powrót do menu.

Formatowanie tekstu i liczb

  Do jednoczesnego formatowania tekstu i liczb można się posłużyć funkcją 'Format':

float x = 1000.0;
String abc = Format("Foramtowana liczba to: %.2f", ARRAYOFCONST((x)));

Istnieje również metoda 'FormatCurr', która działa podobnie do format lecz oferuje znacznie prostszą metodę:

float x = 1000.3;
String abc = FormatCurr("Formatowana liczba to 0#.00#", x);

Uzyskamy ten sam efekt co wyżej, lecz w znacznie prostszy sposób, ponieważ do określania dokładności i liczby miejsc po przecinku używamy zer po których następuje znak #.
Istnieje również funkcja 'FormatString', która służy do formatowania daty i czasu na tekst. Możliwe jest określenie własnych separatorów:

//określenie aktualnego czasu systemowego.
TDateTime now = TDateTime::CurrentTime();
String abc = now.FormatString("hh:nn:ss");

  W podanym przykładzie aktualny czas zostanie sformatowany i przypisany zmiennej abc. Litery hh oznaczają godzinę, użycie dwóch liter oznacza że godziny jednocyfrowe np. godz. 8 będą przedstawiane za pomocą dwóch cyfr poprzez dopisanie zera na początku (godz. 08), żeby przedstawić godzinę za pomocą jednej cyfry wystarczy wpisać jedną literę h a godziny dwucyfrowe zostaną i tak właściwie pokazane za pomocą dwóch cyfr. Litery nn oznaczają minuty, natomiast ss - sekundy.
Formatowanie daty odbywa się podobnie:

//określenie aktualnej daty systemowej.
TDateTime today = TDateTime::CurrentDate();
String abc = today.FormatString("dd.mm.yy");

W podanym przykładzie aktualna data zostanie sformatowana i przypisana zmiennej abc. Litery dd oznaczają dzień, jedna litera reprezentuje dzień jednocyfrowy za pomocą jednej cyfry, dwie litery reprezentują dzień jednocyfrowy za pomocą dwóch cyfr poprzez dopisanie zera na początku, trzy litery reprezentuję dzień za pośrednictwem skrótu nazwy np.: poniedziałek zostanie przdstawiony jako: Pn. Cztery litery przedstawiają dzień jako całą nazwę. Litery mm reprezentują miesiąc, stosowanie ich ilości jest takie samo jak w przypadku liter reprezentujących dzień. Litery yy reprezentują rok, można stosować je w ilości od 1 do 4, ale w efekcie można uzyskać reprezentację roku w całości (trzy lub cztery litery), lub w formie dwóch ostanich cyfr określających rok (jedna lub dwie litery).

...powrót do menu.

Usuwanie tekstu z łańcucha znaków.

  Do usuwania tekstu z tekstu (łańcucha znaków) służy funkcja 'Delete'. Załóżmy, że chcemy z tekstu "Przykładowy tekst do usunięcia" usunąć wyraz "Przykładowy":

String abc = "Przykładowy tekst do usunięcia";
abc.Delete(1, 11);

W podanym przykładzie ze zmiennej abc zostaje usunięty człon "Przykładowy" i zostaje tylko tekst " tekst do usunięcia". Funkcja Delete pobiera dwa parametry, pierwszy określa miejsce od którego będzie usuwany tekst (w przykładzie 1, odliczanie zaczyna się od jednego a nie od zera), drugi parametr określa długość usuwanego tekstu (w przykładzie 11).

...powrót do menu.

Wyszukiwanie liter w łańcuchu znaków.

  Do wyszukiwania liter, fraz lub całych wyrazów w tekście służy funkcja 'LastDelimiter', która zwraca jako parametr wartość int. Załóżmy, że chcemy w tekście "Przykładowy tekst" określić pozycję litery ł:

String abc = "Przykładowy tekst";
int x = abc.LastDelimiter("ł");

W wyniku tej operacji zmienna x przyjmie wartość 6 ponieważ litera ł zajmuje szóste miejsce w tekście (liczenie zaczyna się od 1). Funkcja 'LastDelimiter' rozróżnia wielkość liter.
Znając miejsce określonej litery w tekście możemy ją usunąć posługując się funkcją 'Delete'. Z tekstu "Przykładowy tekst" usuniemy wyraz "tekst" po tym jak określimy jego położenie w łańcuchu.

String abc = "Przykładowy tekst";
String temp = "tekst";
int x = abc.LastDelimiter(temp);
int y = temp.Length();
abc.Delete(x - y + 1, y + 1);

...powrót do menu.

Wstawianie tekstu do łańcucha znaków.

  Do wstawiania tekstu do tekstu służy funkcja 'Insert', która pobiera dwa parametry. Pierwszy parametr określa tekst który zostanie wstawiony do innego tekstu (w przykładzie: " do wstawienia"), drugi parametr określa pozycję w której zostanie wstawiony tekst (w przykładzie: 18):

String abc = "Przykładowy tekst";
abc.Insert(" do wstawienia", 18);

W efekcie zmienna abc przyjmie wartość "Przykładowy tekst do wstawienia".

...powrót do menu.

Usuwanie pustych znaków - spacji.

  Do usuwania pustych znaków występujących na początku lub na końcu tekstu służą funkcje: 'Trim' - usuwa spacje po obydwu stronach tekstu, 'TrimLeft' - usuwa spację z lewej strony tekstu, 'TrimRight' - usuwa spację z prawej strony tekstu.

String abc = " Przykładowy tekst ";
abc.Trim();
abc.TrimLeft();
abc.TrimRight();

Wymienione funkcje nie usuwają spacji wewnątrz tekstu, i nie istnieje funkcja która by to robiła.

...powrót do menu.

Zamiana wszystkich liter w tekście na małe lub wielkie.

  Do zamiany wszystkich liter w tekście na litery wielki służy funkcja 'UpperCase', natomiast do zamiany na litery małe służy funkcja 'LowerCase'.

String abc = "tekst MIESZANY";
String bcd = abc.UpperCase();
String cde = abc.LowerCase();

...powrót do menu.

ZAMIANA LICZB NA LITERY I ODWROTNIE.

    W systemie kodowania ASCII każda litera ma przypisaną wartość liczbową, istnieje możliwość zmiany liczb na litery i odwrotnie. Umieszczamy na formularzu dwa obiekty Edit1 i Edit2 oraz obiekty Button1 i Button2. Do obiektu Edit1 będziemy wprowadzać jakąś wartość, a w Edit2 pojawi się wynik, w zależności od tego czy klikniemy na przycisku Button1 czy Button2, będziemy zamieniać liczbę na literę lub literę na liczbę. Do zamiany liczby na literę służy funkcja CHAR, a do zamiany litery na liczbę funkcja CODE, jednak pojawia się tutaj jakiś błąd ponieważ zgodnie z pomocą BCB funkcja CODE powinna działać, a tymczasem kompilator wogóle jej nie rozpoznaje, funkcja CHAR działa prawidłowo, ponieważ CODE nie działa stworzyłem własną funkcję, którą nazwałem CONVERT, wykorzystuje ona mechanizm funkcji CHAR do konwertowania litery na liczbę.
Przykład pierwszy konwertujemy liczbę na literę za pomocą funkcji CHAR:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 try
 {
  Edit2->Text = CHAR(Edit1->Text.ToInt());
 }
 catch(...){;}
}
//---------------------------------

Przykład drugi konwertujemy literę na liczbę za pomocą funkcji CODE, być może u kogoś zadziała:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 try
 {
  Edit2->Text = IntToStr(CODE(Edit1->Text));
 }
 catch(...){;}
}
//---------------------------------

Przykład trzeci konwertujemy literę na liczbę za pomocą funkcji CONVERT:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
int CONVERT(String litera)
{
  for(int i = -113; i <= 255; i++)
  {
   if(litera == CHAR(i))
   return i;
  }
  return 0;
}
//--------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
 try
 {
  Edit2->Text = IntToStr(CONVERT(Edit1->Text));
 }
 catch(...){;}
}
//---------------------------------

Wykorzystując pętlę 'for' można by zmieniać całe wyrazy i zdania, jednak o ile zamiana - w wyrazie - z litery na liczbę przebiegnie bezproblemowo o tyle konwersja liczby na literę może stanowić problem ponieważ występują liczby zarówno dwu i trzycyfrowe i pobieranie wewnątrz pętli stanowiło by problem, trzeba by zamienić wszystkie liczby dwucyfrowe na trzycyfrowe.
W kolejnym przykładzie pokażę jak zakodować dowolny wyraz lub zdanie za pomocą funkcji CONVERT i CHAR, poprzez przesunięcie liter o zadaną wartość, w przykładzie wyraz wpisany do obiektu Edit1 zostanie zaszyfrowany poprzez przesunięcie liter o trzy i w obiekcie Edit2 ukaże się zupełnie inny wyraz:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
int CONVERT(String litera)
{
  for(int i = -113; i <= 255; i++)
  {
   if(litera == CHAR(i))
   return i;
  }
  return 0;
}
//--------------------------------

void __fastcall TForm1::Button3Click(TObject *Sender)
{
  String wyraz = Edit1->Text;  // można wprowadzić tutaj całe zdanie
  String zakodowany;

  for(int i = 1; i <= wyraz.Length(); i++)
  {
   String tmp = wyraz.SubString(i, 1);
   int x = CONVERT(tmp) + 3;
   zakodowany = zakodowany + CHAR(x);
  }
  Edit2->Text = zakodowany;
}
//---------------------------------

...powrót do menu. 

Porównywanie łańcuchów znaków.

Do porównywania dwóch łańcuchów znaków służy funkcja SameText, porównuje ona dwie zmienne AnsiString i jeśli są takie same zwraca true w przeciwnym razie zwraca false;

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 String Password1 = Edit1->Text;
 String Password2 = Edit2->Text;
 Boolean Result = SameText(Password1, Password2);

 if(!Result)
 {
  Panel1->Caption = "Nieprawidłowe hasło";
  Edit1->SetFocus();
 }
 else
 {
  Panel1->Caption = "Hasło prawidłowe";
  Close();
 }
}
//---------------------------------

Inną funkcją porównującą dwie zmienne AnsiString jest AnsiCompareIC, działa podobnie do funkcji SameText:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 AnsiString String1 = Edit1->Text;
 AnsiString String2 = Edit2->Text;

 if(String1.AnsiCompareIC(String2) < 0)
  Edit3->Text = "Obydwa wyrazy mają tą samą długość";
 else
  if(String1.AnsiCompareIC(String2) > 0)
   Edit3->Text = "Obydwa wyrazy różnią się";
  else
   Edit3->Text = "Obydwa wyrazy są takie same";
}
//---------------------------------

Funkcje nie rozróżniają wielkości liter.
Kolejną funkcją porównującą łańcuchy znaków jest AnsiCompare, ta funkcja rozróżnia wielkość liter:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 AnsiString String1 = Edit1->Text;
 AnsiString String2 = Edit2->Text;

 if(String1.AnsiCompare(String2) < 0)
  Edit3->Text = "Obydwa wyrazy mają tą samą długość";
 else
  if(String1.AnsiCompare(String2) > 0)
   Edit3->Text = "Obydwa wyrazy różnią się";
  else
   Edit3->Text = "Obydwa wyrazy są takie same";
}
//---------------------------------

W przypadku funkcji AnsiCompareIC i AnsiCompare różnice między zmiennymi zostaną wykazane dopiero gdy będą miały one różną długość.
Następną funkcją porównującą łańcuchy znaków jest AnsiSameStr, ta funkcja rozróżnia wielkość liter:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 AnsiString String1 = Edit1->Text;
 AnsiString String2 = Edit2->Text;

 if(AnsiSameStr(String1, String2))
  Edit3->Text = "obydwa wyrazy są takie same";
 else
  Edit3->Text = "wyrazy różnią się";
}
//---------------------------------

...powrót do menu. 

Pobieranie ostatniego znaku z łańcucha znaków.

W celu pobrania ostatniego znaku z łańcucha znaków, należy posłużyć się funkcją AnsiLastChar:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 String S = Edit1->Text;
 Edit3->Text = S.AnsiLastChar();
}
//---------------------------------

...powrót do menu. 

Umieszczanie łańcucha znaków w cudzysłowie.

Łańcuch znaków można umieścić w cudzysłowie (zacytować) za pomocą funkcji AnsiQuotedStr:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 String Tekst = Edit1->Text;
 char Quote = '"';
 Edit3->Text = AnsiQuotedStr(Tekst, Quote);
}
//---------------------------------

Oczywiście można zastosować inny znak niż cudzysłów, niezależnie od tego jaki to będzie znak zostanie on umieszczony na początku i na końcu zdania.
Inną podobną funkcją jest QuotedStr ta funkcja umieszcza zdanie lub wyraz między apostrofami:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 String Tekst = Edit1->Text;
 Edit3->Text = QuotedStr(Tekst);
}
//---------------------------------

Do usuwania cudzysłowów lub znaków zdefiniowanych zamiast cudzysłowów służy funkcja AnsiExtractQuotedStr:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 String Tekst = Edit1->Text;
 char Quote = '"';
 Edit3->Text = AnsiExtractQuotedStr(Tekst.c_str(), Quote);
}
//---------------------------------

Łańcuch znaków musi jednak zawierać cudzysłowy lub inne zdefiniowane znaki, w przeciwnym razie zostanie usunięty cały wyraz lub zdanie.

...powrót do menu. 

Konwersja zmiennej int do hex.

    Do konwersji wartości liczbowej na wartość hexadecymalną służy funkcja: IntToHex. funkcja pobiera dwa argumenty, pierwszy to wartość liczbowa którą chcemy przekonwertować na hex, drugi parametr to minimalna długość hex'a. Jeżeli wartość drugiego parametru będzie wyższa od długości hex'a, to hex zostanie uzupełniony o 0 na początku, czyli np. mamy taki hex F2D0EA, zawiera on 6 znaków, jeśli jednak jako jego długość podamy np. wartość 8 to hex zostanie przedstawiony w taki sposób 00F2D0EA. Oto przykład wywołania:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Edit1->Text = IntToHex(15913194, 6);
}
//---------------------------------

Oto jeszcze jeden przykład konwersji koloru z RGB do postaci hexadecymalnej:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift,
        int X, int Y)
{
 TColor kolor = Image1->Canvas->Pixels[X][Y];
 byte b = GetBValue(kolor);
 byte g = GetGValue(kolor);
 byte r = GetRValue(kolor);
 Label1->Caption = "0x00" + IntToHex(r, 2) + IntToHex(g, 2) + IntToHex(b, 2);

 Image2->Canvas->Brush->Color = Image1->Canvas->Pixels[X][Y];
 Image2->Canvas->FillRect(Rect(0, 0, Image2->Width, Image2->Height));
}
//---------------------------------

Proszę zwrócić uwagę, że na kolor składają się trzy hex'y pogrupowane po dwa znaki.
Jeśli chodzi o konwersję w drugą stronę, to nie ma na to gotowej funkcji więc trzeba ją sobie dopiero stworzyć, ale o tym w następnej poradzie.

...powrót do menu. 

Konwersja wartości hex do int.

Do przekonwertowania wartości hexadecymalnej na liczbę nie ma gotowej funkcji, trzeba taką funkcję stworzyć. Funkcja korzysta z biblioteki tchar dlatego należy ją włączyć #include <tchar.h>:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
#include <tchar.h>
int httoi(const TCHAR *value)
{
 struct CHexMap
 {
  TCHAR chr;
  int value;
 };
 const int HexMapL = 16;
 CHexMap HexMap[HexMapL] =
 {
  {'0', 0}, {'1', 1},
  {'2', 2}, {'3', 3},
  {'4', 4}, {'5', 5},
  {'6', 6}, {'7', 7},
  {'8', 8}, {'9', 9},
  {'A', 10}, {'B', 11},
  {'C', 12}, {'D', 13},
  {'E', 14}, {'F', 15}
 };
 TCHAR *mstr = _tcsupr(_tcsdup(value));
 TCHAR *s = mstr;
 int result = 0;
 if(*s == '0' && *(s + 1) == 'X') s += 2;
 bool firsttime = true;
 while (*s != '\0')
 {
  bool found = false;
  for(int i = 0; i < HexMapL; i++)
  {
   if(*s == HexMap[i].chr)
   {
    if(!firsttime) result <<= 4;
    result |= HexMap[i].value;
    found = true;
    break;
   }
  }
  if(!found) break;
  s++;
  firsttime = false;
 }
 free(mstr);
 return result;
}
//---------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Edit1->Text = (AnsiString)httoi(Edit2->Text.c_str());
}
//---------------------------------

Na zakończenie przykład konwersji koloru z postaci hexadecymalnej do postaci RGB:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
#include <stdio.h>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 int l = Edit2->Text.Length();
 char myHex[3][3];
 sprintf(myHex[0], "%s", (Edit2->Text.SubString(l - 1, 2)).c_str());
 sprintf(myHex[1], "%s", (Edit2->Text.SubString(l - 3, 2)).c_str());
 sprintf(myHex[2], "%s", (Edit2->Text.SubString(l - 5, 2)).c_str());
 Edit1->Text = "R-" + (AnsiString)httoi(myHex[0])+
               " G-" + (AnsiString)httoi(myHex[1]) +
               " B-" + (AnsiString)httoi(myHex[2]);
}
//---------------------------------

...powrót do menu. 

Jak używać funkcji Format?

Jednym z prostszych sposobów formatowania w jednym zdaniu tekstu i liczb jest użycie funkcji Format. Przykład użycia tej funkcji został pokazany w poradzie formatowanie tekstu i liczb, tutaj jednak pokażę bardziej uniwersalne zastosowanie tej funkcji pozwalające na wstawianie kilku liczb w różnych miejscach łańcucha znaków:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 int x = 5;
 int y = 25;
 TVarRec vr[] = {x, y};
 Label1->Caption = Format("Wybierz liczbę pomiędzy %d i %d.", vr, 2);
}
//---------------------------------

Jak widać o tym co zostaje wstawione do łańcucha znaków decyduje klasa TVarRec a właściwie to co ona zawiera, w przykładzie są to liczby 5 i 25, kolejność wstawiania tych liczb do łańcucha jest zdefiniowana właśnie w tej klasie, czyli w przykładzie mamy w nawiasach umieszczoną najpierw zmienną x a potem y i w takiej kolejności będą one umieszczane w łańcuchu. TVarRec może zawierać  zmienne różnych typów. Funkcja format jako pierwszy argument pobiera łańcuch znaków pomiędzy wyrazy zostały wplecione specyfikatory formatów, w przykładzie jest to %d, oczywiście należy używać specyfikatora odpowiedniego do typu użytej zmiennej. Opis specyfikatorów formatu można znaleźć w poradzie typy znakowe - char.  Drugi argument funkcji Format to tablica typu TVarRec a trzeci argument to liczba elementów tej tablicy.

...powrót do menu. 

Tworzenie własnej maski ograniczającej wpisywanie wybranych znaków np. liczb do obiektów typu TEdit, TMemo itp.

W tej poradzie pokażę jak można stworzyć własną maskę ograniczającą wpisywanie wybranych znaków. Najprościej będzie to wytłumaczyć na przykładzie. Załóżmy, że mamy obiekt Edit1 i chcemy żeby użytkownik programu mógł wpisywać do niego tylko litery bez liczb, w takiej sytuacji można by się oczywiście posłużyć obiektem TMaskEdit, ale szybko da się zauważyć, iż można co prawda ustawić maskę ograniczającą wpisywanie tylko liter jednak użytkownik będzie musiał zawsze wpisywać tyle liter ile przewiduje maska np. ustawimy w masce wpisywanie 10 liter to konieczne będzie wpisanie do maski dokładnie tylu liter nie mniej i nie więcej. Co jednak jeżeli tworzymy np. formularz i chcemy stworzyć pole w którym użytkownik ma podać swoje nazwisko, wiadomo, że nazwiska z reguły nie zawierają cyfr i mają różną długość, w takiej sytuacji maska się nie sprawdzi, więc trzeba w obiekcie Edit wyłączyć wpisywanie cyfr. W tym celu stworzę prostą funkcję, która będzie usuwała cyfry z obiektu Edit1 jeżeli zostaną tam wpisane. Wywołanie funkcji należy umieścić w zdarzeniu OnKeyUp dla obiektu Edit1:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void NoDigit(TEdit *Edit)
{
 AnsiString tab[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
 String temp = Edit->Text;
 for(int i = 0; i < 10; i++)
  temp = StringReplace(temp, tab[i], "", TReplaceFlags() << rfReplaceAll);

 Edit->Text = temp;
 Edit->Perform(EM_SETSEL, temp.Length(), temp.Length());
}
//--------------------------------
void __fastcall TForm1::Edit1KeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
 NoDigit(Edit1);
}

//---------------------------------

 

Funkcja NoDigit pobiera jako argument odwołanie do obiektu typu TEdit, który ma kontrolować, można tam oczywiście wstawić inny typ np. TMemo. Wewnątrz funkcji została zdefiniowana tablica znaków typu AnsiString i została ona wypełniona znakami, które obiekt Edit ma odrzucać, czyli znaki znajdujące się w tej tablicy będą automatycznie kasowane. Można tam zdefiniować dowolne znaki litery i liczby. Usuwanie znaków odbywa się wewnątrz pętli i co ważne ilość obiegów pętli musi być równa ilości elementów znajdujących się w tablicy, w przykładzie tablica zawiera 10 elementów i pętla wykonuje 10 obiegów. Usuwanie znaków odbywa się poprzez zastąpienie niedozwolonego znaku znakiem pustym (""). Na biało została zaznaczona funkcja Perform, bez niej kod również będzie działał, jednak po każdorazowym usunięciu znaku wstawka korektora (kursor piszący - carret) będzie przesuwał się na początek znaku, dlatego funkcja Perform została tak ustawiona, żeby przesuwać wstawkę na koniec łańcucha znaków.
    W przedstawionym przykładzie znaki będą usuwane, ale użytkownik nie będzie powiadamiany żadnym komunikatem o tym, że program wprowadził korektę, dlatego można dlatego dołączyć kod wyświetlający komunikat o wpisaniu niedozwolonego znaku:


// Plik źródłowy np. Unit1.cpp
//--------------------------------
void NoDigit(TEdit *Edit)
{
 AnsiString tab[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
 String temp = Edit->Text;
 for(int i = 0; i < 10; i++)
 {
  if(temp.SubString(temp.Length(), 1) == tab[i])
   ShowMessage(
"Wpisano niedozwolony znak! Naniesiono korektę.");
 
  temp = StringReplace(temp, tab[i], "", TReplaceFlags() << rfReplaceAll);
 }

 Edit->Text = temp;
 Edit->Perform(EM_SETSEL, temp.Length(), temp.Length());
}
//--------------------------------
void __fastcall TForm1::Edit1KeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
 NoDigit(Edit1);
}

//---------------------------------

...powrót do menu. 

Konwersja wartości binarnej do wartości dziesiętnej i na odwrót.

Pragnę w tej poradzie przedstawić dwie funkcje własnej produkcji dokonujące konwersji z wartości binarnej (dwójkowej) na wartość dziesiętną i odwrotnie. Co to jest wartość binarna, czyli liczba zapisana w systemie dwójkowym nie będę tutaj wyjaśniał ponieważ zakładam, że jeśli ktoś nie wie co to jest to ta porada na nic mu się nie przyda. Podam więc tylko prostą definicję:

dwójkowy system (system binarny) - system używający kombinacji cyfr 1 i 0. Dwójkowe systemy odgrywają kluczową rolę w komputerach cyfrowych, w których tworzą podstawę wewnętrznego kodowania informacji. Wartość bitów jest reprezentowana jako stan włączenia i wyłączenia (1 i 0) wysokiego i niskiego napięcia w obwodzie elektrycznym. 

Dla lepszego zrozumienia problemu posłużymy się czterema obiektami typu TEdit. Do obiektu Edit1 będziemy wprowadzać wartość dwójkową, a w obiekcie Edit2 zostanie wyświetlona jego reprezentacja dziesiętna. Obiekt typu TEdit pozwalają na wprowadzanie wszelkich dostępnych znaków, a nie tylko cyfr i to cyfr z zakresu od 0 do 1, dlatego najpierw stworzymy funkcję ograniczającą wpisywanie do obiektu Edit jeden tylko cyfr 0 i 1, funkcję wywołamy w zdarzeniu OnKeyUp dla obiektu Edit1:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void BinOnly(TEdit *Edit)
{
 AnsiString tab[] = {"0", "1"};
 String temp = Edit->Text;
 const int c = (sizeof(tab) / sizeof(tab[0]));
 for(int i = 0; i < c; i++)
 {
  if(temp.SubString(temp.Length(), 1) == tab[i])
   return;
 }
 temp = temp.Delete(temp.Length(), 1);
 Edit->Text = temp;
 Edit->Perform(EM_SETSEL, temp.Length(), temp.Length());
}
//--------------------------------
void __fastcall TForm1::Edit1KeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
 BinOnly(Edit1);
}

//---------------------------------

Przedstawiona tutaj funkcja BinOnly została szerzej opisana w poradzie Tworzenie własnej maski ograniczającej wpisywanie wybranych znaków np. liczb do obiektów typu TEdit, TMemo itp., ja tutaj tylko ją trochę zmodyfikowałem. Teraz gdy obiekt Edit jeden akceptuje tylko cyfry 0 i 1 tworzymy funkcję BinToInt dokonującą konwersji, funkcja zostanie wywołana w zdarzeniu OnChange dla obiektu Edit1, a wynik jej działania zostanie wyświetlony w obiekcie Edit2:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
int BinToInt(String Value)
{
 int ValueSize, Result;
 Result = 0;
 ValueSize = Value.Length();

 for(int i = ValueSize; i > 0; i--)
  if(Value.SubString(i, 1) == "1")

 Result = Result + (1 << (ValueSize - i));
 return Result;
}
//--------------------------------
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
 Edit2->Text = (AnsiString)BinToInt(Edit1->Text);
}
//---------------------------------

To tyle jeśli chodzi o konwersję wartości binarnej do dziesiętnej, teraz stworzymy funkcję, która będzie działała w odwrotną stronę, ale zanim to zrobimy dobrze jest stworzyć funkcję ograniczającą wpisywanie do obiektu Edit3 tylko cyfr z zakresu od 0 do 9, bez liter:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
void NoLetter(TEdit *Edit)
{
 AnsiString tab[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
 String temp = Edit->Text;
 const int c = (sizeof(tab) / sizeof(tab[0]));
 for(int i = 0; i < c; i++)
 {
  if(temp.SubString(temp.Length(), 1) == tab[i])
   return;
 }
 temp = temp.Delete(temp.Length(), 1);
 Edit->Text = temp;
 Edit->Perform(EM_SETSEL, temp.Length(), temp.Length());
}
//--------------------------------
void __fastcall TForm1::Edit1KeyUp(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
 NoLetter(Edit3);
}

//---------------------------------

A teraz funkcja konwertująca wartość dziesiętną na binarną i wyświetlająca wynik w obiekcie Edit4, jak poprzednio funkcję wywołamy w zdarzeniu OnChange tyle, że dla obiektu Edit3:

// Plik źródłowy np. Unit1.cpp
//--------------------------------
#include <stdlib.h>  // tą bibliotekę należy włączyć do projektu
AnsiString IntToBin(long int Value)
{
 int p;
 String bin = "";
 String Result;
 if(Value == 0) return "0";

 for(int x = 1; x < (8 * sizeof(Value)); x++)
 {
  if((Value % 2) == 0)
   bin = "0" + bin;
  else
   bin = "1" + bin;
  Value = Value >> 1;
 }
 bin.Delete(1, (8 * bin.Pos("1") - 1) / 8);
 Result = "0" + bin; //jeżeli nie chcemy 0 na początku ten fragment należy zmienić na: Result = bin;
 return Result;
}
//--------------------------------
void __fastcall TForm1::Edit3Change(TObject *Sender)
{
 try
 {
  Edit4->Text = IntToBin(Edit3->Text.ToInt());
 }
 catch(...){;}
}
//---------------------------------

...powrót do menu.