Форум » [x]Harbour » Волшебные числа и операции » Ответить

Волшебные числа и операции

AndreyZh: Доброе утро! Вот наткнулся на очередную ошибку 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

Pasha: 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%

Pasha: Докладываю о проделанной работе. На форуме фокса выяснил, что в 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), то считается, что число равно нулю. Как показывает практика, при вычислениях большей погрешности не возникаает. Вот эта функция: [pre]#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[/pre]

petr707: По поводу "..используется целочисленная арифметика, при которой подобных пролбем в принципе быть не может" Это справедливо, видимо, только для операций, где нет деления ( вычисления частей) - сложение,вычитание, умножение на целое. Вопрос возник из учетной задачи с финансовыми суммами с дробями( где копейки - десятичные дроби). В этой среде -дополнительно к погрешности,вызванной машинным представлением чисел (то есть неточно), есть другая погрешность - из-за необходимости представлять промежуточные и конечные результаты целым числом копеек. Поскольку принципиально есть операции деления с потерей значаших разрядов ( например расчет сумм НДС), и где всегда ошибка (неточность) вычисления суммы частей не равна сумме ошибок(неточностей) частей. Как правило, при вычислениях необходимо , наборот, явно определять и контролировать получаемую погрешность и относить ее на особую статью учета. Например, товар 3 упаковки (упаковка = 1/3 часть целого) на общую сумму 1.00 руб (100 коп) при ставке НДС 10 % с суммой НДС 0.10 руб при продаже тремя частями при необходимости указывать цену продажи в целых копейках и фиксировать, как полную сумму, так и сумму НДС - при каждой продаже, приводит к разнице в копейку по сумме товара и сумме НДС: 1.00 руб(0.10 ндс) <> 3*0,33 (0,03 ндс - потеря значимости) = 0,99 ( 0,09 ндс) То есть, видимо, лучше применять свой специальный инструментарий для вычислений, в том числе и для сравнения финансовых сумм, чем полагаться на некие неявные свойства арифметических библиотек среды исполнения.


Pasha: petr707 пишет: По поводу "..используется целочисленная арифметика, при которой подобных пролбем в принципе быть не может" Это справедливо, видимо, только для операций, где нет деления ( вычисления частей) - сложение,вычитание, умножение на целое. Числовой тип харбора сейчас имеет несколько подтипов: целый (integer и long), и double. Операндами бинарной арифметической операции могут быть любые значения, и подтип результата такой операции определяется автоматически, чтобы избежать потери значности: скажем, если это операция long-long, то ее результатом может быть как long, так и double, если для long не хватает разрядности. Результат операции long div long - всегда double. Такие же правила можно выработать и для нового целого типа currency. Пусть результат операции currency div currency будет double, а результат остальных трех операций - currency или double (если разрядности currency не хватает). Так что этот вопрос как раз проблемы не составляет.

AndreyZh: Уважаемые господа! Особенно поклон Pasha за большие усилия по нахождению вариантов решения проблем! Ещё раз "оговорюсь", что в данной теме перечисляются проблемки, выявляющиеся в процессе эксплуатации конкретной системы и их разрешение позволит другим разработчика сразу их избежать при создании своих систем. Если и другие разработчики выявляют какие-то "технические проблемы системы разработки", то так же их хотелось бы знать заранее, что бы не наступать на "грабельки" в своих программах.

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.

AndreyZh: Доброе утро! Опишу очередные геморрои с исправлениями "несовместимостей" между 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...

Pasha: AndreyZh пишет: - xPar := AllTrim(Upper(xPar)) Если переменная не задана ( =mmm() ), то клиппер нормально продолжает работать, воспринимая xPar, как пустое символьное значение... по крайней мере в моём последующем коде... xHarbour вылетает по ошибке "неопределенный тип"... Выявилось, когда user, самостоятельно настраивая прогу забыл задать параметр. Это не соответствует действительности. Клиппер (я проверил на 5.2е) работает точно так же, как Харбор Upper(nil) генерирует argument error

AndreyZh: Pasha пишет: Это не соответствует действительности. Клиппер (я проверил на 5.2е) работает точно так же, как Харбор Upper(nil) генерирует argument error Вот "Фома неверущий"... Clipper 5.01r + CTII... использовалось без ошибок с корректной обработкой: [pre2] #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() .... // Блок ввода и проверки допуска к работе с программой [/pre2] Сейчас исправлено: [pre2] PROC Main( cPar, cSaveLog ) LOCA lRep:=FALSE, nSel:=1, nSelWork:=1, cTxt:="", aArr:={}, nC:=0, nI:=0 DEFAULT cPar TO "", cSaveLog TO "" [/pre2] где default команда препроцессора: [pre2] #xcommand DEFAULT <p> TO <v> [, <p2> TO <v2> ] => ; <p> := IF(<p> == NIL, <v>, <p>) ; [; <p2> := IF (<p2> == NIL, <v2>, <p2>) ] [/pre2]

Pasha: AndreyZh пишет: Вот "Фома неверущий"... Clipper 5.01r + CTII... использовалось без ошибок с корректной обработкой: Точнее формулировать надо. AllTrim(Upper()) это не то же самое, что Upper(Alltrim()) Проверяем: Alltrim(nil) 501 - работает 52, харбор - возникает ошибка Т.е. речь идет не о совместимости клиппера и харбора, а о несовместимости разных версий клиппера. Харбор естественно совместим с версиями 5.2e и 5.3b, которые считаются "каноническими".

AndreyZh: Pasha пишет: Alltrim(nil) 501 - работает 52, харбор - возникает ошибка Т.е. речь идет не о совместимости клиппера и харбора, а о несовместимости разных версий клиппера. Харбор естественно совместим с версиями 5.2e и 5.3b, которые считаются "каноническими Pasha пишет: Об ентом и написал выше... То есть, если писать прогу на Harbour "с нуля", то многие вещи будут казаться "очевидными" и прога, скорее всего не будет иметь ошибок, связанных "с совместимостью"... если переделывать, а особенно код, который перелопачивался на протяжении 16 лет, то "веселье" гарантировано. Точнее формулировать надо. AllTrim(Upper()) это не то же самое, что Upper(Alltrim()) Нормально, но

Pasha: AndreyZh пишет: Об ентом и написал выше... То есть, если писать прогу на Harbour "с нуля", то многие вещи будут казаться "очевидными" и прога, скорее всего не будет иметь ошибок, связанных "с совместимостью"... если переделывать, а особенно код, который перелопачивался на протяжении 16 лет, то "веселье" гарантировано. Если бы вы переводили программу с 501 на 52/53, то столкнулись с такой же проблемой. Дело не в харборе, а самом клиппере. Кстати, разработчики харбора предусмотрели возможность поведения харбора как для старых версий клиппера Если харбор собрать без флага HB_COMPAT_C53, то Alltrim не будет генерировать ошибку, если ему не передать параметр Так что слова о какой-то особой сложности перехода на харбор некорректны. Надо просто понимать эти несовместимости и учитывать их при переходе. Множество казалось бы несовместимостей связано с особенностиями ms dos, которых просто нет в современных ОС. По поводу ошибки, которую генерирует use, если попытаться 2-й раз открыть уже открытый файл 5.2 генерирует при этом ошибку, как и харбор, а 501 - игнорирует ошибку. Ну и что правильного в таком поведении ? И опять таки, мы имеем дело с разным поведением версий клиппера, а не с несовместимостью клиппера и харбора. Харбор же не может одновременно воспроизвести противоположное поведение разных версий клиппера. По TBrowseDB. Вообще-то это не класс, а функция с уже определенными блоками кода для навигации по таблице БД, и как ее можно прикрутить для навигации по текстовому файлу, я не представляю.

AndreyZh: Pasha пишет: По TBrowseDB. Вообще-то это не класс, а функция с уже определенными блоками кода для навигации по таблице БД, и как ее можно прикрутить для навигации по текстовому файлу, я не представляю. Не придирайтесь!!! - Да? Что по технологии - ловите, имеющий самостоятельную ценность исходник: [pre2] * =========================================================================== * Просмотр файлов неограниченной длины. Адаптация под стиль Жукова Андрея * программы Станислава Кросмана, а также исправление логических ошибок. * !!! Для 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 ) [/pre2]

Pasha: AndreyZh пишет: Не придирайтесь!!! - Да? Что по технологии - ловите, имеющий самостоятельную ценность исходник: Да я не придираюсь, просто не очень был понятен вопрос. Что касается xHarbour TBrowse - то он не на 100% совместим с клипперовским. И с такой сложной схемой могут быть несовместимости. В этом отношении Harbour TBrowse лучше, я так и делал: собирал xHarbour, а TBrowse брал от Harbour А касательно этой процедуры: ну зачем же делать так сложно ? Лучше загнать текстовый файл в массив, используя MLCount/MemoLine(), и затем этот массив просматривать тем же TBrowse. Это будет и быстрее, и куда проще. Массив строк можно сформировать и другим способом: через TokenInit/TokenNext/ToeknEnd

AndreyZh: Здравствуйте! ... очередной глобальный глюк [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 связывался с разработчиками и они порешали её.

Dima: AndreyZh Проверил в Harbour и XHarbour , проблемы не увидел.

AlexMyr: Т.к. AndreyZh пишет: loca cc:=spac(1) , то при вводе любого символа read завершается пробуйте так @ 1,1 say "введите символ" get cc valid lastkey()=13

AndreyZh: Уважаемые господа. Есть некая сложность с тестами, т.к. всё является частью большой системы с кучей взаимопересекающихся процедур, но как то Pasha исхитрялся находить самодостаточные примеры иллюстрирующие приводимые мной уже пару лет "глюки" Dima пишет: AndreyZh Проверил в Harbour и XHarbour , проблемы не увидел. Вы скачивали систему... желающие могут это сделать уже с нормальным дистрибутивом дистрибутив с версией июня 2012 (34мб) где ещё имеется данная проблема: программа аналитика/динамические... любой отчет, на последний запрос знака разделителя вводите различные знаки (глюки только на точке и запятой) AlexMyr пишет: , то при вводе любого символа read завершается пробуйте так @ 1,1 say "введите символ" get cc valid lastkey()=13 Не в этом проблема... Ясно, что когда ввожу единственный знак, то это и завершает READ, но поведение проги в дальнейшем зависит от символа.

AlexMyr: AndreyZh пишет: но поведение проги в дальнейшем зависит от символа. каким боком Harbour к проблеме в Вашей проге ввиде обработки символа?

petr707: 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



полная версия страницы