Konverze řetězce na číslo a zpět v C a C++

Programátoři, kteří s jazykem C (C++) teprve začínají, mohou mít problémy s konverzemi z řetězce na číslo (a zpět z čísla na řetězec). Cílem tohoto článku je poskytnout těmto programátorům přehled metod, jak v C/C++ tyto konverze snadno a bez úrazů provést. Pro ostřílené profesionály tento článek nebude ničím novým, můžete jej klidně přeskočit.

Převody z řetězce na číslo

K převodům z řetězce na číslo můžeme použít buďto vestavěné funkce jazyka C, nebo ve speciálních případech i API funkce systému Windows. Před konverzí z řetězce na číslo je nutné si jenom uvědomit, jaké číslo chceme z řetězce získat (celé, desetinné).

Vestavěné funkce funkce jazyka C

Nejjednoduššími funkcemi jazyka C pro konverzi řetězce na číslo jsou funkce atoi(), atol(), atof(), atold() a pro 64-bitové hodnoty funkce _atoi64(). Použití těchto funkcí shrnuje následující tabulka:

  atoi() atol()
Návratová hodnota int long
Příklad použití
char * szNum = "12345678";
int iNum = atoi(szNum);

printf("atoi() : %i\n", iNum);
char * szNum = "12345678";
long iNum = atol(szNum);

printf("atol() : %ld\n", iNum);
  atof() _atoi64()
Návratová hodnota double __int64
Příklad použití
char * szNum = "12345789123456789";
__int64 iNum = _atoi64(szNum);

printf("_atoi64() : %I64i\n", iNum);
char * szNum = "12345.678";
double fNum = atof(szNum);

printf("atof() : %f\n", fNum);

Při použití některé z těchto funkcí je potřeba zvolit funkci s vhodnou návratovou hodnotou. Problém nastane v případě, pokud potřebujeme zjistit, zda řetězec byl v pořádku zkonvertován. Funkce totiž nevracejí chybu. V případě že se konverze podařila jen částečně, funkce vrátí to, co se podařilo zkonvertovat. V případě zcela chybného řetězce vracejí funkce nulu. Nula ale může být i správná hodnota:

    printf("%i\n", atoi("123AAA"));    // Prints "123"
    printf("%i\n", atoi("AHOJ"));      // Prints "0"
    printf("%i\n", atoi("0"));         // Prints "0"

Pokud ale nepotřebujeme ověřovat správnost konverze (jsme si na 100% jisti, že řetězec bude VŽDY obsahovat číslo), je možné bez obav použít funkce atoXX().

Další možností, jak zkonvertovat řetězec na číslo, je použití funkce strtol() nebo strtod():

    long strtol(const char * nptr, char ** endptr, int base);
    double strtod(const char * nptr, char ** endptr);

Obě funkce vracejí výsledek konverze a zároveň volajícímu předávají pozici, na které konverze skončila. Tato vlastnost je vynikající v případě, kdy konvertujeme např. datum (1.12.2001) nebo číslo verze programu z textu do číselné podoby. Celočíselná verze (strtol) navíc umožňuje konvertovat řetězce v libovolné číselné soustavě (dvojkové, desítkové, šestnáctkové, nebo třeba v jedenáctkové - i když nepředpokládám, že byste tuto možnost někdy využili :-)). Mnohem častější je použití funkce pro konverzi v šestnáctkové soustavě:

    char * szNum = "12FF3ACD";
    int nResult;

    nResult = strtol(szNum, &szNum, 16);
    printf("strtol : 0x%08lX\n", nResult);

Tato funkce bohužel chybně zpracovává hodnoty vyšší než 0x7FFFFFFF. Pokud se pokusíte zpracovat řetězec např. 0xA0000000, funkce vrátí hodnotu 0x7FFFFFFF. Odstranění tohoto problému je možné napsáním vlastní funkce na konverzi:

int StrToInt(const char * ptr, char ** end, int root)
{
    int nDigit;
    int acc = 0;

    while((nDigit = *ptr) != 0)
    {
        // If the character is not an alphanumeric, break
        if(!isalnum(nDigit))
            break;

        // Convert to digit
        nDigit -= '0';
        if(nDigit > 9)
            nDigit -= ('A' - '9' - 1);
        if(nDigit > root-1)
            break;

        // Move the value to the next rank and add the digit
        acc *= root;
        acc += nDigit;
        ptr++;
    }

    if(end != NULL)
        *end = (char *)ptr;
    return acc;
}

Řetězce na číslo je možné převést také funkcí sscanf():

    char * szNumber = "12345678";
    int nNumber;

    sscanf(szNumber, "%i", &nNumber);
    printf("sscanf() : %i\n", nNumber);

Konverzní funkce ve Windows API

Převést řetězec na číslo umí funkce StrToIntEx(), která je součástí API MS Internet Exploreru verze 4.0. Tato funkce umožňuje konvertovat i čísla v šestnáctkové soustavě. Funkce vrací FALSE, pokud se konverze nezdařila. Pokud nevadí požadavek na přítomnost Internet Exploreru 4.0 nebo vyššího (k programu je nutné přilinkovat knihovnu Shlwapi.dll, což mi přijde zbytečné na to, abychom pouze zkonvertovali řetězec na číslo), lze ji použít bez problémů:

    int nResult;

    if(StrToIntEx("12345", STIF_DEFAULT, &nResult) == FALSE)
        printf("Chyba při konverzi\n");

Poslední zde uvedená možnost je konverze textu v dětském okně dialogu (editační políčko, statický text) přímo na číslo. K tomu slouží funkce GetDlgItemInt, definovaná takto:

    UINT GetDlgItemInt(
        HWND hDlg,            // Handle dialogového okna
        int  nID,             // Identifikátor dětského okna (např. IDC_EDIT1)
        BOOL *lpTranslated,   // Ukazatel na výsledek konverze
        BOOL bSigned);        // TRUE pokud je hodnota se znaménkem

Funkce umí zkonvertovat jak čísla se znaménkem (signed), tak i bez znaménka (unsigned), podle hodnoty parametru bSigned. Návratovou hodnotou funkce je výsledek konverze. Pokud nemohl být obsah editačního políčka zkonvertován, funkce uloží hodnotu FALSE do lpTranslated.

Převody čísel na řetězce

K převodu "opačným směrem", tedy čísla na řetězce je v jazyce C/C++ rovněž k dispozici škála funkcí, jednat vestavěných (podle normy ANSI C) a jednak i funkcí Windows API. Začneme tou asi nejznámější, sprintf().

Funkce sprintf() umí zkonvertovat jedno nebo více čísel na řetězec. K tomu, aby konverze korektně pracovala, je nutné, aby byl řetězec předem alokován (staticky, na zásobníku nebo dynamicky). Taktéž je nutné dát pozor na shodu formátovacího řetězce a typu číselné proměnné předávané jako parametr funkci sprintf:

    char   szNumber[20];                // Musí mít dostatečnou velikost !!!
    int    iNumber = 12345678;
    double fNumber = 123.456;

    sprintf(szNumber, "%i", iNumber);   // Správně
    sprintf(szNumber, "%i", fNumber);    // Chyba
    sprintf(szNumber, "%f", iNumber);    // Chyba
    sprintf(szNumber, "%f", fNumber);    // Správně

K převodu jednoho čísla na řetězec je možné použít i funkci _itoa(). Tato funkce je pracuje obráceně k funkci atoi(), uvedené výše. Použití funkce je jednoduché, a kromě nedostatečné velikosti bufferu pro uložení textu zde nelze udělat chybu:

    char szNumber[12];
    int nNumber = 123456;

    itoa(nNumber, szNumber, 10);
    printf("itoa() : %s\n", szNumber);

Konverzní funkce pro desetinná čísla se jmenuje _gcvt(). Funguje obdobně jako funkce itoa(), namísto základu zadáváme počet platných číslic:

    char szNumber[25];
    double fNumber = 123.456;

    _gcvt(fNumber, 10, szNumber);       // 10 = Max. 10 platných číslic
    printf("_gcvt() : %s\n", szNumber);    

Konverzní funkce ve Windows API

Pro účely snadného zobrazování čísel v dialogových oknech přidali vývojáři do API systému Windows funkci SetDlgItemInt(). Tato funkce je obráceným ekvivalentem funkce GetDlgItemInt(), popsané výše. Příklad na její použití je zde:

    // Nastavíme hodnotu editačního políčka
    SetDlgItemInt(hDlg, IDC_EDIT1, 12345678, FALSE);

    // Nastavíme hodnotu jiného editačního políčka
    SetDlgItemInt(hDlg, IDC_NUM_FILES, nFiles, FALSE);

Tak, a to je vše. Převody čísel na řetězce by pro vás již neměly být problémem. Na závěr malý test - dokážete najít chyby v těchto kódech ?

    //-------------------------------
    // Pokus č. 1

    char szNumber[5];
    sprintf(szNumber, "%i", 12345);

    //-------------------------------
    // Pokus č. 2

    char * szNumber = "123.555";
    double fValue; 

    sscanf(szNumber, "%i", &fValue);

    //-------------------------------
    // Pokus č. 3

    char * szNumber;
    int    nValue = 1010152;

    itoa(nValue, szNumber, 10);