Решение практических задач на Паскале. Выпуск 3


Чтение информации из файла. ВАРИАНТ 2

В первом выпуске этой рассылки был описан способ хранения во внешнем файле исходных данных, необходимых для работы программы. Сейчас будет представлен второй способ, более удобный для использования, особенно в том случае, когда программу нужно передать для работы кому-нибудь. Например, ... хм... , ну хоть кому-нибудь :))

Отмечу, что я собираюсь описать еще два способа. Один очень близок к данному, но удобнее, если исходных данных много и их можно объединить в смысловые группы. Это будет вариант 3.

Имеется и четвертый метод хранения исходных данных для программы. Он аналогичен тому, что применяется в системе Windows. Это INI-файлы. Эти файлы, как правило, имеют имя, совпадающее с именем программы их использующей, и расширение .INI.

Это обычные текстовые файлы. В таких файлах информация хранится в виде строк формата

Name=Value
Для работы с ними в Delphi имеется специальный класс TIniFile, а в этом классе - удобные методы для сохранения и поиска информации. Я напишу объект для работы с таким файлом на Паскале, но позднее ... :))

А сейчас числа, как и в предыдущем варианте, располагаются в определенном порядке, но перед каждым запишем, что это за величина

Нижняя граница =0.5   Правая: 100,   Шаг = 5.1    outFileName=  out.dat
Данные теперь записаны в виде строки и программе должно быть точно известно, что и на какой строке следует искать. Записывать данные в такой файл можно любым текстовым редактором. Информация перед значением параметра будет игнорироваться, так что можно писать все, что Вам нравится. Единственное условие: перед именем файла для вывода результатов должен стоять знак равенства "=".

Для упрощения анализа строки я написал процедуру для "выгрызания" чисел типа  Real из строковой переменной:

procedure FindReal(Source: String; VAR Destign: Real;
                        VAR NBeg, CodeOp: Integer);
Эта процедура в строке символов Source, начиная с позиции NBeg, пытается отыскать цепочку символов, которую можно преобразовать в число типа Real. Если такую цепочку удалось найти, то в переменную Destign заносится считанное значение, а код завершения операции CodeOp будет равен нулю. В противном случае CodeOp нулю не равен, а Destign сохранит значение, установленное "по умолчанию". После завершения работы процедуры NBeg указывает на первый символ, который не анализировался.

Ниже я покажу пример использования, а сейчас код:

procedure FindReal(Source: String; VAR Destign: Real;
                        VAR NBeg, CodeOp: Integer);
     CONST
                BegOfNumb: SET OF CHAR = ['0'..'9']; {Этими символами начинается число}
                NumbChars: SET OF CHAR = ['0'..'9','E','e','+','-','.']; {Эти символы могут
                         входить в состав числа. Любой символ, не входящий в группу,
                         воспринимается, как сигнал конца числа}

     VAR
        Buf_String: String;           {сюда занесем найденную цепочку символов}
        Buf_Real  : Real;              {в эту переменную преобразуем цепочку. Я 
                                     собираюсь использовать стандартную процедуру VAL, 
                                     а она портит число, если есть ошибки преобразования}
        len           : Integer;         {длина анализируемой строки}
     BEGIN
          	CodeOp:=-30;                { установим код ошибки "не ноль" }
             len:=LENGTH(Source);    { запомнили длину анализируемой строки }
        	if (NBeg<=0) then EXIT;  { таких значений NBeg быть не должно }
        	if NBeg>=len then EXIT;   { Все, больше анализировать нечего }
       Buf_String:='';
       While (NOT (Source[NBeg] in BegOfNumb))
               	AND (NBeg < len) do    INC(NBeg);
        if NBeg=len then EXIT;     { 	Все, дошли до конца строки, а цифр не нашли. 
        				 Выходим и код ошибки уже установлен }
        				 
        { Учтем, что числу может предшествовать знак, но только если это не 
          первый символ сторки. Этот символ включается в состав Buf_STRING }
        if NBeg>1 then
          if (Source[NBeg-1]='+') OR (Source[NBeg-1]='-')
                                          then DEC(NBeg,1); 
        { начало цепочки символов найдено, начинаем формировать Buf_STRING }
        While (Source[NBeg] in NumbChars) AND (NBeg<=len) do
        begin
                Buf_String:=Buf_String + Source[NBeg];
                INC(NBeg)
        end; 
        { Преобразование произведем с помощью процедуры VAL }
        Val(Buf_String, Buf_Real, CodeOp);
        { Если преобразовать удалось (CodeOp=0 в этом случае), то результат
          преобразования запишем в Destign }
        if CodeOp=0 then Destign:=Buf_Real
     END;
Для отыскания числа типа INTEGER можно написать аналогичную процедуру, но проще считать число в буферную переменную типа REAL, а затем преобразовать в число типа INTEGER стандартной процедурой ROUND.

Пример использования такой процедуры дается ниже. Предполагается, что процедура описана в модуле MyIO003.pas. Его и все остальные файлы можно скачать в виде архива по адресу solv003.zip. Исходные данные содержатся в файле Solv003.dat. Он содержит одну строку текста, такую, какая показана выше.

Главная программа Solv003.pas:

program Solv003;
uses CRT, MyIO003;

function ReadData(AFileName: String;
  var Left, Right, Step: Real;
  var AName: String): Boolean;
var
  ini: Text;
  buf: String;
  k, opCode: Integer;
begin
  ReadData:=False;
  Assign(ini, AFileName);
  {$I-} Reset(ini); {$I+}
  if IOResult <> 0 then begin
    WriteLn('Не могу открыть файл с исходными данными:');
    WriteLn(AFileName);
    Exit;
  end;
  ReadData := TRUE;
  Left := 0;  {Значение по умолчанию}
  Right:= 1;
  Step := 0.1;
  AName:= 'out003.dat';
  ReadLn(ini, buf);
  Close(ini); {Больше он нам нужен}
  k:=1;
  FindReal(buf, Left,  k, opCode); if OpCode <> 0 then exit;
  FindReal(buf, Right, k, opCode); if OpCode <> 0 then exit;
  FindReal(buf, Step,  k, opCode); if OpCode <> 0 then exit;
  buf:=copy(buf, k, 255);
  if pos('=', buf) = 0 then Exit; {Значение по умолчанию}
  buf:=Copy(buf, pos('=', buf)+1, 255);
  {Возможно, что содержатся лишние пробелы. Удалим ВСЕ!}
  while pos(' ', buf) <> 0 do delete(buf, pos(' ', buf), 1);
  if buf <> '' then AName:=buf; {Если что-то осталось}
end;

var
   a, b, h, res: Real;
   OutFileName: String;
   OutFile: Text;
BEGIN
  ClrScr;
  if NOT ReadData('solv003.dat', a, b, h, OutFileName) then Halt(1);
  WriteLn('>>>> ',a:10:2,'  ',b:10:2,'   ',h:10:2,'  ',OutFileNAme);
  Assign(OutFile, OutFileName);
  {$I-} Rewrite(OutFile); {$I+}
  if IOResult <> 0 then begin
    WriteLn('Странно, но не могу создать файл: ', OutFileName); Halt(1)
  end;
  while a < b do begin
     res:=sin(a);
     WriteLn(a:10:2,'    ',res:10:7);          {Выводим на экран}
     WriteLn(OutFile, a:10:2,'    ',res:10:7); {Выводим в файл}
     a:=a+h;
     if a > b then a:=b
  end;
  Flush(OutFile);
  Close(OutFile);
END.

Как видите, для чтения данных организована "специательная" процедура, выделенная курсивом. Больше ничего не скажу. Попробуйте использовать и задайте вопросы, если что непонятно

Свои вопросы и предложения присылайте Борису

Мы приглашаем Вас и Ваших друзей к сотрудничеству. Напишите, какая проблема Вас лично интересует - и мы постараемся помочь Вам. Поделитесь со всеми, если Вам удастся найти красивое решение. Присылайте свои программы, и если они хороши, то опубликуем их с обязательным указанием Вашего авторства.

По всем вопросам можно писать либо в Гостевую книгу нашего сайта на www.turbopascal.tk, либо мне, автору этого выпуска, © Сурину Борису: surin_bp@mail.ru, ICQ: 320096696. Постараюсь ответить на все вопросы и учесть все разумные предложения

Рассылка поддерживается сайтом www.turbopascal.tk. При перепечатке ссылка на сайт обязательна

Hosted by uCoz