Нескілько способів перетворення строки в число і зворотно

Кілька способів перетворення рядка в число та назад.

Дуже часто у форумі спливає те саме питання - "Як перетворити char*/char[]/CString/string в число?" або, трохи рідше "Як перетворити число на char*/char[]/CString/string". У цій невеликій статті я спробую відповісти на нього максимально повним чином.

Перетворення рядка на число

На вхід вони приймають покажчик на рядок, завершений банкрутом, а повертають - число, яке описується цим рядком.atoiтаatolсприймають наступний формат числа: [пробіли][знак]цифриаatof, відповідно: [пробіли][знак][цифри][.цифри][deE>[знак]цифри] Тутпробіли - будь-який із знаків пробілу, табуляції (t), вертикальної табуляції (v) - вони ігноруються.Знак- символ '+' або '-'. Якщо не вказано, то вважається, що число позитивне. Цифри – символи від '0' до '9'. Для числа з плаваючою точкою, якщо не вказано цифри до знака '.', то повинна бути вказана хоча б одна цифра після нього. Після дробової частини може бути вказана експонента, наступна за одним із символів-префіксів експоненти. Основний недолік цих функцій полягає в тому, що вони не сигналізують про помилку, якщо така відбулася в процесі аналізу переданого рядка. Під помилкою я розумію неможливість коректно розібрати переданий набір символів - невідповідність його формату або з інших причин.

Ці функції маютьнаступні відмінності від попередньої групи:

  • Через параметр end_ptr вони повертають покажчик перший символ, який може бути інтерпретований як частину числа.
  • Контролюють переповнення і, якщо таке сталося, сигналізують про це виставленням значення змінної errno ERANGE, а також повертають, відповідно, LONG_MAX/LONG_MIN, ULONG_MAX/ULONG_MIN і +/-HUGE_VAL залежно від знака числа в переданому рядку.
  • strtodвикористовує інформацію про поточні встановлені (через setlocale) регіональні налаштування, таким чином може коректно інтерпретувати числа із символом ',' як роздільник цілої та дробової частини.
  • Для функційstrtolтаstrtoulможна вказати основу системи числення. При цьому, якщо як основа передана 0, то основа визначається автоматично за першими символами числа. Якщо це символ '0', а відразу за ним йде цифра - то основа приймається рівним 8. Якщо перша цифра '0', а за нею йде символ 'x' або 'X', то основа приймається рівним 16. В інших випадках основа приймається рівним 10. В якості цифр у цьому випадку можна використовувати символи '0' - '9' і 'A' - 'Z' або 'a' - 'z', а основа може набувати значень від 2 до 36.
  • Якщо варіанти цих функцій для перетворення чисел, описаних unicode-рядками. Вони, відповідно, носять назви wcstolwcstoul іwcstod.
Типове використання цих функцій таке:

CODE
char* end_ptr; long val = strtol(str, &end_ptr, 10); if (*end_ptr) // Сигналізуємо про помилку в рядку > if ((val == LONG_MAX val == LONG_MIN) && errno == ERANGE) // Сигналізуємо про переповнення > // Продовжуємо штатнуроботу.

Як можна побачити, використовуючи ці функції можна отримати набагато більший контроль над перетворенням рядка в число. Для різних перетворень рекомендую користуватися саме ними. Говорячи про стандартні бібліотечні функції не можна не згадати таку функцію якscanfта її різновиди -sscanf,fscanfі т.д. .п:

і т. п. Цю функцію має сенс використовувати тільки у разі отримання числа від користувача з консолі (stdin) або файлу. Слід зазначити, що функція дуже важковагова (за обсягом лінкуемого до виконуваного модуля бібліотечного коду), і далеко не така швидка, як попередні, тому що для перетворення необхідно розібрати форматний рядок і відповідним чином його проінтерпретувати. Аналогічно можна використовувати оператори потокового введення ('>>'). У разі написання програм на C++ цей варіант набагато кращий, ніж використання методу scanf, тому що забезпечує набагато більший контроль за типами на етапі компіляції. Очікуваний формат числа можна вказати за допомогою прапора формату:dec -ціле в десятковому форматі;hex- ціле у шістнадцятковому форматі;oct- ціле у вісімковому форматі;scientific -число з плаваючою точкою в експоненційному форматі;fixed -число з плаваючою точкою у фіксованому форматі. При цьому формати для цілих чисел і чисел із плаваючою точкою встановлюються незалежно один від одного.

UINTGetDlgItemInt(HWNDhDlg,intitemId, BOOL*pTranslated, BOOLsigned) UNITCWnd::GetDlgItemInt(intitemId, BOOL*pTranslated, BOOLsigned)

Першим параметром API-виклик передається хендл діалогового вікна, якому належить рядок редагування. Параметр itemId визначає ідентифікатор рядка введення, через pTranslated повертається ознака того, що введений користувачем рядок успішно проінтерпретований як ціле число. Параметром signed задається необхідність отримання як результат функції знакового числа. Для перетворення екземпляра класу CString в число можна скористатися будь-яким із запропонованих вище методів, тому що CString легко перетворюється на покажчик на null-terminated-рядок. Спеціальних методів для цього класу розробниками бібліотеки не передбачено. У VCL ситуація трохи краща. Для класу AnsiString з цієї бібліотеки визначено такі методи, що дозволяють отримати число з його рядкового представлення:ToInt- просте перетворення рядка в ціле число;ToDouble- перетворює рядок на число з плаваючою точкою. Формат розділових символів читається з регіональних налаштувань системи. Обидва методи (у разі невідповідності рядка формату числа) викидають виняток. Також варто звернути увагу на методToIntDef, який (у разі невдалого перетворення) повертає значення за умовчанням, передане йому як параметр. Ось основні бібліотечні та API-функції, які можна використовувати для перетворення рядка в число.

Перетворення числа в рядок

На жаль, стандартноїспеціалізованоїфункції (описаної в стандарті мови) для зворотного перетворення (числа в рядок) не існує. Але це не означає, що таких функцій немає у RTL компіляторів. Для перетворення цілогочисла в рядок можна скористатися такими функціями:

ці функції входять до бібліотеки компіляторів від Microsoft. Для компіляторів фірми Borland можна використовувати такі функції:

Як можна бачити - у компіляторах сімейства Microsoft і Borland імена функцій відрізняються не сильно і формуються за таким принципом: [_]вихідний_типtoформат_рядка. Вихідні типи представлені наступними абревіатурами:i- int;l- long;ul- unsigned long;i64- int64;ui64- unsigned int 64; формат рядка, відповідно:a- однобайтний char (ANSI);w- wide-char (Unicode). Параметри, що приймаються на вхід, мають наступний сенс. Перший параметр (value) – це значення, яке необхідно перетворити на рядок. Другий (string) - буфер, який буде поміщений результат перетворення. А третій (radix) - основа системи числення, в якій буде представлено число. аналогиatoiтаstrtol. До неспеціалізованих стандартних функцій, що виконують необхідне перетворення, можна віднести функціюsprintf:

виконує форматований висновок заданий буфер, а також інші функції з цього сімейства. Для необхідного перетворення нам необхідно скористатися форматним рядком "%d". При цьому буде виконано стандартне перетворення на десяткове уявлення числа, аналогічне попереднім функціям. Формат одержаного числа визначається тегом типу. Існують такі теги типів:d(абоi) - знакове ціле у десятковому форматі;u- беззнакове ціле у десятковому форматіформаті;o- беззнакове ціле у вісімковому форматі;x(абоX) - беззнакове ціле у шістнадцятковому форматі. Можна використовувати також спеціальні специфікатори тегів типу (вказуються безпосередньо перед тегом) для того, щоб вказати спеціальний формат, в якому передано число:l- передано довге ціле (signed/unsigned long);h- передано коротке ціле (signed/unsigned short);l64(для Microsoft) абоL(для Borland) - передано 64-бітове ціле (signed/unsigned __int64). Взагалі кажучи, при використанні специфікатора типу h Необхідно розуміти, що з передачі у функцію printf змінних типу short (за стандартом) просуваються до типу int. Отже, цей специфікатор просто дає вказівку функції форматування ігнорувати старші розряди отриманого цілого параметра. Функції форматного виведення хороші тим, що можна явно вказати формат, у якому хочемо отримати результат. Зокрема, форматний рядок "%08x" перетворює передане число в рядок, що містить шістнадцяткове уявлення числа шириною 8 символів, при цьому, при необхідності, рядок буде доповнено зліва незначними нулями до 8 символів. Також перевагою цих функцій (перед попередньою групою) є те, що рядок перетворюється відповідно до встановленихдля програми(за допомогою виклику функціїsetlocale) регіональними налаштуваннями (locales). До основних недоліків методів форматного виведення можна віднести їх низьку швидкість роботи (на порядок повільніше їх спеціалізованих аналогів), неможливість перетворення в системи числення, відмінні від вісімкової, десяткової, і шістнадцяткової, а також відсутності контролю за типами та розміром переданого буфера, що може призводити доважковиявлених помилок. Для переведення чисел з плаваючою точкою призначені такі методи:

- для Microsoft-компіляторів та

Останнім параметрам у функції передається ознака знаковості числа.

Ось, напевно, і все, що можна сказати про штатні функції перетворення. У наступній частині буде розглянуто різні варіанти "самописних" функцій перетворення.

А можна оптимізувати та узагальнити ф-ії подібні _itow, _ltow, _ultow, _i64tow, _ui64tow та їх ANSI аналоги. Причому нижче наведена ф-ия працюватиме значно швидше, ніж стандартні. Наприклад, для перетворення цілого на рядковий параметр UNICODE, всі аналоги з префіксом _w, спочатку створюють буфер char фіксованого розміру, потім викликають однойменні версії для С-рядків, які викликають xtoa або x64toa, потім відбувається виклик ф-ій перетворення для перетворення ANSI в UNICODE (mbstowcs та __mbstowcs_mt). Ця ф-ии робить ніяких непрямих викликів, і вимагає менше часу проходження її тілу. Прюс ще одна відмінність від стандарту, при перетворенні на 16-річне число до рядка додається префікс "\0x".

// Відповідні constants повинні бути використані під час функції call. enum Radix BINARY = 2, OCTOPLE = 8, DECIMAL = 10, HEXADEMICAL = 16 >;

template void APIENTRY itos(Int val, Str buf, UINT size, Radix r=DECIMAL) throw() Str offset = buf; // a pointer to a string Str first = offset; // a pointer to // the first digit UINT delta_y; // the value of a digit UINT count = 0; // a number of digits // in the string

// val is negative, so let's add '-' to // the begin of the string. if (val 0 & size-- >= 0) delta_y = (Int)(val % r); val /= r;

if (delta_y> 9) // Шістнадцятковий формат. *зміщення++ = (delta_y - 10 + 'a'); else // Двійковий, десятковий та вісімковий формати. *зміщення++ = (delta_y + '0'); рахунок++; >

// Вказує на останню цифру. *зміщення-- = '\0';

// Тепер наш рядок відповідає цілим числам // цифр, але в зворотному порядку. Тож зробіть // його зворотне перетворення. Якщо він містить // лише два символи, просто поміняйте їх без будь-яких // додаткових дій. якщо (рахунок == 1) повернути; інакше, якщо (кількість == 2) char temp = (char)*offset; *зсув-- = *перший; *first++ = temp; > // Для більш ніж двох символів у рядку // повертайте повний рядок у зворотному порядку в наступному циклі. інакше delta_y = кількість / 2;

while (delta_y--) char temp = (char)*перший; *перший++ = *зсув; *offset-- = temp; > > > >

Використання ф-ии "пошти" не відрізняється від стандартних.

Думаю, цей спосіб теж має право на життя:

шаблон std::string toString(T val) std::ostringstream oss; oss T fromString(const std::string& s) std::istringstream iss(s); T res; is >> res; повернути res; >

// Приклад використання std::string str; int iVal; float fVal;

str = toString(iVal); str = tiString(fVal);

iVal = fromString (str); fVal = fromString (str);