Покрутил, потестировал функцию hb_iniRead для работы с ini-файлами.
Хочу поделиться мыслями: В память закачивается дерево хэш массивов. Комментарии ';' и '#' игнорируются. Вообще все строки, где нет разделителя "=" - идут в игнор. И если затем записать файл с помощью hb_iniWrite - то в файле будут только секции со строками key = valiue
Отправлено: 19.12.24 18:47. Заголовок: krutoff пишет Покрут..
krutoff пишет
цитата:
Покрутил, потестировал функцию hb_iniRead для работы с ini-файлами
посмотрите в MiniGui h_objects.prg (исп. этой ф-ии) ... CLASS TIniData INHERIT THmgData ... и примеры со строками
oIni := TIniData():New(cIni, .T.):Read() ... //oIni := TIniData():New(cIni, .T.):Read() - значения для ключей будут форматированы по типам //oIni := TIniData():New(cIni, .F.):Read() - значения для ключей будут только типа "C" //oIni := oIniData( cFileDcrp, .T., lUtf8, ):Read() - это читать из файла //oIni -> это hash с вложенными hash секциями и вложенными в них hash ключами
// считать настройку cnfg-файл в переменную oCnf из переменной cBuff oCnf := TIniData():New( , .T., lUtf8, , cBuff ):Read() ... и т.д.
Отправлено: 20.12.24 16:44. Заголовок: Спасибо за ответ, Се..
Спасибо за ответ, Сергей! Воспользовался Вашим советом. Получилось вот что. Сразу скажу, что мне бывает необходимо закинуть просто текст в секцию с маркерами для подстановки значений. Раньше это делал с помощью _getPrivateProfileSection(). Тут такого нет. Кстати hb_iniRead обрабатывает фразу include и вставляет секции из include-файла.
Отправлено: 20.12.24 17:58. Заголовок: krutoff пишет мне бы..
krutoff пишет
цитата:
мне бывает необходимо закинуть просто текст в секцию с маркерами для подстановки значений.
С TIniData не важно, есть ini или нет (ключи в секциях есть или нет), вы всегда можете работать с переменными oIni объекта (по мне, доступ к переменным в объекте-контейнере oIni удобнее, чем в hash). В примере Advanced\App_OopTemplate есть файлы ini, cfg, Андрей написал даже info комментарии в них и в prg, смотрите. Хранить в TIniData можно данные по типам C,N,L,D,A,B и даже данные объекта THmgData (ф-я o := oHmgData(), ...) как json данные, т.е. это несколько больше, чем просто файл ini. Создается, проверяется все оч. просто, примерно так
App.Cargo := oHmgData() ; o := App.Cargo ... o:lIni := hb_FileExists(cIni) // доступ к ини-файлу везде в программе - App.Cargo:oIni o:oIni := TIniData():New(cIni, .T.):Read()
Default o:oIni:INFO := oHmgData() // <- secija, если ее нет в переменной (ini), будет создана, как и переменные ниже Default o:oIni:INFO:Developed_in := MiniGUIVersion() Default o:oIni:INFO:xBase_compiler := Version() Default o:oIni:INFO:C_compiler := Hb_Compiler() Default o:oIni:INFO:Programm := o:cTitle Default o:oIni:INFO:ProgVers := o:cVersion Default o:oIni:INFO:Avtor := o:cAvtor Default o:oIni:INFO:Email := o:cEmail
IF !o:lIni // если нет файла, то создадим его o:oIni:cCommentBegin := " Modify: " + hb_TtoC( hb_DateTime() ) o:oIni:Write() // НЕ UTF8, т.е. нет BOM на выходе ENDIF ...
цитата:
Кстати hb_iniRead обрабатывает фразу include и вставляет секции из include-файла.
Этим не пользовался, т.к. в объекте-контейнере есть свои варианты слияния нескольких переменных oIni в один, добавление в него hash, array, json данных -> в основе лежит класс THmgData, смотрите его методы и свойства PS. тут https://clipper.borda.ru/?1-4-0-00001425-000-0-0-1729596515 пример использования oHmgData() для разных вычислений (по адресу счетчик) в примере App_OopReport подсчет итогов, т.е. секция это вариант oHmgData() контейнера со всеми вытекающими
Отправлено: 21.12.24 12:48. Заголовок: Сергей, спасибо за о..
Сергей, спасибо за ответ! Ваш TiniData очень хорош, мне все тут подходит, если бы не одно но: (правда, это не критично, можно попробовать обойтись и без комментов)
Метод TIniData():New(cIni, .T.):Read() использует функцию hb_iniReadStr(), которая глотает комментарии ';' и '#' и на выходе выдает дерево HASH-массивов. Вообще все строки, где нет разделителя "=" - идут в игнор.
В Вашем примере Advanced\App_OopTemplate в файле Demo_timer.ini в самом начале файла я поставил:
;COMMENT1 ;COMMENT2 #COMMENT 3
[HELP] Тут должна быть информация #HELP
[TEST] 01 = TEST #COMMENT 4 #COMMENT 5
Запустил Demo_timer.exe и сразу закрыл. Начало файла Demo_timer.ini стало таким:
#COMMENT 3
[HELP]
[TEST] 01 = TEST
P.S. Пример использования oHmgData() с hb_memoread(cFileini) очень заинтересовал - применить его вместо hb_iniReadStr, а парсинг hb_iniStringLow() доработать.
Отправлено: 21.12.24 13:59. Заголовок: krutoff пишет Метод ..
krutoff пишет
цитата:
Метод TIniData():New(cIni, .T.):Read() использует функцию hb_iniReadStr(), которая глотает комментарии ';' и '#'
Как говорится "Не виноватая я, он сам ... ", так работает эта ф-я в hb + она воспринимает "[" в любом месте, как начало новой секции, т.к. в работе часто после = надо ссылаться на секцию, для настройки, то в примерах и для json вместо "[","]" используются скобки "<",">", для ";" есть переменная объекта VAR cCommentChar AS STRING INIT ";", которую можно сменить параметром TIniData():New( cIni, lMacro, lUtf8, cChar, cData, cSrcChar, cOutChar ) выделенное синим, позволяет на лету делать подмену символа при чтении строки (METHOD ToValue( cStr )), т.е.
Запустил Demo_timer.exe и сразу закрыл. Начало файла Demo_timer.ini стало таким:
Особенность работы с TIniData еще в том, что (по мне) ini, cfg файлы надо набирать руками, как настроечные, тогда коменты в строках "; ..." с "=" будут сохранены (с "#" остается как было, так работают ф-ии). Если создать ini, cfg программно и потом внести коменты, то эти файлы можно класть в ресурсы, как базовые настройки, менять некоторые настройки в них (привязка к клиенту) можно из обычных файлов ini, cfg у конкретного клиента, сделав чтение и слияние с данными oIni из ресурсов. Еще всегда есть возможность обработки файла своими средствами, прочитав в буффер cBuff := hb_memoRead(cFile), обработать как вам надо, ... и потом формировать секции и ключи для работы в переменной oCnf := TIniData():New( , .T., lUtf8, , cBuff ):Read() Для программного формирования файлов с комментами, надо для символов комментариев использовать другие данные, отличные от тех, которые исп. родные ф-ии hb при чтении файлов, если это надо PS. запись в ini [HELP] Тут должна быть информация #HELP всегда можно заменить на [HELP] Rem = Тут должна быть информация ;HELP или [HELP] Help1 = Тут должна быть информация Help2 = ... ... или [HELP] Help = {"Тут должна быть информация", "#HELP", ...} ; тут текст коммента
Отправлено: 21.12.24 14:29. Заголовок: Сергей, спасибо за д..
Сергей, спасибо за детальное пояснение! Стратегия работы с ini-файлами у меня практически такая же. Попробую все перевести на TiniData. Тут возможностей больше. Спасибо!
А нельзя в методе Read( cIniNew ) CLASS TIniData переопределить символ '#' У меня в ini есть куча строк типа #WIDTH =550;550 К примеру #WIDTH использую как ключевое слово, а не параметр.
Отправлено: 21.12.24 15:40. Заголовок: krutoff Для информа..
krutoff Для информации, вариант исп. в oHmgData() контейнере псевдо методов (блоки кода) и выполнение их методом :Do(...) Это кусочек кода из примера "Testing DEMO Odbc/Firebird" (в hmg его нет)Скрытый текст
*----------------------------------------------------------------------------* FUNCTION oFDB_Connect(cFdb, cUsr, cPsw, cErr, lNoDbf) *----------------------------------------------------------------------------* LOCAL oac := App.Cargo LOCAL o := oHmgData(), cPref, cMsg, cKod, nCnt LOCAL cSql, aRec, cTbl, aFld, cFld, nFld, aStru, oField
STATIC s_oFdb ; IF s_oFdb == NIL ; s_oFDB := oHmgData() ; ENDIF // регистация баз в работе IF Empty(s_oFdb:Get(cFdb)) ; s_oFdb:Set(cFdb, hb_FNameName(cFdb)) ENDIF
o:GetTable := {|aVal| // get table R_CODE, R_TABL Local oo := ATail(aVal) // Self Local xTabl := aVal[1] // номер\имя таблицы Local oRet := oHmgData() Local nOldA, cAls Local cTabl, nTabl, lTabl, nKod, nTbl oRet:R_CODE := "" oRet:R_TABLE := "" oRet:N_TABLE := 0 oRet:N_COUNT := 0 IF !oo:lConnect ; Return oRet ENDIF cAls := oo:cAlsTabl + "_TMP" nOldA := Select() USE ( oo:cDbfTabl ) ALIAS ( cAls ) NEW SHARED GO TOP nKod := FieldPos("R_CODE" ) nTbl := FieldPos("R_TABLE") IF IsNumeric(xTabl) ; nTabl := xTabl ELSEIF IsChar(xTabl) ; xTabl := upper(alltrim(xTabl)) ENDIF DO WHILE !EOF() lTabl := .F. IF IsNumeric(xTabl) lTabl := Val(FieldGet(nKod)) == nTabl ELSEIF IsChar(xTabl) cTabl := alltrim(FieldGet(nTbl)) lTabl := upper(cTabl) == xTabl ENDIF IF lTabl oRet:R_CODE := FieldGet(nKod) oRet:R_TABLE := upper(alltrim(FieldGet(nTbl))) oRet:N_COUNT := FieldGet(FieldPos("R_COUNT")) oRet:N_TABLE := RecNo() EXIT ENDIF SKIP ENDDO USE dbSelectArea(nOldA) Return oRet }
o:GetTableStru := {|aVal| // get table structure Local oo := ATail(aVal) // Self Local xTabl := aVal[1] // номер\имя таблицы Local nOldA, cAls, nPos Local cFld, cTyp, nLen, nDec, cNam Local oTbl := oo:Do("GetTable", xTabl) oTbl:A_STRU := {} oTbl:O_FIELD := oHmgData() IF Empty(oTbl:N_TABLE) ; Return oTbl ENDIF nPos := 0 cAls := oo:cAlsStru + "_TMP" nOldA := Select() USE ( oo:cDbfStru ) ALIAS ( cAls ) NEW SHARED SET ORDER TO 1 SET SCOPE TO oTbl:R_CODE, oTbl:R_CODE GO TOP DO WHILE !EOF() nPos++ cFld := alltrim((cAls)->&("R_NAME")) cTyp := (cAls)->&("R_TYPE") nLen := (cAls)->&("R_LEN" ) nDec := (cAls)->&("R_DEC" ) cNam := alltrim((cAls)->&("F_NAME")) IF Empty(cFld) ; cFld := "R_" + hb_ntos(nPos) ENDIF IF cTyp == "U" ; cTyp := "C" ENDIF IF nLen == 0 ; nLen := 10 ENDIF AAdd(oTbl:A_STRU, {cFld, cTyp, nLen, nDec, cNam}) oTbl:O_FIELD:Set(cNam, cFld) SKIP ENDDO USE dbSelectArea(nOldA) Return oTbl }
o:TablesCount := {|aVal| // count(*) in all tables Local oo := aVal[1] // Self Local nRow := 0, cTbl, lErr FOR EACH cTbl IN oo:aTables nRow := oo:Do('GetRowCount', cTbl) oo:oTables:Set(cTbl, nRow) lErr := nRow < 0 IF lErr ; EXIT ENDIF NEXT Return lErr }
o:GetRowCount := {|aVal| // count(*) in table Local nCnt := -1, oo := aVal[2] // Self Local cTbl := aVal[1] Local cSql := "SELECT COUNT(*) FROM " IF oo:lConnect .and. ! oo:lError oo:lError := .F. oo:lErrorSql := .F. oo:oConnect:SetSql( cSql + cTbl ) IF oo:oConnect:Open() nCnt := 0 IF HB_ISARRAY(oo:oConnect:aRecordset) .and. ; Len(oo:oConnect:aRecordset) > 0 nCnt := oo:oConnect:aRecordset[1][1] ENDIF ELSE oo:lError := .T. oo:lErrorSql := .T. ENDIF oo:oConnect:Close() IF oo:lError ; oo:Do('ErrorAlert') ENDIF ENDIF Return nCnt }
o:RowSelect := {|aVal| Local oo := ATail(aVal) // Self Local cSql := aVal[1] // 'SELECT ...' Local cTmp := upper(cSql) Local oRet := oHmgData() Local aRec, oFld, nTmp, cTbl IF ( nTmp := At(" FROM ", cTmp) ) > 0 cTmp := ltrim(subs(cTmp, nTmp + 6)) cTbl := left(cTmp, At(" ", cTmp) - 1) ELSE cTbl := cSql cSql := "SELECT * FROM " + cSql ENDIF oRet:cTbl := cTbl oRet:cSql := cSql oRet:lErr := !oo:lConnect oRet:aArr := {} oRet:aFld := {} IF oo:lConnect .and. !oo:lError oo:lError := .F. oo:lErrorSql := .F. oo:oConnect:SetSql( cSql ) IF oo:oConnect:Open() FOR EACH oFld IN oo:oConnect:Fields AAdd( oRet:aFld, oFld:FieldName ) NEXT FOR EACH aRec IN oo:oConnect:aRecordset AAdd( oRet:aArr, AClone(aRec) ) NEXT ELSE oo:lError := .T. oo:lErrorSql := .T. ENDIF oo:oConnect:Close() IF oo:lError oRet:lErr := .T. oRet:aFld := {} oRet:aArr := {} oo:Do('ErrorAlert') ENDIF ENDIF Return oRet }
o:GetSqlType := {|aVal| // sql type -> dbf type Local nDataType := aVal[1] Local cType := "U" SWITCH nDataType CASE 1 // SQL_CHAR CASE -8 // SQL_WCHAR CASE -9 // SQL_WVARCHAR CASE 12 // SQL_VARCHAR cType := 'C' EXIT CASE 2 // SQL_NUMERIC CASE 3 // SQL_DECIMAL CASE 4 // SQL_INTEGER CASE 5 // SQL_SMALLINT CASE 6 // SQL_FLOAT CASE 7 // SQL_REAL CASE 8 // SQL_DOUBLE CASE -5 // SQL_BIGINT CASE -6 // SQL_TINYINT cType := 'N' EXIT CASE 9 // SQL_DATE CASE 91 // SQL_TYPE_DATE cType := 'D' EXIT CASE 11 // SQL_TIMESTAMP CASE 93 // SQL_TYPE_TIMESTAMP cType := 'T' EXIT CASE -4 // SQL_BLOB cType := 'B' EXIT ENDSWITCH Return cType } // --------------------------------------------------
// Tables list cSql := 'SELECT rdb$relation_name FROM rdb$relations ' + ; 'WHERE rdb$view_blr IS null AND ' + ; '(rdb$system_flag IS null OR rdb$system_flag = 0) ORDER BY 1'
o:oConnect:SetSql( cSql ) IF o:oConnect:Open() FOR EACH aRec IN o:oConnect:aRecordset AAdd( o:aTables, alltrim( aRec[1] ) ) NEXT ELSE o:lError := .T. o:lErrorSql := .T. ENDIF o:oConnect:Close()
IF o:lError // ERROR cMsg := o:cError IF o:lErrorSql cMsg += CRLF + o:oConnect:SQLErrorMessage() cMsg += CRLF + o:oConnect:cSQL ENDIF AlertStop( cMsg,,,64 ) RETURN o ENDIF
// Tables structure cSql := 'SELECT FIRST 1 * FROM ' FOR EACH cTbl IN o:aTables o:oConnect:SetSql( cSql + cTbl ) IF o:oConnect:Open() aStru := {} FOR EACH oField IN o:oConnect:Fields aFld := array(7) aFld[1] := oField:FieldName aFld[2] := o:Do('GetSqlType', oField:DataType) aFld[3] := oField:DataSize aFld[4] := oField:DataDecs aFld[5] := oField:AllowNull aFld[6] := oField:DataSize aFld[7] := oField:DataType IF aFld[2] == "N" .and. aFld[3] > 18 aFld[3] := 18 ELSEIF aFld[2] == "B" // BLOB or M aFld[3] := 10 ELSEIF aFld[2] == "U" .and. aFld[3] > 999 // ??? aFld[3] := 10 ENDIF AAdd( aStru, aFld ) NEXT o:oFields:Set(cTbl, aStru) ELSE o:lError := .T. o:lErrorSql := .T. ENDIF o:oConnect:Close()
IF o:lError ; EXIT // ERROR ENDIF NEXT
IF o:lError // ERROR cMsg := o:cError IF o:lErrorSql cMsg += CRLF + o:oConnect:SQLErrorMessage() cMsg += CRLF + o:oConnect:cSQL ENDIF AlertStop( cMsg,,,64 ) RETURN o ENDIF
// create a list of Tables and Structures IF !hb_FileExists(o:cDbfTabl+oac:ExtDbf) .or. !hb_FileExists(o:cDbfStru+oac:ExtDbf) hb_FileDelete(o:cDbfTabl+".*") hb_FileDelete(o:cDbfStru+".*") dbCreate( o:cDbfStru, o:aDbfStru, , .T., o:cAlsStru ) dbCreate( o:cDbfTabl, o:aDbfTabl, , .T., o:cAlsTabl ) FOR EACH cTbl IN o:aTables cKod := StrZero(hb_enumindex(cTbl), 4) nCnt := o:oTables:Get(cTbl, 0) DbSelectArea( o:cAlsTabl ) // Record Tables list dbAppend() AEval({cKod, cTbl, nCnt}, {|x,n| FieldPut(n, x) }) DO EVENTS DbSelectArea( o:cAlsStru ) // Record Structure nFld := FieldPos("F_NAME") FOR EACH aFld IN o:oFields:Get(cTbl) dbAppend() FieldPut(1, cKod) AEval(aFld, {|x,n| FieldPut(n+2, x) }) cFld := Alltrim( FieldGet( nFld ) ) IF Len( cFld ) > 10 // имя в F_NAME > 10 dbDelete() ELSE FieldPut( nFld-1, cFld) ENDIF DO EVENTS NEXT NEXT DbSelectArea( o:cAlsStru ) INDEX ON &("R_CODE") TAG "CODE" GO TOP dbCloseArea() DO EVENTS DbSelectArea(o:cAlsTabl) INDEX ON &("R_CODE") TAG "CODE" GO TOP dbCloseArea() DO EVENTS ENDIF
USE ( o:cDbfStru ) ALIAS ( o:cAlsStru ) NEW SHARED IF OrdCount() == 0 INDEX ON &("R_CODE") TAG "CODE" ENDIF OrdSetFocus(1) GO TOP
USE ( o:cDbfTabl ) ALIAS ( o:cAlsTabl ) NEW SHARED IF OrdCount() == 0 INDEX ON &("R_CODE") TAG "CODE" ENDIF OrdSetFocus(1) GO TOP
RETURN o
для более полного понимания исп. объекта-контейнера Использование Скрытый текст
*----------------------------------------------------------------------------* FUNCTION Main(cFile, cUser, cPass, cBclr) *----------------------------------------------------------------------------* LOCAL oTsb1, oTsb2, oac, owc, o LOCAL oFdb, cFdb, cUsr, cPsw, cBrw LOCAL oBrw1, oBrw2, nCnt1, nCnt2, nHtb LOCAL nY, nX, nH, nW, nG, oDlu, lFile LOCAL aBColor := {184, 107, 228}, aBClr LOCAL cForm := "wMain"
IF !Find_In_Memory("Firebird Guardian") AlertStop( "ERROR!;Firebird program not running!;Exit program",,,64 ) QUIT ENDIF
//AltD(1) // база для работы Default oac := App.Cargo cFdb := ".\FDB\Full_Receipt.fdb" cUsr := "SYSDBA" cPsw := "masterkey" oDlu := App.Object
Отправлено: 21.12.24 16:17. Заголовок: krutoff пишет А нель..
krutoff пишет
цитата:
А нельзя в методе Read( cIniNew ) CLASS TIniData переопределить символ '#'
"#" если правильно помню, так комменты, отрабатывают в hb функциях. Отдельно читаю файл от начала и с конца, для получения данных в переменных :cCommentBegin, :cCommentEnd и сам "#" не отрабатываю у себя ключи иногда делаю с "_", ваш вариант ~ так сделать _WIDTH =550,550 еще часто ключи делаю 001 = /spacetv. 002 = /ott. 003 = /ott- 004 = /myott. 005 = /mytv. 006 = /tvmedia2. 007 = /hlss.goodgame. 008 = /cdn1. 009 = /cdn01. 010 = /variant.m3u8 011 = /dmitry- 012 = /zabava- 013 = /okkotv- 014 = /cloud06. 015 = /viacomitalytest- 016 = /autopilot. 017 = /sc. ... PS. Если сейчас значение по ключу получаете через ф-ю, то можно сделать подмену (в ini иметь _Width = ...), а в ф-ии ... IF left(cKey, 1) == "#" ; cKey := "_"+subs(cKey, 2) // или простой ключ иметь cKey := subs(cKey, 2) в ini ENDIF Return App.Cargo:oIni:Get(cKey, xDef)
Отправлено: 22.12.24 21:38. Заголовок: krutoff Собрал прим..
krutoff Собрал пример (выше код был кусочек) с посл. версией hmg, смотрите (кому интересно) Тут (вместе с Firebird) https://TransFiles.ru/ki7ys ставлю firebird C:\Firebird_2_5 запуск сервера с иконки Target: C:\Firebird_2_5\bin\fbguard.exe -a Start in: C:\Firebird_2_5\bin пример в Advanced\_FDB_odbc demo_run.bat запускает demo.exe на несколько fdb
Добрый день! Сделал альтернативный упрощенный вариант TiniData "для себя". Сергей, если Вам будет интересно, то, мне кажется, METHOD Read и Write можно скомбинировать с этим кодом. Да, я для себя VAR cNote2 ставлю '~'
Я в ini-файле формирую структуру, названия, Tooltip для полей файла. И тут же ставлю ключи для формирования экранных и печатных форм для файла. Так сложилось, что у меня там есть одинаковые ключевые слова #LABEL, #BUTTON, #TABLE, #TABLA, #RTF и т.п.
#define _METHOD METHOD /////////////////////////////////////////////////////////////////////////////// CLASS TIniData INHERIT THmgData ///////////////////////////////////////////////////////////////////////////////
VAR cIni AS STRING INIT "" VAR cData AS STRING INIT "" VAR cBOM AS STRING INIT hb_utf8Chr( 0xFEFF ) VAR lUtf AS LOGICAL INIT .F. VAR lUtf8 AS LOGICAL INIT .F. VAR cSplit AS STRING INIT "=" VAR cNote AS STRING INIT ";" VAR cNote2 AS STRING INIT "#" VAR lNote AS LOGICAL INIT .T.
/* we'll read the whole file, then we'll break it in lines. */ cData := Space(hb_vfSize( hIni )) nI := hb_vfRead(hIni, @cData, hb_BLen(cData)) cData := hb_BLeft(cData, nI) hb_vfClose( hIni )
::lUtf8 := Left(::cData, Len(::cBOM)) == ::cBOM cLine := cSec := ''; nI := 0 FOR EACH cData IN hb_ATokens(cData, .T.) // .T. - EOL IF EMPTY(cLine := RTrim(IIF(! ::lUtf .and. ::lUtf8, hb_UTF8ToStr(cData), cData))) LOOP // Skip void lines ENDIF
IF EMPTY(cSec); oSec := Self; ENDIF // handling eventual comments IF ! EMPTY(aKeyVal := hb_regexSplit(reNote, cLine)) IF EMPTY(cLine := AllTrim(aKeyVal[1])) // Comment lines - NOTE IF ::lNote; oSec:Set(_NOTE_+xChar(++nI), RTrim(aKeyVal[2])); ENDIF IF LEN(aKeyVal) > 2; cLine := RTrim(aKeyVal[3]) // Строка после символа cNote2 ELSE; LOOP ENDIF ENDIF ENDIF
// Is it a NEW section ? IF ! EMPTY(aKeyVal := hb_regex(reSection, cLine)) IF ! EMPTY(cLine := AllTrim(aKeyVal[2])) IF ! EMPTY(cSec); ::Set(cSec, oSec); nI := 0; ENDIF cSec := Upper(cLine); oSec := oHmgData() ENDIF // Is it a valid key ELSEIF Len( aKeyVal := hb_regexSplit(reSplit, cLine,,,1)) == 1 // Dont't Value - TEXT IF ::lNote; oSec:Set(_TEXT_+xChar(++nI), RTrim(aKeyVal[1])); ENDIF ELSE IF !EMPTY(oSec:Get(cKey := AllTrim(aKeyVal[1]))) // Уже есть такой ключ cKey += xChar(++nI) ENDIF oSec:Set(cKey, RTrim(aKeyVal[2])) ENDIF NEXT IF ! EMPTY(cSec); ::Set(cSec, oSec); ENDIF
// Установим флаг "case match" для массива Hash (учитывать ли Case символа: большой/маленький ) hb_hCaseMatch( ::a_a_Key, .T. ) RETURN Self
METHOD ToData( cKey, cVal ) CLASS TIniData LOCAL lUtf8 := ! ::lUtf .and. ::lUtf8
ENDIF ... для h_windowsMdi.prg FUNCTION _DefineChildMDIWindow ( FormName, x, y, w, h, nominimize, nomaximize, ; ... LOCAL FormHandle , ChildIndex, op ... IF FormName == NIL FormName := _HMG_TempWindowName
Отправлено: 30.12.24 16:07. Заголовок: В методе METHOD Read..
В методе METHOD Read я соединил функции hb_iniReadStr, hb_iniFileLow и hb_iniStringLow. В блоке hb_iniStringLow добавил доп. обработку и вставку комментов и строк без разделителя. METHOD Write тоже обрабатывает все ваши переменные a_a_Key класа THmgData без переходных hash-переменных. Так, что если заинтересует, предлагаю применить. В последней версии блок проверки на ReadOnly файл переставил в METHOD Write.
Отправлено: 30.12.24 17:59. Заголовок: krutoff пишет Так, ч..
krutoff пишет
цитата:
Так, что если заинтересует, предлагаю применить
Это скорее к Григорию. Со времен clipper в ini файлах использовал в стандарте # и ; как комментарии, даже :Write() не использовал - хватало. Ключ, начинающийся с #KeyName, использовал в VO, отказался быстро, т.к. исп. его, ключ, как имена переменных (правила). То что есть мне хватает выше головы, причем, файлы ini\cfg у меня всегда в Utf8, для других данных сохранение может быть в json (htm, xml, js) или hb_valtoexp(...) и могут быть подчитаны при чтении ini\cfg блоком кода или ф-ей типа (в ini\cfg) xVal1 := {|| myRead(".\wrk\имя файла") }
Все даты в формате GMT
3 час. Хитов сегодня: 159
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет