|
|
|
|
#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; } |
|
|
|
|
#2 |
|
Ищущий знания...
|
у вас какая версия аксапты?
в функции num2str в последним параметром можно указать символ разделитель для тысячных, например: num2str(num, 1,1,1,0); - последняя цифра ноль, говорит о том что разделителя вовсе не будет... может изначально присваивать так? потом можно просто перевести число обратно из строки...
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
|
|
|
#3 |
|
Участник
|
Цитата:
Сообщение от lev
у вас какая версия аксапты?
в функции num2str в последним параметром можно указать символ разделитель для тысячных, например: num2str(num, 1,1,1,0); - последняя цифра ноль, говорит о том что разделителя вовсе не будет... может изначально присваивать так? потом можно просто перевести число обратно из строки... Да, указать-то можно свой разделитель (при переводе из числа в строку), но я отталкиваюсь от того механизма который уже существует в Аксапте и его менять пока не очень хочется. Речь идет о семействе отчетов RLedgerSheet* которые отображают формочку с ListView, а в нем отображаются цифры преобразованные в строки. |
|
|
|
|
#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)); }
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
|
|
|
#5 |
|
Участник
|
Да, этот частный вариант работает.
Но было бы неправильно прописать в коде что разделитель тысячных пробел, по хорошему его надо считать из панели управления. И тогда ваш вариант ( if (sW != thousandSeparator) ) не сработает, т.к. код символа разделителя из панели управления = 160, а в переменной numResS код символа разделителя = 32. |
|
|
|
|
#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). | |
|
|
#7 |
|
Ищущий знания...
|
Цитата:
поэтому и написал цикл, малоли что там может быть, а вот то что пробел не нужен, это мы знаем точно.
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
|
|
|
#8 |
|
Участник
|
|
|
|
|
|
#9 |
|
Ищущий знания...
|
strRem функция хорошая не спорю, но я исходил изначально от того, что нам не известно что в этой функции будет вторым параметром.Если же нам известно, тогда strRem подмышку и вперед
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
|
|
|
#10 |
|
Участник
|
|
|
|
|
|
#11 |
|
Участник
|
Цитата:
![]() Система работает так, как она запрограммирована. Раз система работает вот так, значит таким образом она и была запрограммирована. Если вы хотите получить другой результат, то, попробуйте внятно сформулировать, какого результата вы ожидаете и почему (с какой целью). В зависимости от формулировки станет понятно, надо ли вообще что-либо делать, а если надо, то каким образом. Например, меня вполне устраивает в качестве разделителя тысяч пробел. Менять его на символ из панели управления не вижу смысла. Почему это не устраивает вас? В чем проблема-то? |
|
|
|
|
#12 |
|
Участник
|
Цитата:
при стандартных региональных настройках в панели управления, преобразование из числа в строку а затем из строки в число работает некорректно: X++: s = num2str(123456.78, 10, 2, -1, -1); // s ="123 456.78" (с пробелом, код которого = 32) num = str2num(s); // num = 123, в то время как ожидалось 123456.78 !!! |
|
|
|
|
#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). | |
|
|
#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);
} |
|
|
|
|
#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). | |
|
|
#16 |
|
Участник
|
Ой да, отвлекся от первоначальной задачи.
|
|
|
|
|
#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. |
|
|
|
|
#18 |
|
Участник
|
Спасибо Hyper, in.dc, lev.
Вариант решения с strKeep() вполне приемлемый, с учетом замечания по десятичной точке от lev. |
|
|
|
|
#19 |
|
Moderator
|
|
|
|
|
|
#20 |
|
Ищущий знания...
|
Этот пост к чему? я же написал что разделитель может быть НЕ только точка
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
|
| Теги |
| разделитель тысячных, axapta |
|
|
Похожие темы
|
||||
| Тема | Ответов | |||
| Разделитель страницы в MS WORD | 3 | |||
| Разделитель десятичных знаков в отчетах | 0 | |||
|