Автор | Сообщение |
|
| |
Пост N: 166
Зарегистрирован: 12.11.06
|
|
Отправлено: 07.11.10 10:24. Заголовок: Волшебные числа и операции
Доброе утро! Вот наткнулся на очередную ошибку xHarbour и Clipper. Думал, что глюк ОС или ПК, но нет. Пример ошибки - оператор "остаток от деления": 8.8 * 3 = 26.4 или 3.3 * 3 = 9.9, но операции (%) дают 26.4%8.8 = 8.8 или (НО правильно) 9.9%3.3 = 0 Вопрос - как Вы обходите данные глюки? Или где в "арифметике" ожидать очередных "засад"? До кучи напомню об ошибочной работе функции Int в Clipper (в xHarbour кажется работает правильно)
| |
|
Ответов - 150
, стр:
1
2
3
4
5
6
7
8
All
[только новые]
|
|
|
| Администратор
|
Пост N: 2071
Зарегистрирован: 23.05.05
|
|
Отправлено: 23.09.11 12:48. Заголовок: AndreyZh пишет: 4. ..
AndreyZh пишет: цитата: | 4. Ваше объяснение не поясняет факт, что в Харб и Клиппере корректно считает (1.00 - (3.00 - 2.00) * 10000000000 = 0.0000 |
| Как так ?!! А мне показалось, что я все обьяснил Тестовая программа: Local n1 := 10000.00 Local n2, n3, i Local i1 := 0 Local i2 := 0 for i := 1 to 10000000 n2 := Round(n1+i*0.01, 2) n3 := Round(n1+n1+i*0.01, 2) if n1-(n3-n2) == 0.00 i1 ++ else i2 ++ endif next qout(i1, i2) __Wait('') Результат: 8733568 1266432 Т.е., в 87.3% сравнений получаем равенство, а в 12.7% - неравенство Вот такая она, арифметика с плавающей точкой. Это как повезет. Считайте, что для (1.00 - (3.00 - 2.00) - повезло Кстати, если варьировать параметры цикла, например поставить i до 100000, то результат будет совершенно другим, 52% на 48%
| |
|
|
| Администратор
|
Пост N: 2072
Зарегистрирован: 23.05.05
|
|
Отправлено: 25.09.11 12:57. Заголовок: Докладываю о продела..
Докладываю о проделанной работе. На форуме фокса выяснил, что в vfp используется вобщем-то некрасивое и недокументированное решение: при сравнении double числа не просто сравниваются, а сравниваются с округлением до количества десятичных знаков, которое задается для отображения переменной numeric. Это количество десятичных знаков задается при создании переменной в соответсвии со значением set decimals (или прямо в константе), и по умолчанию равно двум. Если перед созданием переменных задать SET DECIMALS TO 18, то при сравнении возникнет ошибка (.F.), точно такая же, как в харборе. Я рассказал об этом в devlist, предложив добавить подобную фичу и в харбор (по отдельной настройке SET). Виктор, естественно, отказал. Да я бы и сам подумал бы 10 раз, прежде чем это делать. Одно дело - предложить, другое - реализовать. Есть еще вариант решения этого вопроса - добавить в харбор еще один числовой подтип - currency. Имеется в виду хранение чисел в целом формате (4 или 8 байт), но с некоторой базой: например, 2 или 4. Если это 2, то, грубо говоря, значения хранятся в "копейках", если 4 - в десятитысячных долях целого. И используется целочисленная арифметика, при которой подобных пролбем в принципе быть не может. В формате dbf есть подобный тип данных - "Y" Несколько лет назад у меня самого чесались руки сделать это, но там есть свои подводные камни. Вобшем, пока этот вопрос открыт. В свою очередь, могу предложить такую функцию для проверки double на 0. В этой функции помимо сравнения значения с нулем проверяется еще и эксонента, и, если она меньше или равна -36 (т.е. значение меньше или равно 2**-36), то считается, что число равно нулю. Как показывает практика, при вычислениях большей погрешности не возникаает. Вот эта функция: #pragma BEGINDUMP #include "hbapi.h" static int exponent( double d ) { int iExponent = 0; union { double value; char string[sizeof( double )]; } xConvert; xConvert.value = d; if( xConvert.value != 0 ) { iExponent = ( int ) ( xConvert.string[7] & 0x07F ); iExponent = iExponent << 4; iExponent += ( int ) ( ( xConvert.string[6] & 0xF0 ) >> 4 ); iExponent -= 1023; } return iExponent; } HB_FUNC( EQU0 ) { double d = hb_parnd(1); hb_retl( d == 0.0 || exponent( d ) <= -36 ); } #pragma ENDDUMP
| |
|
|
| |
Не зарегистрирован
Зарегистрирован: 01.01.70
|
|
Отправлено: 25.09.11 13:39. Заголовок: Учет неполных копеек
По поводу "..используется целочисленная арифметика, при которой подобных пролбем в принципе быть не может" Это справедливо, видимо, только для операций, где нет деления ( вычисления частей) - сложение,вычитание, умножение на целое. Вопрос возник из учетной задачи с финансовыми суммами с дробями( где копейки - десятичные дроби). В этой среде -дополнительно к погрешности,вызванной машинным представлением чисел (то есть неточно), есть другая погрешность - из-за необходимости представлять промежуточные и конечные результаты целым числом копеек. Поскольку принципиально есть операции деления с потерей значаших разрядов ( например расчет сумм НДС), и где всегда ошибка (неточность) вычисления суммы частей не равна сумме ошибок(неточностей) частей. Как правило, при вычислениях необходимо , наборот, явно определять и контролировать получаемую погрешность и относить ее на особую статью учета. Например, товар 3 упаковки (упаковка = 1/3 часть целого) на общую сумму 1.00 руб (100 коп) при ставке НДС 10 % с суммой НДС 0.10 руб при продаже тремя частями при необходимости указывать цену продажи в целых копейках и фиксировать, как полную сумму, так и сумму НДС - при каждой продаже, приводит к разнице в копейку по сумме товара и сумме НДС: 1.00 руб(0.10 ндс) <> 3*0,33 (0,03 ндс - потеря значимости) = 0,99 ( 0,09 ндс) То есть, видимо, лучше применять свой специальный инструментарий для вычислений, в том числе и для сравнения финансовых сумм, чем полагаться на некие неявные свойства арифметических библиотек среды исполнения.
| |
|
|
| Администратор
|
Пост N: 2073
Зарегистрирован: 23.05.05
|
|
Отправлено: 25.09.11 17:11. Заголовок: petr707 пишет: По п..
petr707 пишет: цитата: | По поводу "..используется целочисленная арифметика, при которой подобных пролбем в принципе быть не может" Это справедливо, видимо, только для операций, где нет деления ( вычисления частей) - сложение,вычитание, умножение на целое. |
| Числовой тип харбора сейчас имеет несколько подтипов: целый (integer и long), и double. Операндами бинарной арифметической операции могут быть любые значения, и подтип результата такой операции определяется автоматически, чтобы избежать потери значности: скажем, если это операция long-long, то ее результатом может быть как long, так и double, если для long не хватает разрядности. Результат операции long div long - всегда double. Такие же правила можно выработать и для нового целого типа currency. Пусть результат операции currency div currency будет double, а результат остальных трех операций - currency или double (если разрядности currency не хватает). Так что этот вопрос как раз проблемы не составляет.
| |
|
|
| |
Пост N: 211
Зарегистрирован: 12.11.06
|
|
Отправлено: 25.09.11 18:53. Заголовок: Уважаемые господа! О..
Уважаемые господа! Особенно поклон Pasha за большие усилия по нахождению вариантов решения проблем! Ещё раз "оговорюсь", что в данной теме перечисляются проблемки, выявляющиеся в процессе эксплуатации конкретной системы и их разрешение позволит другим разработчика сразу их избежать при создании своих систем. Если и другие разработчики выявляют какие-то "технические проблемы системы разработки", то так же их хотелось бы знать заранее, что бы не наступать на "грабельки" в своих программах.
| |
|
|
| Администратор
|
Пост N: 2079
Зарегистрирован: 23.05.05
|
|
Отправлено: 27.09.11 18:45. Заголовок: Pasha пишет: Тестов..
Pasha пишет: цитата: | Тестовая программа: Local n1 := 10000.00 Local n2, n3, i Local i1 := 0 Local i2 := 0 for i := 1 to 10000000 n2 := Round(n1+i*0.01, 2) n3 := Round(n1+n1+i*0.01, 2) if n1-(n3-n2) == 0.00 i1 ++ else i2 ++ endif next qout(i1, i2) __Wait('') |
| Из любопытства запустил подобную програмку на vfp6. Харбор показал в 2 раза лучший результат: фокс ее выполнил за 18 сек, харбор - за 9.
| |
|
|
| |
Пост N: 215
Зарегистрирован: 12.11.06
|
|
Отправлено: 19.03.12 08:58. Заголовок: Строгость xHarbour, несовместимая с допусками Clipper
Доброе утро! Опишу очередные геморрои с исправлениями "несовместимостей" между Clipper и xHarbour (может быть кто-то избежит ненужных потерь времени, сразу исправляя несовместимости): 1. Передаю в функцию переменную - func mmm(xPar) - .... - xPar := AllTrim(Upper(xPar)) Если переменная не задана ( =mmm() ), то клиппер нормально продолжает работать, воспринимая xPar, как пустое символьное значение... по крайней мере в моём последующем коде... xHarbour вылетает по ошибке "неопределенный тип"... Выявилось, когда user, самостоятельно настраивая прогу забыл задать параметр. 2. USE ls_works SHAR NEW Открываю таблицу, но алиас ls_works уже используется... Клиппер на это реагирует спокойно и правильно обрабатывает рабочую область... xHarbour вылетает по ошибке... На это натыкался в самом начале переделки системы и описывал, но среди кода затесалась эта фигня, к которой обращаются крайне редко... но обратились... и программа вылетела... и на меня справедливо наехали пользователи. 3. Фигня - конечно. Для просмотра создаётся пустой файл, есть "чужая" функция просмотра текстовых файлов неограниченного размера через класс TBroseDB... так и не смог подружить её с xHarbour... но выкрутился - memedit работает с файлами неогр. размера... но народ привык, что можно при просмотре Home/End просматривать широкий отчёт, но первая строка "пустая", т.е. не работает... выкрутился - заставляю курсор перемещаться вниз на реальные строки... но когда 40 чел "проедают плешь" не очень приятно. Это то, что вспомнил с "последнего" сообщения... на тему 100% совместимостей Clipper и [x]Harbour...
| |
|
|
| Администратор
|
Пост N: 2307
Зарегистрирован: 23.05.05
|
|
Отправлено: 19.03.12 10:14. Заголовок: AndreyZh пишет: - x..
AndreyZh пишет: цитата: | - xPar := AllTrim(Upper(xPar)) Если переменная не задана ( =mmm() ), то клиппер нормально продолжает работать, воспринимая xPar, как пустое символьное значение... по крайней мере в моём последующем коде... xHarbour вылетает по ошибке "неопределенный тип"... Выявилось, когда user, самостоятельно настраивая прогу забыл задать параметр. |
| Это не соответствует действительности. Клиппер (я проверил на 5.2е) работает точно так же, как Харбор Upper(nil) генерирует argument error
| |
|
|
| |
Пост N: 216
Зарегистрирован: 12.11.06
|
|
Отправлено: 19.03.12 10:51. Заголовок: Pasha пишет: Это не..
Pasha пишет: цитата: | Это не соответствует действительности. Клиппер (я проверил на 5.2е) работает точно так же, как Харбор Upper(nil) генерирует argument error |
| Вот "Фома неверущий"... Clipper 5.01r + CTII... использовалось без ошибок с корректной обработкой: #include "laks.ch" * ---------------------------------------------------------------------------- * Стартовая процедура. PROC Main( cPar, cSaveLog ) LOCA lRep:=FALSE, nSel:=1, nSelWork:=1, cTxt:="", aArr:={}, nC:=0, nI:=0 // Глобальные системные установки пакета и конфигурация по df. #include "cfg.ch" cnProgramm := "Администр." // Загружаем/изменяем значения переменных настройки из стандартной базы данных IF !NetUse( "LS.CFG", SHAR_MODE, 5 ) THEN fErrQuit("Недоступна БД конфигурации!","W+*/N") #include "config.ch" cpZatr := IF(Empty(Alltrim(cpZatr)),"В.РАСХОД",cpZatr) // Вставка моего заголовка и блокирование закрытия окна pWind(" Администратор и бухгалтерский модуль (УС Land)... Предприятие: "+Alltrim(cpName),"hla.ico") ..... // Настройка и ремонт запускается до открытия файлов. cPar := IF( Pcount()==0, "", cPar ) IF Upper(cPar)=="R" THEN pRepair() // Ремонт IF Upper(cPar)=="C" THEN aConfig(FALSE) // Конфигурация IF Upper(cPar)=="O" THEN lRep:=TRUE // Признак отчетной программы. ZhSoft() pOpAllBase() // Пользователь в режиме неопределенный. Принудительный вызов режима проверки логики после открытия БД IF Upper(cPar)=="L" CLS pChLogic(Upper(Alltrim(cPar)),Upper(Alltrim(cSaveLog))) ENDI // Вызов системы нуления программы. IF Upper(cPar)=="D" THEN pDestroyBase() .... // Блок ввода и проверки допуска к работе с программой Сейчас исправлено: PROC Main( cPar, cSaveLog ) LOCA lRep:=FALSE, nSel:=1, nSelWork:=1, cTxt:="", aArr:={}, nC:=0, nI:=0 DEFAULT cPar TO "", cSaveLog TO "" где default команда препроцессора: #xcommand DEFAULT <p> TO <v> [, <p2> TO <v2> ] => ; <p> := IF(<p> == NIL, <v>, <p>) ; [; <p2> := IF (<p2> == NIL, <v2>, <p2>) ]
| |
|
|
| Администратор
|
Пост N: 2308
Зарегистрирован: 23.05.05
|
|
Отправлено: 19.03.12 11:22. Заголовок: AndreyZh пишет: Вот..
AndreyZh пишет: цитата: | Вот "Фома неверущий"... Clipper 5.01r + CTII... использовалось без ошибок с корректной обработкой: |
| Точнее формулировать надо. AllTrim(Upper()) это не то же самое, что Upper(Alltrim()) Проверяем: Alltrim(nil) 501 - работает 52, харбор - возникает ошибка Т.е. речь идет не о совместимости клиппера и харбора, а о несовместимости разных версий клиппера. Харбор естественно совместим с версиями 5.2e и 5.3b, которые считаются "каноническими".
| |
|
|
| |
Пост N: 217
Зарегистрирован: 12.11.06
|
|
Отправлено: 19.03.12 12:18. Заголовок: Pasha пишет: Alltri..
Pasha пишет: цитата: | Alltrim(nil) 501 - работает 52, харбор - возникает ошибка Т.е. речь идет не о совместимости клиппера и харбора, а о несовместимости разных версий клиппера. Харбор естественно совместим с версиями 5.2e и 5.3b, которые считаются "каноническими |
|
Pasha пишет: Об ентом и написал выше... То есть, если писать прогу на Harbour "с нуля", то многие вещи будут казаться "очевидными" и прога, скорее всего не будет иметь ошибок, связанных "с совместимостью"... если переделывать, а особенно код, который перелопачивался на протяжении 16 лет, то "веселье" гарантировано. цитата: | Точнее формулировать надо. AllTrim(Upper()) это не то же самое, что Upper(Alltrim()) |
| Нормально, но
| |
|
|
|
| Администратор
|
Пост N: 2309
Зарегистрирован: 23.05.05
|
|
Отправлено: 19.03.12 12:47. Заголовок: AndreyZh пишет: Об ..
AndreyZh пишет: цитата: | Об ентом и написал выше... То есть, если писать прогу на Harbour "с нуля", то многие вещи будут казаться "очевидными" и прога, скорее всего не будет иметь ошибок, связанных "с совместимостью"... если переделывать, а особенно код, который перелопачивался на протяжении 16 лет, то "веселье" гарантировано. |
| Если бы вы переводили программу с 501 на 52/53, то столкнулись с такой же проблемой. Дело не в харборе, а самом клиппере. Кстати, разработчики харбора предусмотрели возможность поведения харбора как для старых версий клиппера Если харбор собрать без флага HB_COMPAT_C53, то Alltrim не будет генерировать ошибку, если ему не передать параметр Так что слова о какой-то особой сложности перехода на харбор некорректны. Надо просто понимать эти несовместимости и учитывать их при переходе. Множество казалось бы несовместимостей связано с особенностиями ms dos, которых просто нет в современных ОС. По поводу ошибки, которую генерирует use, если попытаться 2-й раз открыть уже открытый файл 5.2 генерирует при этом ошибку, как и харбор, а 501 - игнорирует ошибку. Ну и что правильного в таком поведении ? И опять таки, мы имеем дело с разным поведением версий клиппера, а не с несовместимостью клиппера и харбора. Харбор же не может одновременно воспроизвести противоположное поведение разных версий клиппера. По TBrowseDB. Вообще-то это не класс, а функция с уже определенными блоками кода для навигации по таблице БД, и как ее можно прикрутить для навигации по текстовому файлу, я не представляю.
| |
|
|
| |
Пост N: 218
Зарегистрирован: 12.11.06
|
|
Отправлено: 19.03.12 13:32. Заголовок: Pasha пишет: По TBr..
Pasha пишет: цитата: | По TBrowseDB. Вообще-то это не класс, а функция с уже определенными блоками кода для навигации по таблице БД, и как ее можно прикрутить для навигации по текстовому файлу, я не представляю. |
| Не придирайтесь!!! - Да? Что по технологии - ловите, имеющий самостоятельную ценность исходник: Скрытый текст * =========================================================================== * Просмотр файлов неограниченной длины. Адаптация под стиль Жукова Андрея * программы Станислава Кросмана, а также исправление логических ошибок. * !!! Для xHarbour не удалось заставить работать - заменил редактированием. * =========================================================================== #include "function.ch" #define ATNUM(d,c,b,e) IF(At(d,Subs(c,e+1))==0,0,At(d,Subs(c,e+1))+nPointer-1) * #define BUFFER 4096 // Размер считываемого в начале буфера * #define BUFFER2 2048 // Размер считываемого куска файла * #define CRITICAL_LENTH 256 // Размер максимальной длины строки **** 09.2001 Переделал старые установки т.к. вылетал при просмотре больших файлов **** после чего программа стала работать более стабильно. **** ? Не понятно как повлияли данные размеры и почему. Память ОЗУ не менялась. #define BUFFER 2048 // Размер считываемого в начале буфера #define BUFFER2 1024 // Размер считываемого куска файла #define CRITICAL_LENTH 512 // Размер максимальной длины строки STAT nH // handler файла STAT nPointer // Указатель в файле // Флаг позиции в файле(0-достигнуто начало файла,1-внутри файла,2-достигнут конец файла) STAT nFl_pos STAT cStr // Строка-буфер STAT nStrPoint:=1 // Указатель в строке // Флаг позиции в строке-буфере(0-начало строки,1-внутри строки,2-достигнут конец строки) STAT nFf_pos STAT lFl_direction // Направление последнего перемещения STAT nLenFile // Длина текстового файла. // Отладочный пример. Даем параметры и вызываем функцию просмотра PROC M__12__34_(); SET SCOR OFF; Wboard(); CLS; lLookFile("LOOPFILE.PRG",1,2,22,73,"RG+/B") RETU /* Функция просмотра текстовых файлов неогран.длины при помощи обьекта-таблицы. Параметры: Имя файла, координаты окна, цвет отображения текста. Возврашает значиние ложь при обнаружении ошибки. */ FUNC lLookFile( cFile, nX, nY, nDx, nDy, cCol ) LOCA x:=2, y:=2, x1:=nX+nDx-2, y1:=nY+nDy-2, nOldCur:=SetCursor() LOCA cOldCol:=SetColor(), oB, oC, ik:=0 // Обьекты PRIV GetList:={} DEFAULT nX TO 0, nY TO 0, nDx TO 24, nDy TO 79, cCol TO "W/N" fSwopen( nX, nY, nDx, nDy, cCol, 4 ) @ 0,nY+nDy-42 SAY " Home-начало End-конец строки Esc - выход " SetCursor( 0 ) nH := Fopen(cFile, FO_READ) // Просматриваемый файл только для чтения IF nH == -1 THEN RETU FALSE // Ошибка при открытии файла. nLenFile := Fseek( nH, 0, FS_END) // Вымеряем длину файла. Go_home() // Устанавливаем указатель на начало файла oB := TBrowseNew(x, y, x1, y1) // Блоки кода перемещения по сканируемой строке. oB:goTopBlock := {|| Go_home() } oB:goBottomBlock:= {|| Go_end() } oB:skipBlock := {|nSkip| Skipp(nSkip) } // Колонка - извлеченная из строки буфера строка. oC := tbColumnNew("",{|| Take_str() }) // Пока без заголовка колонки. oC:width:= y1 - y + 1 oB:addColumn( oC ) oB:autolite := FALSE // Не выделять текущую строку в процессе стабилизации. // Начало обработки табличного обьекта. WHIL TRUE DispBegin() WHIL !oB:stabilize() .AND. NextKey()==0 // Выполняем цикл стабилизации ENDD @ 0,1 SAY " "+IF(nFl_pos==0," 0",IF(nFl_pos==2,"100",cProc()))+"% " DispEnd() IF ( ik:=Inkey(0) ) == K_ESC THEN EXIT DO CASE // Обрабатываем нажатую клавишу. CASE ik == K_LEFT IF nStrPoint > 1 // Можно перемещаться еще влево. nStrPoint-- oB:refreshAll() ENDI CASE ik == K_RIGHT // След. можно уходить за границу текста 256 знаков. nStrPoint++ oB:refreshAll() CASE ik == K_PGDN oB:rowPos := oB:rowCount // Текущая строка = количеству видим.строк IF !oB:hitBottom THEN oB:pageDown() CASE ik == K_PGUP oB:rowPos := 1 IF !oB:hitTop THEN oB:pageUp() CASE ik == K_UP oB:rowPos := 1 oB:up(); oB:up() CASE ik == K_DOWN oB:rowPos := oB:rowCount oB:down(); oB:down() CASE ik == K_CTRL_PGUP IF !oB:hitTop THEN oB:goTop() CASE ik == K_HOME IF nStrPoint <> 1 nStrPoint := 1 oB:refreshAll() ENDI CASE ik == K_CTRL_PGDN IF !oB:hitBottom THEN oB:goBottom() CASE ik == K_END nStrPoint += Max(0,60-nStrPoint) oB:refreshAll() * oB:panEnd() // Эта запись вместо двух была в оригинале текста. ENDC END Fclose( nH ) // Закрываем файл fDeact( cOldCol ) // Закрываем окно. SetCursor( nOldCur ) RETU TRUE /* Начальное считывание из файла и перерисовка экрана. */ STAT FUNC Go_home() nPointer := 1 // Указатель в просматриваемом файле. nFl_pos := nFf_pos := 0 // Позиция в буфере и файле. lFl_direction := TRUE // Направление последнего перемещения. Fseek( nH, 0, FS_SET ) // Позиционируемся в начало файла. cStr := FreadStr( nH, BUFFER ) // Начальное чтение RETU NIL /* Чтение с конца файла символов согласно размеру буфера и установ параметров */ STAT FUNC Go_end() LOCA nI:=0, nJ := Fseek( nH, 0, FS_END) // Вымеряем длину файла. Fseek( nH, -Min(BUFFER,nJ), FS_END ) // От какого места берем в буфер cStr := FreadStr( nH, Min(BUFFER,nJ) ) // Считали в сканируюмую строку Fseek( nH, 0, FS_END ) // Ушли на конец файла. nI := Rat( CRLF, cStr ) nPointer:= IF(/*есть еще знак конца строки CRLF*/ nI<>0, nI+2, 1 ) nFl_pos := nFf_pos := 2 // Признаки достижения конца строки и файла. lFl_direction := TRUE RETU NIL /* Перемещение по таблице на nN строк. Направление зависит от знака nN. */ STAT FUNC Skipp( nN ) LOCA nI := 0 IF nN >= 0 // Перемещение вниз по таблице FOR nI:=1 TO nN IF !Skipp_down() THEN nN := nI-1 // Достигнут конец файла. NEXT nI ELSE // Перемещаемся вверх по таблице. nN := -nN FOR nI:=1 to nN IF !Skipp_up() THEN nN := nI-1 // Достижение начала файла. NEXT nI nN := -nN ENDI RETU nN // На сколько сумели сдвинуться. /* Перемещение на одну строку вниз по таблице (строке-буфере,файлу). */ STAT FUNC Skipp_down() LOCA nI := ATNUM( CRLF, cStr, 1, nPointer-1 ) IF nFl_pos == 2 THEN RETU FALSE // Ранее достигли конца файла. // Необходима подкачка в буферную строку и есть для этого возможность. IF nI == 0/*нет строк вывода*/ .AND. nFf_pos <> 2 /*находимся внутри файла*/ Driver( TRUE ) // Читаем вниз по файлу. Пополняем cStr. nI := ATNUM(CRLF,cStr,1,nPointer-1) ENDI // Дальше некуда. Из файла считали все до самого его конца. IF nI == 0 THEN nFl_pos:=2/*достигли конца файла*/; RETU FALSE nPointer := nI+2 // Перемещаем указатель nFl_pos := 1 // Шагаем внутри файла. RETU TRUE /* Перемещение на одну строку вверх по таблице (строке-буферу,файлу) */ STAT FUNC Skipp_up() LOCA nI := 0 IF nFl_pos == 0 THEN RETU FALSE // Уже в начале строки. nI := Rat( CRLF, Left(cStr,nPointer-2) ) // Последнее вхождение CRLF IF nI == 0 .AND. nFf_pos <> 0 // Нужна и возможна подкачка из файла. Driver(FALSE) // Читаем вверх. nI := Rat( CRLF, Left(cStr,nPointer-2) ) ENDI IF nI == 0 // Более читать нечего. Достигли самого начала файла. nPointer := 1 nFl_pos := 0 RETU TRUE // При след.вызове проанализирует nFl_pos ENDI nPointer := nI+2 nFl_pos := 1 RETU TRUE /* Возвращает строку просмотра в табличный обьект. */ STAT FUNC Take_str() LOCA cSss:="", nI:=ATNUM( CRLF, cStr, 1, nPointer-1 ) // Нет отдельной строки, а подкачать можно т.к. не достигнут конец файла. IF (nI==0) .AND. (nFf_pos <> 2) Driver( TRUE ) // Подкачиваем из буфера в cStr и изменяем указатель nPointer. nI := ATNUM( CRLF, cStr, 1, nPointer-1 ) ENDI IF nI == 0 // Более нет текстовых строк ниже указателя. cSss := Subs( Subs(cStr,nPointer), nStrPoint ) ELSE cSss := Subs( Subs(cStr,nPointer,nI-nPointer), nStrPoint ) ENDI nI := At(CRLF,cSss) cSss := Left( cSss+Spac(CRITICAL_LENTH), CRITICAL_LENTH )+CRLF RETU cSss //tabexpand(sss) /* Драйвер подкачки из текстового файла в сканируемую строку. Параметр определяет направление просмотра. Истина читаем текст вниз по файлу. */ STAT FUNC Driver( lPar ) LOCA cBuf:="", nI:=0, nJ:=0, nK:=0, nL:=0 nFf_pos := 1 // Просмотр производится внутри строки. IF lPar .AND. nFf_pos <> 2 // Смотрим вниз, но конца файла не достигли. IF !lFl_direction // Идем в конец. Fseek( nH, Len(cStr), FS_RELATIVE ) // С текущего места. lFl_direction := TRUE ENDI IF Len( cBuf:=FreadStr(nH,BUFFER2) ) <> BUFFER2 THEN nFf_pos:=2 cStr := Right( cStr, Len(cStr) - Len(cBuf) ) + cBuf nPointer -= Len(cBuf) // nPointer += Len(cBuf) ELSEIF nFf_pos <> 0 // Нет еще начала файла. Просмотр идет вверх. IF lFl_direction // Идем в начало файла. Fseek( nH, -Len(cStr), FS_RELATIVE ) lFl_direction := FALSE ENDI nI := Fseek( nH, -Min((nL:=Fseek(nH,0,FS_RELATIVE)),BUFFER2), FS_RELATIVE) IF nI == 0 // Дальше некуда. nFf_pos := 0 nJ := nL - nI ELSE nJ := BUFFER2 ENDI /* nJ - реальное значение, на которое удалось передвинуться, nL - старое значение указателя в файле, считываем строку */ cBuf := FreadStr( nH, nJ ) Fseek( nH, nI, FS_SET ) cStr := cBuf+Left(cStr,Len(cStr)-nJ) // корректируем строку nPointer+=nJ ENDI RETU NIL /* Расчет и возврат процента прочтения текста из файла по статическим переменным */ STAT FUNC cProc() LOCA nCur := Fseek( nH, 0, FS_RELATIVE ) RETU Str( zInt(nCur*100/nLenFile), 3 )
|
| |
|
|
| Администратор
|
Пост N: 2312
Зарегистрирован: 23.05.05
|
|
Отправлено: 19.03.12 17:34. Заголовок: AndreyZh пишет: Не ..
AndreyZh пишет: цитата: | Не придирайтесь!!! - Да? Что по технологии - ловите, имеющий самостоятельную ценность исходник: |
| Да я не придираюсь, просто не очень был понятен вопрос. Что касается xHarbour TBrowse - то он не на 100% совместим с клипперовским. И с такой сложной схемой могут быть несовместимости. В этом отношении Harbour TBrowse лучше, я так и делал: собирал xHarbour, а TBrowse брал от Harbour А касательно этой процедуры: ну зачем же делать так сложно ? Лучше загнать текстовый файл в массив, используя MLCount/MemoLine(), и затем этот массив просматривать тем же TBrowse. Это будет и быстрее, и куда проще. Массив строк можно сформировать и другим способом: через TokenInit/TokenNext/ToeknEnd
| |
|
|
| |
Пост N: 232
Зарегистрирован: 12.11.06
|
|
Отправлено: 25.05.12 15:44. Заголовок: Здравствуйте! ... оч..
Здравствуйте! ... очередной глобальный глюк [x]harbour? Если так, то может быть послать разработчикам. Програмка примерно структуры: loca cc:=spac(1) .... @ 1,1 say "введите символ" get cc read .... построение отчета .... вызов запросов печати из формы и в зависимости от устройства вывод куда требуется. Проблема: Если введен символ точка или запятая, то как-бы в форме печати харб нажимает PgDn, на прочих знаках/буквах спокойненько ожидает что ему введут в качестве устройства и нажмут клавиши подтверждения. Можно, если интересно дать полный пример отчёта, но данный глюк во всех, где самым последним get вводится один символ. Как её можно побороть: Пока сделал так, благо после каждого read у меня есть вызов ф-ции верификации user, т.е. просто добавил в неё: if lastkey() = 44 .or. lastkey() = 46 keyb chr(K_ENTER) inkey(0) endi P.S. Уже была чем-то схожая проблема, когда харб не любил цифру 1, но Pasha связывался с разработчиками и они порешали её.
| |
|
|
| |
Пост N: 2381
Зарегистрирован: 17.05.05
|
|
Отправлено: 25.05.12 16:11. Заголовок: AndreyZh Проверил в..
AndreyZh Проверил в Harbour и XHarbour , проблемы не увидел.
| |
|
|
| |
Пост N: 493
Зарегистрирован: 11.06.10
|
|
Отправлено: 25.05.12 17:26. Заголовок: Т.к. AndreyZh пишет:..
Т.к. AndreyZh пишет: , то при вводе любого символа read завершается пробуйте так @ 1,1 say "введите символ" get cc valid lastkey()=13
| |
|
|
| |
Пост N: 233
Зарегистрирован: 12.11.06
|
|
Отправлено: 25.05.12 21:38. Заголовок: Уважаемые господа. ..
Уважаемые господа. Есть некая сложность с тестами, т.к. всё является частью большой системы с кучей взаимопересекающихся процедур, но как то Pasha исхитрялся находить самодостаточные примеры иллюстрирующие приводимые мной уже пару лет "глюки" Dima пишет: цитата: | AndreyZh Проверил в Harbour и XHarbour , проблемы не увидел. |
| Вы скачивали систему... желающие могут это сделать уже с нормальным дистрибутивом дистрибутив с версией июня 2012 (34мб) где ещё имеется данная проблема: программа аналитика/динамические... любой отчет, на последний запрос знака разделителя вводите различные знаки (глюки только на точке и запятой) AlexMyr пишет: цитата: | , то при вводе любого символа read завершается пробуйте так @ 1,1 say "введите символ" get cc valid lastkey()=13 |
| Не в этом проблема... Ясно, что когда ввожу единственный знак, то это и завершает READ, но поведение проги в дальнейшем зависит от символа.
| |
|
|
| |
Пост N: 494
Зарегистрирован: 11.06.10
|
|
Отправлено: 25.05.12 22:06. Заголовок: AndreyZh пишет: но ..
AndreyZh пишет: цитата: | но поведение проги в дальнейшем зависит от символа. |
|
каким боком Harbour к проблеме в Вашей проге ввиде обработки символа?
| |
|
|
| |
Не зарегистрирован
Зарегистрирован: 01.01.70
|
|
Отправлено: 25.05.12 22:27. Заголовок: get, lastkey() and picture
1) в общем случае содержимое GET не совпадает с lastkey() 2) для исключения дефолтового поведения лучше использовать конструкцию с Picture 3) ниже код, где можно посмотреть разницу - при нажатии Esc , PageDown и прочее proc main() loca cc:=space(1) , dd:=space(1),ff:=space(1),i @0,0 cls for i=1 to 20 @ 1,1 say "введите символ 1 " get cc read @2,1 say "cc="+str( asc(cc),3) @3,1 say "lastkey()="+str( lastkey(),3) @ 5,1 say "введите символ 2" get dd picture "X" read @6,1 say "dd="+str( asc(dd),3) @7,1 say "lastkey()="+str( lastkey(),3) @10,1 say "введите символ 3" get ff picture "9" read @11,1 say "ff="+str( asc(ff),3) @12,1 say "lastkey()="+str( lastkey(),3) next i return
| |
|
Ответов - 150
, стр:
1
2
3
4
5
6
7
8
All
[только новые]
|
|
|