|
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. |
|
29.04.2009, 12:18 | #6 |
Участник
|
Цитата:
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:36 | #7 |
Ищущий знания...
|
отделять копейки может не только точка поэтому и написал цикл, малоли что там может быть, а вот то что пробел не нужен, это мы знаем точно.
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
29.04.2009, 12:52 | #8 |
Участник
|
|
|
29.04.2009, 12:59 | #9 |
Ищущий знания...
|
strRem функция хорошая не спорю, но я исходил изначально от того, что нам не известно что в этой функции будет вторым параметром.
Если же нам известно, тогда strRem подмышку и вперед
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
27.04.2009, 18:20 | #10 |
Участник
|
|
|
28.04.2009, 15:05 | #11 |
Участник
|
Цитата:
Система работает так, как она запрограммирована. Раз система работает вот так, значит таким образом она и была запрограммирована. Если вы хотите получить другой результат, то, попробуйте внятно сформулировать, какого результата вы ожидаете и почему (с какой целью). В зависимости от формулировки станет понятно, надо ли вообще что-либо делать, а если надо, то каким образом. Например, меня вполне устраивает в качестве разделителя тысяч пробел. Менять его на символ из панели управления не вижу смысла. Почему это не устраивает вас? В чем проблема-то? |
|
29.04.2009, 11:28 | #12 |
Участник
|
Цитата:
при стандартных региональных настройках в панели управления, преобразование из числа в строку а затем из строки в число работает некорректно: 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:28 | #13 |
Участник
|
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, 19:09 | #14 |
Участник
|
Цитата:
Сообщение от 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); } |
|
30.04.2009, 09:28 | #15 |
Moderator
|
Попробую еще раз.
МОЕ ПОНИМАНИЕ ПРОБЛЕМЫ: Цитата:
Цитата:
X++: static void Job173(Args _args) { real a = 123456.78; str strNumberAfterStrFmt = strFmt('%1', a); real b; boolean c; ; b = str2Num( strFmt ( '%1e-2', strKeep( strNumberAfterStrFmt, '-0123456789'))); c = a == b; box::info(strFmt('%1\n\n%2\n\n%3', strFmt('Исходное число: %1', a), strFmt('Восстановленное число из строки: %1', b), strFmt('Исходное число равно числу из строки?: %1', c))); } В процессе эксперимента обратил внимание на то, что региональные настройки Аксапта считывает при старте приложения и далее в текущей сессии их не меняет (!) даже при изменении региональных настроек в панели управления. Такое поведение Аксапты отличается, например, от поведения Excel, где при смене локали c Russian на English (United States) наглядно видно, как в ячейке тут же меняются разделители тысяч с пробелов на запятые. Последний раз редактировалось Gustav; 30.04.2009 в 09:42. |
|
|
За это сообщение автора поблагодарили: aidsua (1). |
29.04.2009, 13:33 | #16 |
Участник
|
Ой да, отвлекся от первоначальной задачи.
|
|
29.04.2009, 13:39 | #17 |
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 | #18 |
Участник
|
Спасибо Hyper, in.dc, lev.
Вариант решения с strKeep() вполне приемлемый, с учетом замечания по десятичной точке от lev. |
|
29.04.2009, 16:52 | #19 |
Moderator
|
|
|
29.04.2009, 17:02 | #20 |
Ищущий знания...
|
Этот пост к чему? я же написал что разделитель может быть НЕ только точка
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
Теги |
разделитель тысячных, axapta |
|
Похожие темы | ||||
Тема | Ответов | |||
Разделитель страницы в MS WORD | 3 | |||
Разделитель десятичных знаков в отчетах | 0 |
|