Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Форматирование числа (number_format)


Автор: suxxor 27.7.2011, 23:34
Есть число 1000000, нужно его отформатировать в 1,000,000

В общем, нужно создать аналог функции http://php.net/manual/ru/function.number-format.php (PHP)

Я написал что-то близкое (и оно даже работает), но нужно её максимально упростить (самое главное сделать БЕЗ рекурсии, как в примере)
или это самый оптимальный вариант
Код

// lNumber - входное число
// szBuffer - выходные данные
// bNew & iCount - для рекурсивной работы

void number_format(ULONGLONG lNumber, wchar_t* szBuffer, bool bNew, int iCount)
{
    if(lNumber <= 0)
    {
        *szBuffer = L'0';
        return;
    }

    static int iCnt = 0;
    static wchar_t cHex[] = L"0123456789";

    if(bNew)
        iCnt = 0;

    ULONGLONG lCheckDec = lNumber / 10;

    if(lCheckDec > 0)
        number_format(lCheckDec, szBuffer, FALSE, iCount + 1);

    szBuffer[iCnt] = (lNumber % 10)[cHex];

    if(iCount && !(iCount % 3))
    {
        szBuffer[iCnt + 1] = L',';
        iCnt += 2;
    }
    else
    {
        iCnt++;
    }
}


Может есть готовые решения на С++ (с использованием STL/CRT или ещё чего, только без MFC)

Автор: baldina 28.7.2011, 01:51
Код

iCnt=0;
while (lNumber > 0)
{
      if ((iCnt+1)%(4+iCnt/(4+iCnt))== 0)
        szBuffer[iCnt++] = ',';
      szBuffer[iCnt++] = (lNumber % 10)[cHex];
      lNumber /= 10;
}


Добавлено @ 01:51
ну и перевернуть строку

Автор: volatile 28.7.2011, 02:47
Цитата(baldina @  28.7.2011,  01:51 Найти цитируемый пост)
 if ((iCnt+1)%(4+iCnt/(4+iCnt))== 0)

а зачем такое условие, вроде достаточно
Код

if ((iCnt+1)%4 == 0)


Цикл лучше сделать 
do { 
  ... 
} while (lNumber);
а то 0 не выведет.

И в самом конце строку не забыть терминировать.
szBuffer[iCnt] = 0;

Автор: suxxor 28.7.2011, 06:29
О, посоны, я вас люблю!

Сделал так, вроде всё правильно, если чё не так укажите!
Код

#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h>
#include <stdio.h>
#include <conio.h>

bool number_format(ULONGLONG lNumber, wchar_t* szBuffer)
{
    if(lNumber <= 0)
        return 0;

    int iCnt = 0;
    wchar_t cHex[] = L"0123456789";
    wchar_t* szReturn = szBuffer;

    do
    {
        if((iCnt + 1) % 4 == 0)
            szReturn[iCnt++] = ',';

        szReturn[iCnt++] = (lNumber % 10)[cHex];

        lNumber /= 10;
    }
    while(lNumber);

    szReturn[iCnt + 1] = '\0';
    szBuffer = wcsrev(szReturn);

    return 1;
}

int main()
{
    wchar_t format[20] = L"\0";
    long number = 1000000;

    number_format(number, format);

    wprintf(L"%i\n%s", number, format);

    while(!getch());
}

Автор: borisbn 28.7.2011, 10:25
Цитата(suxxor @  28.7.2011,  06:29 Найти цитируемый пост)
если чё не так укажите!

1.
Цитата

  if(lNumber <= 0)
        return 0;

сравнивать ULONGLONG на предмет меньше ли оно нуля не имеет смысла, а сам нуль чем не число ???
Код

  if ( lNumber == 0 )
  {
      szBuffer[ 0 ] = L'0';
      szBuffer[ 1 ] = 0;
      return true;
   }

2.
Цитата
szReturn[iCnt++] = L',';

это непринципиально, но так будет правильней.
3.
И потом, я бы этот знак передавал бы в параметрах, а по-умолчанию сделал бы ',':
Код

bool number_format(ULONGLONG lNumber, wchar_t* szBuffer, wchar_t separator = L',' )
{
...
        if((iCnt + 1) % 4 == 0)
            szReturn[iCnt++] = separator;
...
}

int main()
{
    wchar_t format1[20] = L"\0";
    wchar_t format2[20] = L"\0";
    long number = 1000000;

    number_format( number, format1, L'.' );
    number_format( number, format2 );

    wprintf( L"%i\n%s\n", number, format1 );
    wprintf( L"%i\n%s\n", number, format2 );
...

Автор: borisbn 28.7.2011, 11:07
а вот рабочий вариант не привязанный к windows'у
http://liveworkspace.org/code/67370a62a951e3b9bef8428df42ea5c2

Автор: spyswamp 28.7.2011, 11:34
stringstream + locale и не надо никаких велосипедных циклов, вы чего.

Автор: suxxor 28.7.2011, 11:43
Цитата(spyswamp @ 28.7.2011,  11:34)
stringstream + locale и не надо никаких велосипедных циклов, вы чего.

Хммм.... пример приведи

borisbn, спасибо

а как поступать с отрицательными числами?  if(lNumber < 0) тогда сохраняем значение отрицательности и на выходе прибавляем к началу szBuffer минус, как это реализовать?

Автор: spyswamp 28.7.2011, 11:55
suxxor, http://stackoverflow.com/questions/4163243/thousand-separator-in-c читани для разгона.

Добавлено через 12 минут и 3 секунды
suxxor, а потом это http://blog.p-jansson.com/2011/02/grouping-and-thousands-separator-in-c.html - более гибкий подход.

Автор: spyswamp 28.7.2011, 12:17
Вот еще текст: http://en.allexperts.com/q/C-1040/2008/8/Thousand-Separator.htm Хотя, тут более общая информация про всю эту байду.

Автор: suxxor 28.7.2011, 12:57
spyswamp, locale выдаст формат зависимый от настроек в ОС, если я не ошибаюсь

Автор: spyswamp 28.7.2011, 13:06
suxxor, ты инфу по ссылкам почитал (в особенность по второй)?

Автор: suxxor 28.7.2011, 14:40
да короче, трудно мне с шаблонами STL работать, очень трудно ;( 
По мне лучше http://forum.vingrad.ru/index.php?showtopic=334992&view=findpost&p=2382001 вариант

Автор: bsa 29.7.2011, 11:36
Цитата(suxxor @  28.7.2011,  14:40 Найти цитируемый пост)
да короче, трудно мне с шаблонами STL работать

Если трудности вызваны не запретом начальства, то стоит взять книжку и почитать о STL. Нет в нем ничего сложного. Просто подход несколько неожиданный для классического программиста.

Автор: suxxor 2.8.2011, 00:39
bsa, проблем  начальством нет - потому что начальства нет... Учу по книге STL для профессионалов от Николая Джусьютиса, всё никак ниасилю

Автор: volatile 2.8.2011, 01:55
suxxor, я благодаря вам baldina, и borisbn, сохранил в свою личную библотеку тоже такую функцию.
Если, конечно не последуют претесты от вышеназванных кодеров по лицензионным соглашениям smile 

Если интересует заключительный вариант, то вот он.
Переносимый, универсальный на любой целочисленный тип (включая отрицательные числа).
Вывод тоже универсальный: как в юникод, так и в обычную строку.
Возвращает длину выведенной строки.
Код

template < typename TINT, typename  TCHR >
size_t currency_format  ( TINT val, TCHR * out, TCHR delimiter = TCHR(',') )
{
   bool sign = val < 0;
   size_t i = 0;
   do
   {
     if ((i+1)%4 == 0)
        out[i++] = delimiter;
      int mod = val % 10;
      if ( sign ) mod = -mod;
      out[i++] = mod + TCHR('0');
   } while (val /= 10);
   if ( sign ) out[i++] = TCHR('-');
   out[i] = 0;
   std::reverse ( out, out + i );
   return i;
}

тестирование здесь:
http://liveworkspace.org/code/19f5212fafccda70e0f437555a875e98

зы: если какие замечания по коду, велкам.

Автор: borisbn 2.8.2011, 06:50
volatile, а чего б сразу не взять модуль
Код

if ( sign )
    val = -val;

и избавиться в цикле от
Цитата
if ( sign ) mod = -mod;

?

P.S. http://www.opensource.org/licenses/mit-license.php smile

Автор: bsa 2.8.2011, 10:57
Цитата(suxxor @ 2.8.2011,  00:39)
bsa, проблем  начальством нет - потому что начальства нет... Учу по книге STL для профессионалов от Николая Джусьютиса, всё никак ниасилю

А для кого писалось http://forum.vingrad.ru/forum/topic-287943/kw-faq-books-%D0%BB%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B0-%D0%BA%D0%BD%D0%B8%D0%B3%D0%B8.html?

Автор: spyswamp 2.8.2011, 11:24
Код - жесть. Уязвим и опасен, я бы сказал, как минимум. Ботай яростно STL, ибо это никогда ревью не пройдет. smile

Автор: volatile 2.8.2011, 12:00
Цитата(borisbn @  2.8.2011,  06:50 Найти цитируемый пост)
volatile, а чего б сразу не взять модуль
код C++
if ( sign )
    val = -val;

Я думал над этим. Тут есть одна маленькая зоковыка. 
Если число является самым маленьким отрицательным, то этот фокус не проходит.
например:
signed char c = -128;
c = -c;
здесь  с не станет +128 smile



Цитата(spyswamp @  2.8.2011,  11:24 Найти цитируемый пост)
Код - жесть. Уязвим и опасен

не опасней обычных itoa(); sprintf(); и т.д.
но универсален, в отличии от последних.

Автор: spyswamp 2.8.2011, 13:15
volatile, именно поэтому означенные вами функции не рекомендованы к использованию и являются deprecated.

Автор: volatile 2.8.2011, 13:31
spyswamp, я прекрасно этими функциями пользуюсь, и никогда у меня лично с ними проблем не было.
этот код я писал не для ревью, а для личных нужд.
И меня в плане "безопасности" он вполне устраивает.

Напишите пример безопасной, но такой-же эффективной, удобной, и универсальной функции, я буду только рад.
 smile 
Ваши ссылки на применение потоковых функций с применением locale я просмотрел.
Считаю что это ни универсально, ни удобно, ни эффективно. Имхо.

ЗЫ: Я вообще не очень люблю потоковый вывод (сейчас адепты строгого С++ забросают помидорами).

Автор: borisbn 2.8.2011, 13:36
Цитата(volatile @  2.8.2011,  12:00 Найти цитируемый пост)
Я думал над этим. Тут есть одна маленькая зоковыка. 


borisbn : volatile 0 : 1
где тут смайлик с посыпанием головы пеплом ?

Автор: spyswamp 2.8.2011, 13:56
Цитата

я прекрасно этими функциями пользуюсь, и никогда у меня лично с ними проблем не было


Цитата

Ваши ссылки на применение потоковых функций с применением locale я просмотрел.
Считаю что это ни универсально, ни удобно, ни эффективно. Имхо.

ЗЫ: Я вообще не очень люблю потоковый вывод (сейчас адепты строгого С++ забросают помидорами).


Ваша функция не эффективна, не удобна, не универсальна. Ее завалить можно в 1 прием извне. RTFM.

А по STL я скажу проще: если используете C++, то и пишите на C++ с использованием соответствующего функционала. До вас уже сотни людей все изучили до подноготной, обернули, написали и соптимизировали. Не надо тут заливать всем в уши ваш бред про любовь и нелюбовь.

Дальнейшую дискуссию с вами продолжать не вижу смысла. У вас даже "имхо" неумное оказалось.

Автор: borisbn 2.8.2011, 14:19
Цитата(spyswamp @  2.8.2011,  13:15 Найти цитируемый пост)
именно поэтому означенные вами функции не рекомендованы к использованию и являются deprecated.

Кем ? M$-ом ? Если бы мы их слушали, то мы (программисты) каждый год меняли бы технологии (COM, ActiveX, MTS, .Net, Silverlight, etc. - всех не упомнишь).


Цитата(volatile @  2.8.2011,  13:31 Найти цитируемый пост)
Считаю что это ни универсально, ни удобно, ни эффективно

Цитата(volatile @  2.8.2011,  13:31 Найти цитируемый пост)
Я вообще не очень люблю потоковый вывод

поддерживаю. 

spyswamp, запиши это
Код

sprintf( str, "%03d°%02d'%02d\"", grad, min, sec );

в Си++ с потоками

Автор: spyswamp 2.8.2011, 14:41
borisbn, по printf() и ее безопасности читани интернеты, а потом уже поддерживай.

По второму вопросу: ты реально думаешь, что я не смогу на потоках это сделать? smile

Добавлено через 9 минут и 40 секунд
Это если ты скажешь "да":

Код

std::ostringstream ss;
ss.fill('0'); 
ss << std::setw(3) << grad << "°" << std::setw(2) << min << "'" << std::setw(2) << sec << "\"" << std::endl;
std::string test = ss.str();


Debug: http://dl.dropbox.com/u/943861/projects/trash/sprintf-vs-stream.png

А вообще для таких вещей уже давно есть boost::format().

Автор: baldina 2.8.2011, 14:59
spyswamp, есть у меня знакомый электрик, некоторые вещи иногда он делает не по правилам, не отключая напряжение. это не значит, что напряжение безопасно. но это не значит что он неосторожен: как раз он достаточно осторожен, что бы иногда (когда это эффективнее) работать так.
а потоки в STL не верх совершенства. хотя бы потому, что непросто создать универсальный интерфейс. было б не так, не появились бы подпорки boost::lexical_cast и boost::format.

volatile, borisbn, а вот форматирование чисел с разделителями в STL делается таки довольно изящно и удобно.

вобщем хорошш спорить  smile 

Автор: volatile 2.8.2011, 15:19
Цитата(borisbn @  2.8.2011,  14:19 Найти цитируемый пост)
запиши это
sprintf( str, "%03d°%02d'%02d\"", grad, min, sec );


Цитата(spyswamp @ 2.8.2011,  14:41)
Код

std::ostringstream ss;
ss.fill('0'); 
ss << std::setw(3) << grad << "°" << std::setw(2) << min << "'" << std::setw(2) << sec << "\"" << std::endl;
std::string test = ss.str();


Да, изящно!  smile 

Автор: spyswamp 2.8.2011, 15:33
volatile, не вырывай из контекста. borisbn попросил - я написал. И будьте внимательнее, товарищ велосипедист: "А вообще для таких вещей уже давно есть boost::format()."

Автор: borisbn 2.8.2011, 16:23
Цитата(spyswamp @  2.8.2011,  14:41 Найти цитируемый пост)
по printf() и ее безопасности читани интернеты, а потом уже поддерживай

не говори мне, что мне делать, а я не скажу (дальше - в интернетах)

Цитата(spyswamp @  2.8.2011,  14:41 Найти цитируемый пост)
ты реально думаешь, что я не смогу на потоках это сделать?

я, наверное, неправильно выразился. я не имел в виду, что этого сделать нельзя. я и сам могу это сделать. я как раз "поддерживал" то, что это будет
Цитата(volatile @  2.8.2011,  13:31 Найти цитируемый пост)
ни универсально, ни удобно, ни эффективно

как, собственно и вышло.

Цитата(spyswamp @  2.8.2011,  15:33 Найти цитируемый пост)
И будьте внимательнее, товарищ велосипедист: "А вообще для таких вещей уже давно есть boost::format()."

1. Попридержи язык с оскорблениями
2. У меня, в отличие от тебя, есть необходимость собирать проект на многих компьютерах, а не на одном своём рабочем. Ради простого проекта по тому же самому переводу широты/долготы в строку я буст подключать (а главное такскать его за собой) не собираюсь

Автор: spyswamp 2.8.2011, 16:31
borisbn, у меня проекты собираются под 4 версии винды и 2 дистрибутива linux. А все любители printf() и raw-указателей уже давно кастрированы. Чем еще хочешь померяться?

Автор: asmdzen 2.8.2011, 16:33
Сыроежка, ты где? )) smile 

Автор: volatile 2.8.2011, 16:36
Так ребята, давайте на этом остановимся. Честное слово надоело.

spyswamp, вашу точку зрения я понял.

Мне просто нужна такая функция для того чтобы можно было применять форматный вывод, без привлечения потоков.
Часто пишу небольшие утилитки вообще без <iostream> и иже с ним. Утилитки получаются небольшими, быстрыми, и удобными в написании.

Суть сводится к тому применять ли вообще в С++ функции подобные sprinyf ();
Я их применяю, и в обозримом будущем отказываться от них не собираюсь.
Вы их опасаетесь. Это ваше право, и вашу точку зрения я понимаю (и уважаю).
Всё.

 smile 

Автор: spyswamp 2.8.2011, 16:42
volatile, *handshake* Пожалуй, да. Не будем друг другу настроение портить. smile 

Автор: borisbn 2.8.2011, 17:11
Всё. Не соримся. Я не буду огульно говорить, что стримы плохо ( только http://liveworkspace.org/code/7b513c95ab922f0471544c234ba66b19 smile )... Мало того, можно привести множество примеров, где без них просто не обойтись. Никак. Но и Вы должны признать, что 1) если знаешь, что делаешь 2) протестировал на куче вариантах 3) требуется быстродействие, то в замене стримов "велосипедами" типа sprintf нет ничего предосудительного.

Короче, если обходиться без оскорблений, то высказывание своей точки зрения, а главное - выслушивание чужой - это не ссора, а спор. А в споре, как известно...

Ещё короче, я, также как и Вы, против ссор, но за спор smile
*handshake* ?

Автор: spyswamp 2.8.2011, 17:14
borisbn, sure yeah! *handshake*

Автор: volatile 2.8.2011, 23:14
Ну вот и славно...
В качестве мишени для насмешек и придирок выставляю свой старый код.
Там еще и гото используется. (действует как красная тряпка на многих).

Ох, боюсь низко пасть в глазах общественности...

Ну да и ладно, щаз покажу, чего уж там. 

Компилить под студией.
u64 - это unsigned __int64

Код

void currency_format  ( u64 val, char * out, char delimiter = ',' )
{
   char num [32]; // Достаточно [21], максимальное число 20 символов: 18446744073709551615
   const char * pnum = num;
   _ui64toa ( val, num, 10 );

   int len = strlen (num);
   switch ( ( len - 1 ) % 3 )
   {
      loop :   *out++ = delimiter;
      case 2 : *out++ = *pnum++;
      case 1 : *out++ = *pnum++;
      case 0 : *out++ = *pnum++;
      if ( *pnum ) goto loop;
   }
   *out = 0;
}

Ревнители стогого С++ и знатоки стандартов, можете забросать меня тухлыми яйцами. Я не обижусь.
Даже боюсь представить сколько пунктов стандарта здесь нарушено...

В качестве небольшого оправдания (если вообще такое возможно оправдать!) могу сказать,
что код написан очень давно, работал долго и никогда не подводил.

Автор: baldina 3.8.2011, 10:05
Цитата(volatile @  2.8.2011,  23:14 Найти цитируемый пост)
работал долго и никогда не подводил

в прямых руках и кремниевые ножи рулят)))

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)