27.04.2009, 16:55 | #1 |
Участник
|
Разделитель тысячных
Привет всем!
Возникла проблема преобразования из текстового представления числа в само число, т.е. из "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 |
Ищущий знания...
|
у вас какая версия аксапты?
в функции num2str в последним параметром можно указать символ разделитель для тысячных, например: num2str(num, 1,1,1,0); - последняя цифра ноль, говорит о том что разделителя вовсе не будет... может изначально присваивать так? потом можно просто перевести число обратно из строки...
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
27.04.2009, 17:38 | #3 |
Участник
|
Цитата:
Сообщение от lev
у вас какая версия аксапты?
в функции num2str в последним параметром можно указать символ разделитель для тысячных, например: num2str(num, 1,1,1,0); - последняя цифра ноль, говорит о том что разделителя вовсе не будет... может изначально присваивать так? потом можно просто перевести число обратно из строки... Да, указать-то можно свой разделитель (при переводе из числа в строку), но я отталкиваюсь от того механизма который уже существует в Аксапте и его менять пока не очень хочется. Речь идет о семействе отчетов RLedgerSheet* которые отображают формочку с ListView, а в нем отображаются цифры преобразованные в строки. |
|
27.04.2009, 17:52 | #4 |
Ищущий знания...
|
если я правильно понял то необходимо из строки, в которой тысячные отделяются пробелами, получить число...
могу предложить такой вариант, может он и не изящный, но отработал нормально: 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 |
Участник
|
Да, этот частный вариант работает.
Но было бы неправильно прописать в коде что разделитель тысячных пробел, по хорошему его надо считать из панели управления. И тогда ваш вариант ( if (sW != thousandSeparator) ) не сработает, т.к. код символа разделителя из панели управления = 160, а в переменной numResS код символа разделителя = 32. |
|
27.04.2009, 18:20 | #6 |
Участник
|
|
|
28.04.2009, 15:05 | #7 |
Участник
|
Цитата:
Система работает так, как она запрограммирована. Раз система работает вот так, значит таким образом она и была запрограммирована. Если вы хотите получить другой результат, то, попробуйте внятно сформулировать, какого результата вы ожидаете и почему (с какой целью). В зависимости от формулировки станет понятно, надо ли вообще что-либо делать, а если надо, то каким образом. Например, меня вполне устраивает в качестве разделителя тысяч пробел. Менять его на символ из панели управления не вижу смысла. Почему это не устраивает вас? В чем проблема-то? |
|
29.04.2009, 11:28 | #8 |
Участник
|
Цитата:
при стандартных региональных настройках в панели управления, преобразование из числа в строку а затем из строки в число работает некорректно: X++: s = num2str(123456.78, 10, 2, -1, -1); // s ="123 456.78" (с пробелом, код которого = 32) num = str2num(s); // num = 123, в то время как ожидалось 123456.78 !!! |
|
29.04.2009, 12:18 | #9 |
Участник
|
Цитата:
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 |
Участник
|
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 !!! 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 ) ) ; }
__________________
Dynamics AX 4.0 SP2 Последний раз редактировалось in.dc; 29.04.2009 в 12:34. Причина: вариант с strkeep чуть раньше упомянул Hyper |
|
|
За это сообщение автора поблагодарили: valentino (1). |
29.04.2009, 12:36 | #11 |
Ищущий знания...
|
отделять копейки может не только точка поэтому и написал цикл, малоли что там может быть, а вот то что пробел не нужен, это мы знаем точно.
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
29.04.2009, 12:52 | #12 |
Участник
|
|
|
29.04.2009, 12:59 | #13 |
Ищущий знания...
|
strRem функция хорошая не спорю, но я исходил изначально от того, что нам не известно что в этой функции будет вторым параметром.
Если же нам известно, тогда strRem подмышку и вперед
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
29.04.2009, 13:33 | #14 |
Участник
|
Ой да, отвлекся от первоначальной задачи.
|
|
29.04.2009, 13:39 | #15 |
Moderator
|
Функция 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)); } Если исходное число целое, то его можно по ходу без напряжения превратить в 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 |
Участник
|
Спасибо Hyper, in.dc, lev.
Вариант решения с strKeep() вполне приемлемый, с учетом замечания по десятичной точке от lev. |
|
29.04.2009, 16:52 | #17 |
Moderator
|
|
|
29.04.2009, 17:02 | #18 |
Ищущий знания...
|
Этот пост к чему? я же написал что разделитель может быть НЕ только точка
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
29.04.2009, 17:15 | #19 |
Moderator
|
этот пост был к следующему:
1. Говоря о копейках и разделителе, Вы, наверное, имели в виду DecimalSeparator - что он может быть не только точкой, но и ЗАПЯТОЙ. 2. Я же говорил о ThousandsSeparator, что он тоже может быть ЗАПЯТОЙ (например, это стандартный разделитель у американцев). 3. Мой пост больше адресовался valentino, который учёл Ваши рекомендации, lev. 4. Мне стало любопытно, какой же вариант алгоритма он избрал окончательным. 5. Так как мне показалось, что символ "запятая" претендует на место в списке strKeep, я решил подлить масла в огонь. |
|
29.04.2009, 19:09 | #20 |
Участник
|
Цитата:
Сообщение от valentino
Может я недостаточно четко сформулировал вначале, суть сводится к следующему:
при стандартных региональных настройках в панели управления, преобразование из числа в строку а затем из строки в число работает некорректно: X++: s = num2str(123456.78, 10, 2, -1, -1); // s ="123 456.78" (с пробелом, код которого = 32) num = str2num(s); // num = 123, в то время как ожидалось 123456.78 !!! Вы описали бессмысленную, с моей точки зрения, задачу. Преобразовать число в строку, а затем из полученной строки снова сделать число. Зачем? Почему нельзя было взять исходной число без этих преобразований? Есть правила перевода числа в строку. Есть правила перевода строки в число. Эти правила вовсе не обязаны быть взаимно-однозначны. Могут, но не обязательно. Попробуйте сформулировать, хотя бы для самого себя, какова конечная цель? Вы хотите, чтобы алгоритмы преобразования число-строка и строка-число были взаимно-однозначны? По каким правилам? Почему правила должны быть именно такими? Или вам надо преобразовать вполне конкретную символьную строку (сформированную по определенным правилам) в число? Если стоит всего-лишь вторая задача, то посмотрите мой пример преобразования символьной строки в число. Алгоритм достаточно сложен. А ведь я это писал под вполне конкретную задачу. Хотя и постарался сделать код, по-возможности, достаточно универсальным. Код: // Конвертация символьной строки в число // Данный метод не анализирует возможность того, что один из разделителей может являться частью другого // Например, разделитель целой и дробной части - это две точки подряд, а разделитель троек цифр - одна точка // Последствия использования подобных разделителей в данном методе могут быть парадоксальными (не ожидАемыми) // Примеры вызова /* 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); } |
|
Теги |
разделитель тысячных, axapta |
|
Похожие темы | ||||
Тема | Ответов | |||
Разделитель страницы в MS WORD | 3 | |||
Разделитель десятичных знаков в отчетах | 0 |
Опции темы | Поиск в этой теме |
Опции просмотра | |
|