AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 27.04.2009, 16:55   #1  
valentino is offline
valentino
Участник
Аватар для valentino
 
33 / 11 (1) +
Регистрация: 14.02.2007
Разделитель тысячных
Привет всем!
Возникла проблема преобразования из текстового представления числа в само число, т.е. из "123 456.78" в 123456.78.
При использовании str2num(), any2real() получаем число 123 (ожидалось 123456.78).
Исследование показало следующее:
1. Попытка удалить пробелы из текстового представления числа (разделитель тысячных) ни к чему не приводит, т.к. там на самом деле стоит символ с кодом 160. Этот символ (пробел с кодом 160) берется из региональных настроек для Российского стандарта панели управления. Это стандартно для любого виндоуса.
2. Текстовое представлене числа изначально в Аксапте формируется с помощью функции strfmt("%1", число). При этом код символа разделителя тысячных = 32 (обычный пробел).
3. Текстовое представлене с помощью num2str() дает тот же результат (код символа разделителя тысячных = 32).

Таким образом вопрос сводится к следующему:
почему при преобразовании из числа в строку в качестве разделителя тысячных Аксапта вставляет пробел (код = 32), а не символ из панели управления (код = 160)?

Тестовый джоб:
X++:
static void Test_ConvertStr2num(Args _args)
{
    amount      a, b;
    int         asciiCode;
    str         s, separator, thousandsSeparator;
    boolean     result;
    ;
    thousandsSeparator = WinApi::getUserLocaleThousandSeparator_RU();
    asciiCode = char2num(thousandsSeparator, 1);    // 160
    result    = thousandsSeparator == " ";          // false
    a = 123456.78;
    s = num2str(a, 10, 2, -1, -1);                  // "123 456.78"
    separator = substr(s, 4, 1);                    // вытащить разделитель
    asciiCode = char2num(separator, 1);             // 32
    result = thousandsSeparator == separator;       // false
    result = separator == " ";                      // true
    s = strfmt("%1", a);                            // "123 456.78"
    b = any2real(s);                                // 123
    b = str2num(s);                                 // 123
    separator = substr(s, 4, 1);                    // вытащить разделитель
    asciiCode = char2num(separator, 1);             // 32
    s = strrem(s, thousandsSeparator);              // ничего не происходит
    b = str2num(s);                                 // 123
    b = any2real(s);                                // 123
    b = b;
}
Старый 27.04.2009, 17:25   #2  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
у вас какая версия аксапты?
в функции num2str в последним параметром можно указать символ разделитель для тысячных, например: num2str(num, 1,1,1,0); - последняя цифра ноль, говорит о том что разделителя вовсе не будет... может изначально присваивать так?
потом можно просто перевести число обратно из строки...
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
Старый 27.04.2009, 17:38   #3  
valentino is offline
valentino
Участник
Аватар для valentino
 
33 / 11 (1) +
Регистрация: 14.02.2007
Цитата:
Сообщение от lev Посмотреть сообщение
у вас какая версия аксапты?
в функции num2str в последним параметром можно указать символ разделитель для тысячных, например: num2str(num, 1,1,1,0); - последняя цифра ноль, говорит о том что разделителя вовсе не будет... может изначально присваивать так?
потом можно просто перевести число обратно из строки...
AX 4.0, 4.0.2501 (SP1, FP2)

Да, указать-то можно свой разделитель (при переводе из числа в строку), но я отталкиваюсь от того механизма который уже существует в Аксапте и его менять пока не очень хочется. Речь идет о семействе отчетов RLedgerSheet* которые отображают формочку с ListView, а в нем отображаются цифры преобразованные в строки.
Старый 27.04.2009, 17:52   #4  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
если я правильно понял то необходимо из строки, в которой тысячные отделяются пробелами, получить число...
могу предложить такой вариант, может он и не изящный, но отработал нормально:
X++:
static void testStrNum(Args _args)
{
    real    num = 123456.78;
    real    numRes;

    str     numResS;
    str     getNum; // промежуточное строковое значение

    int     i;
    str     sW;
    ;

    numResS = num2str(num, 1,1,1,3);
    for(i=1;i<= strLen(numResS);i++)
    {
        sW = subStr(numResS, i, 1);

        if (sW != " ")
            getNum += sW;
    }

    numRes = str2num(getNum);

    info(strFmt('%1', numRes));
}
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
Старый 27.04.2009, 18:19   #5  
valentino is offline
valentino
Участник
Аватар для valentino
 
33 / 11 (1) +
Регистрация: 14.02.2007
Да, этот частный вариант работает.
Но было бы неправильно прописать в коде что разделитель тысячных пробел, по хорошему его надо считать из панели управления. И тогда ваш вариант ( if (sW != thousandSeparator) ) не сработает, т.к. код символа разделителя из панели управления = 160, а в переменной numResS код символа разделителя = 32.
Старый 27.04.2009, 18:20   #6  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Excel: не грузятся копейки
Старый 28.04.2009, 15:05   #7  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,701 / 1195 (43) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от valentino Посмотреть сообщение
Таким образом вопрос сводится к следующему:
почему при преобразовании из числа в строку в качестве разделителя тысячных Аксапта вставляет пробел (код = 32), а не символ из панели управления (код = 160)?
На таким образом поставленный вопрос может быть только один ответ: потому что!

Система работает так, как она запрограммирована. Раз система работает вот так, значит таким образом она и была запрограммирована.

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

Например, меня вполне устраивает в качестве разделителя тысяч пробел. Менять его на символ из панели управления не вижу смысла. Почему это не устраивает вас? В чем проблема-то?
Старый 29.04.2009, 11:28   #8  
valentino is offline
valentino
Участник
Аватар для valentino
 
33 / 11 (1) +
Регистрация: 14.02.2007
Цитата:
Сообщение от Владимир Максимов Посмотреть сообщение
Например, меня вполне устраивает в качестве разделителя тысяч пробел. Менять его на символ из панели управления не вижу смысла. Почему это не устраивает вас? В чем проблема-то?
Может я недостаточно четко сформулировал вначале, суть сводится к следующему:
при стандартных региональных настройках в панели управления, преобразование из числа в строку а затем из строки в число работает некорректно:
X++:
s = num2str(123456.78, 10, 2, -1, -1);    // s ="123 456.78"  (с пробелом, код которого = 32)
num = str2num(s);    // num = 123, в то время как ожидалось 123456.78  !!!
а попытка удалить из текстового представления тысячный разделитель (это может быть не только пробел) ни к чему не приводит, т.к. в данный момент в региональных настройках символ тысячного разделителя - это пробел с кодом 160! (см. первый джоб в начале темы)
Старый 29.04.2009, 12:18   #9  
Hyper is offline
Hyper
Участник
Соотечественники
 
163 / 29 (1) +++
Регистрация: 09.10.2003
Цитата:
Сообщение от lev Посмотреть сообщение
могу предложить такой вариант, может он и не изящный, но отработал нормально
Можно от цикла избавиться:
X++:
static void testStrNum(Args _args)
{
    real    num = 123456.78;
    real    numRes;

    str     numResS;
    str     getNum; // промежуточное строковое значение

    //int     i;
    str     sW;
    ;

    numResS = num2str(num, 1,1,1,3);
    /*
    for(i=1;i<= strLen(numResS);i++)
    {
        sW = subStr(numResS, i, 1);

        if (sW != " ")
            getNum += sW;
    }
    */
    getNum = strKeep(numResS, '0123456789.');

    numRes = str2num(getNum);

    info(strFmt('%1', numRes));
}
За это сообщение автора поблагодарили: valentino (1).
Старый 29.04.2009, 12:28   #10  
in.dc is offline
in.dc
Участник
 
29 / 53 (2) ++++
Регистрация: 09.04.2009
strrem vs strkeep
Цитата:
Сообщение от valentino
X++:
s = strrem(s, thousandsSeparator);              // ничего не происходит
Цитата:
Сообщение от valentino Посмотреть сообщение
X++:
s = num2str(123456.78, 10, 2, -1, -1);    // s ="123 456.78"  (с пробелом, код которого = 32)
num = str2num(s);    // num = 123, в то время как ожидалось 123456.78  !!!
а попытка удалить из текстового представления тысячный разделитель (это может быть не только пробел) ни к чему не приводит, т.к. в данный момент в региональных настройках символ тысячного разделителя - это пробел с кодом 160! (см. первый джоб в начале темы)
А если рассмотреть вопрос с удалением символа 'разделителя тысяч' с другой позиции: т.е. не пытаться удалить (strrem) конкретный символ, а оставить в строке (содержащией числа) только допустимые с точки зрения Ax числовые символы (strkeep)?

X++:
static void jobRemoveThousandsSeparator(Args _args)
{
    #define.Numerics( ',.+-0123456789e' )
    str     s = num2str( 123456.78, 10, 2, -1, -1 ) ;
    real    num = str2num( strkeep( s, #Numerics ) ) ;
    ;

    info( strfmt( "%1", num ) ) ;
}
UPD: Oops, немного опоздал.
Изображения
 
__________________
Dynamics AX 4.0 SP2

Последний раз редактировалось in.dc; 29.04.2009 в 12:34. Причина: вариант с strkeep чуть раньше упомянул Hyper
За это сообщение автора поблагодарили: valentino (1).
Старый 29.04.2009, 12:36   #11  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
Цитата:
Сообщение от Hyper Посмотреть сообщение
Можно от цикла избавиться:
X++:
static void testStrNum(Args _args)
{
...
    getNum = strKeep(numResS, '0123456789.');
...
}
отделять копейки может не только точка поэтому и написал цикл, малоли что там может быть, а вот то что пробел не нужен, это мы знаем точно.
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
Старый 29.04.2009, 12:52   #12  
Hyper is offline
Hyper
Участник
Соотечественники
 
163 / 29 (1) +++
Регистрация: 09.10.2003
Цитата:
Сообщение от lev Посмотреть сообщение
поэтому и написал цикл, малоли что там может быть, а вот то что пробел не нужен, это мы знаем точно.
Цикл все равно не нужен - есть strRem
Старый 29.04.2009, 12:59   #13  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
Цитата:
Сообщение от Hyper Посмотреть сообщение
Цикл все равно не нужен - есть strRem
strRem функция хорошая не спорю, но я исходил изначально от того, что нам не известно что в этой функции будет вторым параметром.
Если же нам известно, тогда strRem подмышку и вперед
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
Старый 29.04.2009, 13:33   #14  
Hyper is offline
Hyper
Участник
Соотечественники
 
163 / 29 (1) +++
Регистрация: 09.10.2003
Ой да, отвлекся от первоначальной задачи.
Старый 29.04.2009, 13:39   #15  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Функция strFmt возвращает в виде строки: int-число - без десятичных знаков, real-число - с двумя десятичными знаками (и без всяких научных e-форматов). Если быть уверенным, что речь идет именно о real (а наверняка речь идёт о деньгах, а они всегда real), то можно оставить в строке только 10 цифровых символов и знак "минус". Далее полученную строку конвертнуть обратно в число и разделить на 100, "обманув" таким образом и разделитель тысяч, и десятичную точку (запятую):
X++:
static void Job173(Args _args)
{
    AmountMst   a = -10.000012345678987e+12;
    str         s = strKeep(strFmt('%1', a), '-0123456789');
    real        b = str2num(s) / 100; // ну или * 0.01
    ;
    box::info(strFmt('%1', b));
}
P.S.
Если исходное число целое, то его можно по ходу без напряжения превратить в real, сложив с 0. или умножив на 1. (точки важны!) или разделить на 1 (можно без точки):
X++:
int a = 1000000;

strFmt('%1', a + 0.) // или
strFmt('%1', a * 1.) // или
strFmt('%1', a / 1 ) // хоть по теории это и самый долгий вариант, 
                     // зато без точки :)

Последний раз редактировалось Gustav; 30.04.2009 в 12:53.
Старый 29.04.2009, 16:29   #16  
valentino is offline
valentino
Участник
Аватар для valentino
 
33 / 11 (1) +
Регистрация: 14.02.2007
Спасибо Hyper, in.dc, lev.
Вариант решения с strKeep() вполне приемлемый, с учетом замечания по десятичной точке от lev.
Старый 29.04.2009, 16:52   #17  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от valentino Посмотреть сообщение
с учетом замечания по десятичной точке от lev.
Цитата:
Сообщение от lev Посмотреть сообщение
отделять копейки может не только точка
Как насчет того, что разделителем тысяч может быть и запятая?
Старый 29.04.2009, 17:02   #18  
lev is offline
lev
Ищущий знания...
Аватар для lev
Oracle
MCBMSS
Axapta Retail User
 
1,723 / 491 (20) +++++++
Регистрация: 18.01.2005
Адрес: Москва
Цитата:
Сообщение от Gustav Посмотреть сообщение
Как насчет того, что разделителем тысяч может быть и запятая?
Этот пост к чему? я же написал что разделитель может быть НЕ только точка
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с)
С Уважением,
Елизаров Артем
Старый 29.04.2009, 17:15   #19  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от lev Посмотреть сообщение
Этот пост к чему? я же написал что разделитель может быть НЕ только точка
этот пост был к следующему:
1. Говоря о копейках и разделителе, Вы, наверное, имели в виду DecimalSeparator - что он может быть не только точкой, но и ЗАПЯТОЙ.
2. Я же говорил о ThousandsSeparator, что он тоже может быть ЗАПЯТОЙ (например, это стандартный разделитель у американцев).
3. Мой пост больше адресовался valentino, который учёл Ваши рекомендации, lev.
4. Мне стало любопытно, какой же вариант алгоритма он избрал окончательным.
5. Так как мне показалось, что символ "запятая" претендует на место в списке strKeep, я решил подлить масла в огонь.
Старый 29.04.2009, 19:09   #20  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,701 / 1195 (43) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от valentino Посмотреть сообщение
Может я недостаточно четко сформулировал вначале, суть сводится к следующему:
при стандартных региональных настройках в панели управления, преобразование из числа в строку а затем из строки в число работает некорректно:
X++:
s = num2str(123456.78, 10, 2, -1, -1);    // s ="123 456.78"  (с пробелом, код которого = 32)
num = str2num(s);    // num = 123, в то время как ожидалось 123456.78  !!!
а попытка удалить из текстового представления тысячный разделитель (это может быть не только пробел) ни к чему не приводит, т.к. в данный момент в региональных настройках символ тысячного разделителя - это пробел с кодом 160! (см. первый джоб в начале темы)
Хорошо. А сама задача-то в чем заключается? С какой целью надо "ходить по кругу"?

Вы описали бессмысленную, с моей точки зрения, задачу. Преобразовать число в строку, а затем из полученной строки снова сделать число. Зачем? Почему нельзя было взять исходной число без этих преобразований?

Есть правила перевода числа в строку. Есть правила перевода строки в число. Эти правила вовсе не обязаны быть взаимно-однозначны. Могут, но не обязательно.

Попробуйте сформулировать, хотя бы для самого себя, какова конечная цель?

Вы хотите, чтобы алгоритмы преобразования число-строка и строка-число были взаимно-однозначны? По каким правилам? Почему правила должны быть именно такими?

Или вам надо преобразовать вполне конкретную символьную строку (сформированную по определенным правилам) в число?

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

Код:
// Конвертация символьной строки в число

// Данный метод не анализирует возможность того, что один из разделителей может являться частью другого
// Например, разделитель целой и дробной части - это две точки подряд, а разделитель троек цифр - одна точка
// Последствия использования подобных разделителей в данном методе могут быть парадоксальными (не ожидАемыми)


//    Примеры вызова
/*
    print global::rtg_str2num("123.45");                // 123.45
    print global::rtg_str2num("1 234 567.89");          // 1234567.89
    print global::rtg_str2num("1 234 567.89e-2");       // 12345.6789
    print global::rtg_str2num("1 234 567,89",",");      // 1234567.89
    print global::rtg_str2num("123,5","","",",");       // 123e5 = 12 300 000
    print global::rtg_str2num("123.4,1","","",",");     // 123.4e1 = 1234
    print global::rtg_str2num("123e4.1","e","",".");    // 123.4e1 = 1234
    print global::rtg_str2num("123e4","e","",".");      // 123.4
    pause;
    return;

*/

#define.point(".")
#define.separator(" ")
#define.base("e")

public static real rtg_str2num( str _string,                    // собственно строка, которую надо перевести "123 456.78"
                                str _point      = #point,       // разделитель целой и дробной части
                                str _separator  = #separator,   // разделитель троек цифр
                                str _base       = #base         // Основание. Разделитель мантиссы и порядка числа, если оно представлено в форме "123e-2"
                                )
{

    ;

    // Как правило, параметр не указывают, если хотят указать значение параметра, следующего за ним,
    // а значение пропущенного параметра предполагается считать значением по умолчанию
    if (! _point)
    {
        _point = #point;
    }
    if (! _separator)
    {
        _separator = #separator;
    }
    if (! _base)
    {
        _base = #base;
    }

    // Ситуация, когда разные разделители имеют одно и то же значение рассматривается как ошибка,
    // поскольку становится невозможно выделить нужные части строки
    if (_point == _separator)
    {
        throw error(strFmt("Разделитель целой и дробной части \"%1\" не может быть равен разделителю троек цифр \"%2\"",_point, _separator));
    }

    if (_point == _base)
    {
        throw error(strFmt("Разделитель целой и дробной части \"%1\" не может быть равен разделителю мантиссы и порядка числа \"%2\"",_point, _base));
    }

    if (_separator == _base)
    {
        throw error(strFmt("Разделитель троек цифр \"%1\" не может быть равен разделителю мантиссы и порядка числа \"%2\"",_separator, _base));
    }


    // Удаляю ведущие и концевые пробелы
    // Вообще все пробелы удалять нельзя, поскольку какой-либо символ разделитель может содержать или быть пробелом
    // Хотя, в принципе, эту операция можно вообще не делать, поскольку функции str2num() эти пробелы не мешают
    _string = global::strLRTrim(_string);

    // Удаляю символы разделители троек цифр
    if (_separator)
    {
        if (strLen(_separator) == 1)
        {
            _string = strRem(_string, _separator);
        }
        else
        {
            // Здесь strRem() использовать нельзя, поскольку необходимо удалить именно
            // определенную последовательность символов, а не отдельные символы
            _string = global::strReplace(_string, _separator, "");
        }
    }

    // Если необходимо сделать две последовательные замены одного набора символов на другой,
    // то необходимо убедтиться в том, что вторая замена не "затрет" результат первой
    // т.е. в результате первой замены не должны появиться символы, которые заменит вторая замена
    switch (true)
    {
    case ((_base != #base) && (_point != #point) && (_point == #base) && (_base == #point)) :
        // Здесь нужна промежуточная замена на символы, которые не равны ни _point, ни _base
        // Подойдет _separator, поскольку это значение уже было проверено на данное равенство
        // и последовательность символов _separator была удалена из строки
        _string = global::strReplace(_string, _base, _separator);
        _string = global::strReplace(_string, _point, #point);
        _string = global::strReplace(_string, _separator, #base);
        break;
    case ((_base != #base) && (_point != #point) && (_point == #base)) :
        _string = global::strReplace(_string, _point, #point);
        _string = global::strReplace(_string, _base,  #base);
        break;
    case ((_base != #base) && (_point != #point) && (_base == #point)) :
        _string = global::strReplace(_string, _base,  #base);
        _string = global::strReplace(_string, _point, #point);
        break;
    default :
        if (_point != #point)
        {
            _string = global::strReplace(_string, _point, #point);
        }
        if (_base != #base)
        {
            _string = global::strReplace(_string, _base,  #base);
        }
        break;
    }


    return str2num(_string);

}
PS: использовать теги X++ не получилось, поскольку они съедают строки-комментарии в начале кода
Теги
разделитель тысячных, axapta

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Разделитель страницы в MS WORD FiGr DAX: Программирование 3 14.05.2007 15:24
Разделитель десятичных знаков в отчетах dirigente DAX: Функционал 0 20.11.2003 12:28

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 05:17.