Вопрос
Как работать (читать и записывать) с ini-файлами?
Ответ
ini-файл или файл с расширением из 3 букв "INI" (от слов initialization file - файл инициализации) - это обычный текстовый файл, который имеет специфическое содержание. Выглядит его содержимое примерно так:
// Комментарий
[Имя_раздела_1]
Имя_реквизита_1 = значение реквизита
Имя_реквизита_2 = значение реквизита
// Комментарий
[Имя_раздела_2]
Имя_реквизита_1 = значение реквизита
Имя_реквизита_2 = значение реквизита
Т.е. в квадратных скобках указывается имя раздела (другое название - "секция"), затем идет список реквизитов (другое название - "ключ") этого раздела и их значение. Количество разделов и количество реквизитов внутри раздела не ограничено.
Желательно давать имена разделам и реквизитам в одно слово из латинских букв без пробелов и спец.символов. Но это скорее перестраховка. Если для Вас удобнее воспринимать имя раздела или реквизита с пробелом (в два слова) - пишите с пробелом!
В качестве значения реквизита принимается вся строка от первого отличного от пробела символа после символа равенства до окончания строки, включая пробелы. Т.е. здесь для задания текстовой строки использовать кавычки не надо. Предполагается, что все значения имеют только и исключительно символьный тип.
Символом комментария обычно служит две наклонные черты подряд. Хотя, в принципе, можно использовать абсолютно любой символ (вот только пробел нельзя ). Для используемых ниже API-функций важен факт совпадения искомого имени начиная с первого символа. Достаточно того, чтобы первый символ не совпадал с искомым.
В принципе, можно прочитать такой файл из FoxPro функциями чтения текстовых файлов (FOPEN(), FREAD(), FileToStr() и т.п.) и сделать его разбор. "Но есть способ лучше" (с). Существуют специальные API-функции предназначенные для чтения и записи INI-файлов.
GetPrivateProfileString - считывает значение указанного реквизита из указанного раздела.
WritePrivateProfileString - записывает значение указанного реквизита в указанный раздел указанного ini-файла. Если указанного раздела или реквизита не существует, то он будет создан. Если указанного ini-файла не существует, то он также будет создан.
Выглядит это примерно так:
Запись значения в ini-файл
Declare Integer WritePrivateProfileString In Win32API As WritePrivStr ;
String cSection, ; && имя раздела
String cKey, ; && имя реквизита
String cValue, ; && значение реквизита
String posfile && имя ini-файла с полным путем доступа
LOCAL lnError
lnError = WritePrivStr("TestSection", ;
"TestKey", ;
"TestValue", ;
Fullpath("test.ini"))
IF lnError<>1
* Запись в ini-файл не удалась. Произошла ошибка
ELSE
* Смотрим, что получилось во вновь созданном файле
MODIFY FILE test.ini
ENDIF
Чтение значения из ini-файл
Declare Integer GetPrivateProfileString In Win32API As GetPrivStr ;
String cSection, ; && Имя раздела
String cKey, ; && Имя реквизита
String cDefault, ; && Значение по умолчанию, если нет указанного раздела или реквизита
String @cBuffer, ; && Собственно считанное значение реквизита
Integer nBufferSize, ; && Максимальное количество символов в считанном реквизите
String posfile && имя ini-файла с полным путем доступа
LOCAL lcBuffer, lnBuffer
lcBuffer = SPACE(2000)
lnBuffer = GetPrivStr("TestSection", ;
"TestKey", ;
"Нет значения", ;
@lcBuffer, ;
LEN(m.lcBuffer), ;
Fullpath("test.ini"))
IF m.lnBuffer = 0
* Ничего не прочитали
ELSE
* Прочитанное значение храниться в первых m.lnBuffer символах переменной m.lcBuffer
?LEFT(m.lcBuffer,m.lnBuffer)
* Общая длина переменной m.lcBuffer по прежнему 2000 символов,
* но символ m.lnBuffer+1 имеет ASCII-код равный 0, а все прочие - пробелы
ENDIF
Удаление раздела или реквизита
Удаление - это запись не определенного значения
* Удаление только одного реквизита TestKey в разделе TestSection
LOCAL lnError
lnError = WritePrivStr("TestSection", ;
"TestKey", ;
NULL, ;
Fullpath("test.ini"))
IF lnError<>1
* Запись в ini-файл не удалась. Произошла ошибка
ELSE
* Смотрим, что получилось
MODIFY FILE test.ini
ENDIF
* Удаление всего раздела TestSection со всеми реквизитами
LOCAL lnError
lnError = WritePrivStr("TestSection", ;
NULL, ;
NULL, ;
Fullpath("test.ini"))
IF lnError<>1
* Запись в ini-файл не удалась. Произошла ошибка
ELSE
* Смотрим, что получилось
MODIFY FILE test.ini
ENDIF
Определение структуры ini-файла
Если структура ini-файла, т.е. имена секций и ключей, заранее не известны, то получить структуру ini-файла можно также используя функцию GetPrivateProfileString указав вместо имени секции или ключа число 0.
В этом случае в качестве возвращаемого значения Вы получите либо список имен секций (если указать 0 первым параметром), либо список имен ключей указанной секции (если указать 0 вторым параметром). Имена будут разделены символом Chr(0)
* Список секций ini-файла
LOCAL lcBuffer, lnBuffer
lcBuffer = SPACE(2000)
lnBuffer = GetPrivStr(0, ;
0, ;
"", ;
@lcBuffer, ;
LEN(m.lcBuffer), ;
Fullpath("test.ini"))
IF m.lnBuffer > 0
m.lcBuffer = CHR(0) + LEFT(m.lcBuffer,m.lnBuffer)
LOCAL lnI, lnFromPos, lnToPos
FOR m.lnI = 1 TO OCCURS(CHR(0),m.lcBuffer)-1
lnFromPos = AT(CHR(0),m.lcBuffer,m.lnI)
lnToPos = AT(CHR(0),m.lcBuffer,m.lnI+1)
?SubStr(m.lcBuffer,m.lnFromPos+1,m.lnToPos-m.lnFromPos-1)
ENDFOR
ENDIF
* Аналогично получаем список ключей секции TestSection
LOCAL lcBuffer, lnBuffer
lcBuffer = SPACE(2000)
lnBuffer = GetPrivStr("TestSection", ;
0, ;
"", ;
@lcBuffer, ;
LEN(m.lcBuffer), ;
Fullpath("test.ini"))
IF m.lnBuffer > 0
m.lcBuffer = CHR(0) + LEFT(m.lcBuffer,m.lnBuffer)
LOCAL lnI, lnFromPos, lnToPos
FOR m.lnI = 1 TO OCCURS(CHR(0),m.lcBuffer)-1
lnFromPos = AT(CHR(0),m.lcBuffer,m.lnI)
lnToPos = AT(CHR(0),m.lcBuffer,m.lnI+1)
?SubStr(m.lcBuffer,m.lnFromPos+1,m.lnToPos-m.lnFromPos-1)
ENDFOR
ENDIF
Разумеется, объявлять API-функции каждый раз перед записью или чтением значений нет необходимости. Достаточно объявить эти функции один раз в стартовом файле.
Также пример работы с INI-файлами можно посмотреть в классе, поставляемом вместе с FoxPro
MODIFY CLASS oldinireg OF HOME()+'FFC\registry.vcx'
Хотя в новейшей идеологии Microsoft предполагается, что вместо ini-файлов следует использовать запись/чтение в системный реестр Windows.
Применительно к FoxPro следует рассмотреть возможность хранения дополнительных настроек в обычном файле DBF, поскольку FoxPro предназначен, прежде всего, именно для работы с файлами DBF.