Konwersja liczb na string i vice versa

Oleksy_Adam

# Wstęp. Jak to było dawniej

Bardzo często podczas pisania aplikacji zachodzi potrzeba konwersji zmiennej tekstowej na jej numeryczny odpowiednik i vice versa. Wszelkiego typu kontrolki edycyjne, etykiety i inne wymagają łańcucha znaków, natomiast CPU do swoich obliczeń potrzebuje wartości numerycznej. Czy nam się to podoba czy nie należy dokonać konwersji.

W czasach kiedy dominował Dos i Turbo Pascal oraz C programiści nie mieli zbyt dużego wyboru w funkcjach konwertujących. Pascal oferował tylko dwie, a mianowicie: Val oraz Str. Ich odpowiednikami w C były kolejno: atoi i itoa i pochodne dla różnych typów atof, atol, ltoa. Deklaracje owych funkcji (procedur) wyglądają następująco:

Pascal/Delphi

procedure Val(S; var V; var Code: Integer);

Procedura ta operuje na liczbach całkowitych i rzeczywistych. Opis poszczególnych parametrów jest następujący:

  • S jest sekwencją znaków; Do poprawnej konwersji musi zawierać znaki + - , . 0..9.
  • V jest zwracaną wartością numeryczną po konwersji zmiennej S. Jeżeli V ma zwrócić wynik całkowitoliczbowy, zmienna S nie może zawierać , oraz ..
  • Code Zwraca pozycję wystąpienia nielegalnego znaku w zmiennej S, który uniemożliwił konwersję.

Przykłady użycia:

Delphi

Var Value :Integer;
Val('1234', Value, Code); // Value = 1234, Code = 0
Val('1.234', Value, Code); // Value = 0, Code = 2
Val('abcd', Value, Code); // Value = 0, Code = 1

C/C++
Prototyp

int atoi(const char *s);
double atof(const char *s);
long atol(const char *s);

Jeżeli operacja konwersji zakończy się sukcesem wówczas funkcje zwracają wartość odpowiadającą zmiennej s, w przeciwnym razie zwracają wartość 0.

Przykłady użycia:

Int n;
n= atoi("1234"); // n = 1234
n= atoi("1s"); // n= 0

Wiemy już jak zamienić łańcuch znaków na liczbę, teraz kolej na konwersję liczba -> łańcuch.

Nie ma problemu jeżeli używamy procedur Write/Writeln z Pascala oraz printf z C. Obie procedury tolerują zapis bez konwersji. Problem zaczyna się gdy używamy np. TextOut (Pascal) textout (C/C++). Tutaj zapis

Var alfa :Byte;
TextOut('alfa = ', alfa);  // lub
TextOut('alfa = '+alfa);

wywoła u kompilatora niestrawność. Dlatego udostępniono programistą procedurę Str w Pascalu i ltoa, itoa w C/C++.

Deklaracja w Pascalu jest następująca:

procedure Str(X [: Width [: Decimals ]]; var S);

Jej używanie jest bardzo podobne jak w przypadku Val z tą różnicą, że dla zmiennej X (która może być typu całkowitego i rzeczywistego) można zastosować formatowanie: wartość_liczbowa:ilość_cyfr:po_przecinku. Procedury tej używamy w następujący sposób:

Var s :string;
Str(1234, s); // s = '1234'
Str(PI:10:3, s); // s = 3.141

W C/C++ sprawa ma się nieco odmiennie:

char * ltoa(long value, char * string, int radix);
char *itoa(int value, char *string, int radix);

Gdzie:

  • value - zmienna liczbowa
  • string - wynik konwersji
  • radix - system liczbowy, musi zawierać się pomiędzy 2..36, jeżeli będzie poza przedziałem wówczas radix przyjmie wartość 10 (co de facto odpowiada dziesiętnemu systemowi liczbowemu). Funkcje zwracają wartość w postaci ASCII.

przykład

   int number = 12345;
   char string[25];
 
   itoa(number, string, 10);

# Ewolucja i rozwój możliwości

Czasy się jednak zmieniają (kompilatory również). Obecnie używa się Delphi i BCB. Dają one programistom większy wybór jeżeli chodzi o konwersję pozostając nadal w zgodzie z poprzednikami, dlatego nagłówki i sposoby w/w procedur nie uległy zmianie. Mamy tu do dyspozycji szereg przydatnych funkcji: IntToStr, FloatToStr, FloatToStrF, FloatToCurr, IntToHex, IntToStrDef, TryStrToInt, TryStrToFloat, StrToColor, StrToInt, StrToFloatDef i wiele innych. Przejdźmy zatem do omówienia każdej z nich. Należy dodać, że funkcje są przeciążane w celu optymalizacji działań na określonych typach.

# Konwersja liczby na string

Składnia Delphi:

function IntToStr(Value: Integer): string; overload;
function IntToStr(Value: Int64): string; overload;

Składnia C++ :

extern PACKAGE AnsiString __fastcall IntToStr(int Value);
extern PACKAGE AnsiString __fastcall IntToStr(__int64 Value);

Opis:

Konwersja typu Integer na String. Value jest to wartość konwertowana. W przypadku gdy konwersja nie była możliwa Windows zgłasza EConvertError, który należy przechwycić i odpowiednio obsłużyć.

Składnia Delphi:

function IntToHex(Value: Integer; Digits: Integer): string; overload;
function IntToHex(Value: Int64; Digits: Integer): string; overload;

Składnia C++ :

extern PACKAGE AnsiString __fastcall IntToHex(int Value, int Digits);
extern PACKAGE AnsiString __fastcall IntToHex(__int64 Value, int Digits);

Opis:

Konwersja zmiennej Integer na String w reprezentacji szesnastkowej gdzie:

  • Value - zmienna konwertowana,
  • digits - minimalna długość cyfry po konwersji
    W przypadku niepowodzenia generowany jest wyjątek EConvertError.

Składnia Delphi:

function FloatToStr(Value: Extended): string; overload;
function FloatToStr(Value: Extended; const FormatSettings: TFormatSettings): string; overload;

Składnia C++ :

extern PACKAGE AnsiString __fastcall FloatToStr(Extended Value);
extern PACKAGE AnsiString __fastcall FloatToStr(Extended Value, const TFormatSettings FormatSettings);

Opis:

Konwersja zmiennych rzeczywistych na String. Mamy tu dwie odmiany: zwykła i z możliwością ustalenia formatu wynikowego. Stała FormatSettings wygląda następująco:

type
  TFormatSettings = record
    CurrencyFormat: Byte;
    NegCurrFormat: Byte;
    ThousandSeparator: Char;
    DecimalSeparator: Char;
    CurrencyDecimals: Byte;
    DateSeparator: Char;
    TimeSeparator: Char;
    ListSeparator: Char;
    CurrencyString: string;
    ShortDateFormat: string;
    LongDateFormat: string;
    TimeAMString: string;
    TimePMString: string;
    ShortTimeFormat: string;
    LongTimeFormat: string;
    ShortMonthNames: array[1..12] of string;
    LongMonthNames: array[1..12] of string;
    ShortDayNames: array[1..7] of string;
    LongDayNames: array[1..7] of string;
    TwoDigitYearCenturyWindow: Word;
  end;

W przypadku niepowodzenia generowany wyjątek EConvertError.

Składnia Delphi:

function FloatToStrF(Value: Extended; Format: TFloatFormat; Precision, Digits: Integer): string; overload;
function FloatToStrF(Value: Extended; Format: TFloatFormat; Precision, Digits: Integer; const FormatSettings: TFormatSettings
): string; overload;

Składnia C++ :

extern PACKAGE AnsiString __fastcall FloatToStrF(Extended Value, TFloatFormat Format, int Precision, int Digits);
extern PACKAGE AnsiString __fastcall FloatToStrF(Extended Value, TFloatFormat Format, int Precision, int Digits, const TFormatSettings FormatSettings);

Opis:

Konwersja zmiennych rzeczywistych na zmienne tekstowe z możliwością ustalenia liczb znaczących i precyzji wyniku.

  • Value - zmienna konwertowana
  • Format - styl reprezentacji zmiennej po konwersji. Przyjmuje następujące wartości:
Format Definicja
ffGeneral Generalny format liczbowy. Zmienna konwertowana jest na najkrótszą możliwą postać dziesiętną używająć formatu naukowego lub zwykłego. Zbędne zera są usuwane. W przypadku gdy `x = 0.00001` wartość jest wyświetlana normalnie. Poniżej tego progu wyświetlana jest w formacie naukowym.
ffExponent Format naukowy-wykładniczy. Zmienna jest konwertowana na string postaci `d.ddd...E+dddd`. Jeżeli wartość jest mniejsza od zera to **String** zaczyna się od znaku minus. Całkowita liczba cyfr przed obliczeniem wykładnika jest ustalana parametrem Precision.
ffFixed Zmodyfikowany format wynikowy. Wartość konwertowana jest na `ddd.ddd...;`. Liczba miejsc po przecinku musi być w zakresie `0..18`. W przypadku zakresy większego liczba będzie wyświetlona w formacie naukowym.
ffNumber Format numeryczny. Wartość jest konwertowana na `d,ddd,ddd.ddd...` Generalnie to samo co `ffFixed` z dodatkowymi separatorami tysięcznymi.
ffCurrency Konwersja na format walutowy.
  • Precision - liczba cyfr znaczących
  • Digits - liczba cyfr po przecinku
    W przypadku niepowodzenia generowany wyjątek EConvertError.

Składnia Delphi:

function FloatToCurr(const Value: Extended): Currency;

Składnia C++ :

extern PACKAGE System::Currency __fastcall FloatToCurr(const Extended Value);

Opis:

Konwersja liczby zmiennoprzecinkowej na format walutowy.

# Konwersja stringu na liczbę

Analogicznie wyglądają funkcje do konwersji zmiennej tekstowej na wartość numeryczną:

Składnia Delphi:

function StrToInt(const S: string): Integer;
function StrToInt64(const S: string): Int64;
function StrToFloat(const S: string): Extended; overload;
function StrToFloat(const S: string; const FormatSettings: TFormatSettings): Extended; overload;
function StrToCurr(const S: string): Currency; overload;
function StrToCurr(const S: string; const FormatSettings: TFormatSettings): Currency; overload;

Składnia C++ :

extern PACKAGE int __fastcall StrToInt(const AnsiString S);
extern PACKAGE __int64 __fastcall StrToInt64(const AnsiString S);
extern PACKAGE Extended __fastcall StrToFloat(const AnsiString S);
extern PACKAGE Extended __fastcall StrToFloat(const AnsiString S, const TFormatSettings FormatSettings);
extern PACKAGE System::Currency __fastcall StrToCurr(const AnsiString S);
extern PACKAGE System::Currency __fastcall StrToCurr(const AnsiString S, const TFormatSettings FormatSettings);

Opis:

W przypadku gdy konwersja nie była możliwa wszystkie generują wyjątek EConvertError.

# Konwersja a ustalenie wartości domyślnej

W przypadku gdy nie chcemy obsługiwać wyjątków możemy użyć funkcji z grupy poniżej. Wszystkie one mają możliwość ustalenia domyślnej wartości zwracanej w przypadku gdy konwersja nie będzie możliwa:

Składnia Delphi:

function StrToIntDef(const S: string; const Default: Integer): Integer;
function StrToCurrDef(const S: string; const Default: Currency): Currency; overload;
function StrToCurrDef(const S: string; const Default: Currency; const FormatSettings: TFormatSettings): Currency; overload;
function StrToFloatDef(const S: string; const Default: Extended): Extended; overload;
function StrToFloatDef(const S: string; const Default: Extended; const FormatSettings: TFormatSettings): Extended; overload;

Składnia C++ :

extern PACKAGE int __fastcall StrToIntDef(const AnsiString S; const int Default);
extern PACKAGE System::Currency __fastcall StrToCurrDef(const AnsiString S, const System::Currency Default);
extern PACKAGE System::Currency __fastcall StrToCurrDef(const AnsiString S, const System::Currency Default, const TFormatSettings FormatSettings);
extern PACKAGE Extended __fastcall StrToFloatDef(const AnsiString S; const Extended Default);
extern PACKAGE Extended __fastcall StrToFloatDef(const AnsiString S; const Extended Default, const TFormatSettings FormatSettings);

Opis:

W miejsce Default przypisujemy wartość jaką ma zwrócić funkcja w przypadku niepomyślnej konwersji.

# Czy dany ciąg znaków jest liczbą?

Często stajemy przed problemem sprawdzenia czy dany ciąg znaków jest liczbą. Tu z pomocą przychodzą nam funkcje:

Składnia Delphi:

function TryStrToInt(const S: string; out Value: Integer): Boolean;
function TryStrToInt64(const S: string; out Value: Int64): Boolean;
function TryStrToFloat(const S: string; out Value: Extended): Boolean; overload;
 
function TryStrToFloat(const S: string; out Value: Double): Boolean; overload;
function TryStrToFloat(const S: string; out Value: Single): Boolean; overload;
 
function TryStrToFloat(const S: string; out Value: Extended; const FormatSettings: TFormatSettings): Boolean; overload;
 
function TryStrToFloat(const S: string; out Value: Double; const FormatSettings: TFormatSettings): Boolean; overload;
function TryStrToFloat(const S: string; out Value: Single; const FormatSettings: TFormatSettings): Boolean; overload;
function TryStrToCurr(const S: string; out Value: Currency): Boolean; overload;
function TryStrToCurr(const S: string; out Value: Currency; const FormatSettings: TFormatSettings): Boolean; overload;

Składnia C++ :

extern PACKAGE bool __fastcall TryStrToInt(const AnsiString S, int &Value);
extern PACKAGE bool __fastcall TryStrToInt64(const AnsiString S, __int64 &Value);
extern PACKAGE bool __fastcall TryStrToFloat(const AnsiString S, Extended &Value);
 
extern PACKAGE bool __fastcall TryStrToFloat(const AnsiString S, double &Value);
extern PACKAGE bool __fastcall TryStrToFloat(const AnsiString S, float &Value);
 
extern PACKAGE bool __fastcall TryStrToFloat(const AnsiString S, Extended &Value, const TFormatSettings FormatSettings);
 
extern PACKAGE bool __fastcall TryStrToFloat(const AnsiString S, double &Value, const TFormatSettings FormatSettings);
extern PACKAGE bool __fastcall TryStrToFloat(const AnsiString S, float &Value, const TFormatSettings FormatSettings);
extern PACKAGE bool __fastcall TryStrToCurr(const AnsiString S, System::Currency &Value);
extern PACKAGE bool __fastcall TryStrToCurr(const AnsiString S, System::Currency &Value, const TFormatSettings FormatSettings);

Opis:

W przypadku pomyślnej konwersji wunkcja zwraca wartość True, w przeciwnym razie False.

# Przykłady użycia funkcji w kodzie programu

Składnia Delphi\Pascal

Var 
 i :LongInt;
s :string;
 
i := StrToInt(Edit1.Text);
i := StrToInt('123');
s := intToStr(i);
s := IntToStr(123);
 
i := StrToIntDef(s, 0);
if TryStrToInt(s, i) then
  ShowMessage(s + ' jest poprawnym formatem liczbowym');

Składnia C\C++

long i;
 
i = StrToInt(Edit1->Text);
Edit1->Text = IntToStr(i);
 
If !(TryStrToInt(Edit1->Text, i) ShowMessage("Błąd konwersji");</cpp>
 
= Przechwytywanie wyjątku EConvertError =
 
Składnia Delphi\Pascal
 
```delphi
try
 i := StrToInt(Edit1.Text);
except
on Ex :EconvertError do 
 begin
  { obsługa wyjątku }
 end;

Składnia C\C++ :

try
 
{
 i = StrToInt(Edit1->Text);    
}
catch (EConvertError &ConvertErr)
{
    // obsługa wyjątku
}

4 komentarzy

StrToFloat przy episaniu literek moze spowodowac Floating point overflow, a tego juz tak prosto nie da sie przechwycic:(

Miałem ochotę zapytać się jak można przekonwertować liczbę na "vice versa" ;)

@ŁF - poprawione

poprawcie apostrofy przy stringach