Ł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 |
|
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; |
Konwersja zmiennej String do char.
String abc = "Przykładowy tekst"; char tekst[20]; sprintf(tekst, "%s", abc.c_str()); |
...i nie ma innego sposobu.
Konwersja zmiennej String do int.
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(); |
Konwersja zmiennej int do String.
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. |
Konwersja zmiennej String do float i double.
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(); |
Konwersja zmiennej float lub double do String.
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 (" ").
String abc = "Przykładowy tekst"; int x = abc.Length(); |
W efekcie zmienna x przyjmie wartość 17 ponieważ tyle znaków przechowuje zmienna abc.
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).
Usuwanie tekstu z łańcucha znaków.
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).
Wyszukiwanie liter w łańcuchu znaków.
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); |
Wstawianie tekstu do łańcucha znaków.
String abc = "Przykładowy tekst"; abc.Insert(" do wstawienia", 18); |
W efekcie zmienna abc przyjmie wartość "Przykładowy tekst do wstawienia".
Usuwanie pustych znaków - spacji.
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.
Zamiana wszystkich liter w tekście na małe lub wielkie.
String abc = "tekst MIESZANY"; String bcd = abc.UpperCase(); String cde = abc.LowerCase(); |
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; } //--------------------------------- |
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ę"; } //--------------------------------- |
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(); } //--------------------------------- |
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.
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.
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]); } //--------------------------------- |
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.
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); } //--------------------------------- |
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(...){;} } //--------------------------------- |