Работа с типизированным файлом


#1

Имеется задание.

Создать и заполнить файл F с компонентами целочисленного типа. Отобразить содержимое исходного файла. Вычислить сумму компонент файла, имеющих четное значение. Увеличить на значение этой суммы отрицательные компоненты второй половины файла. Отобразить полученный файл. Удалить из файла компоненту с минимальным значением, файл сжать. Отобразить полученный файл.

Понятно, что Паскаль обладает набором средств, позволяющим решить эту задачу, как и любую другую. Но вот насколько они удобны?

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

begin
  var f:file of integer;
  Rewrite(f,'F.bin');
  Randomize(2); // отладочное, чтобы числа не менялись
  var n:=20; // число компонент
  loop n do
    Write(f,Random(-50,50));
  f.Close; // файл создан
  
end.

Вывод компонент файла. Снова открываем файл и в цикле читаем каждую запись и выводим её. Продолжаем до конца файла. В принципе, можно одновременно получить и сумму компонент, но попробуем быть последовательными в пошаговом решении.

  Reset(f,'F.bin');
  while not f.Eof do Print(f.Read);
  Writeln;

Вычисляем сумму. Для этого нужно снова стать на начало файла и просмотреть его, отбирая компоненты по критерию.

  var s:=0; var m:integer;
  f.Reset;
  while not f.Eof do begin
    m:=f.Read;
    if m.IsEven then s+=m
    end;

Теперь просматриваем вторую половину файла и отрицательные компоненты увеличиваем на величину найденной суммы. Неприятная операция, потому что Н.Вирт (и разработчики этого Паскаля) не предусмотрели специальных средств корректировки записей в файле. Поэтому нужно прочитать значение, вернуть указатель на предыдущую запись и записать значение.

  f.Seek(n div 2); // указатель на вторую половину файла
  while not f.Eof do begin
    m:=f.Read;
    if m<0 then begin
      f.Seek(f.FilePos-1); // это просто перл (((
      Write(f,m+s)
      end
    end;
  // вывод содержимого файла
  f.Reset;
  while not f.Eof do Print(f.Read);
  Writeln;

Ищем номер записи с минимальной компонентой. Опять с начала файла, конечно же.

  // поиск минимума
  f.Reset;
  var (pmin,min):=(0,f.Read);
  while not f.Eof do begin
    m:=f.Read;
    if m<min then
      (pmin,min):=(f.FilePos-1,m)  // занятное место
    end;

Здесь возможна частая ошибка, потому что Seek считает, будто компоненты нумеруют от нуля, а FilePos с завидным упорством отсчитывает их от единицы.

Ну и последний рубеж - удаление минимума. Занятие еще то. Внешне все понятно. Местоположение минимума найдено, оно в pmin. Теперь нам надо стать на следующую за pmin компоненту и переписать её на одну компоненту к началу файла. И поступить так для всех компонент. После записи последней компоненты на предпоследнее место надо усечь файл и закрыть его. А потом вновь открыть и вывести. Эту часть я оставляю желающим, поскольку эти f.Seek и анализ f.Eof меня несколько расстроили.

Надеюсь, что если кто-то пожелает сделать задачу полностью и проанализирует решение в целом, тоже ощутит желание кое-что в такой работе с файлами "подкрутить. Тогда и обменяемся идеями.


#2

Ну тут можно воспользоваться последовательностями. Конечно отсутствие функции Peek это несколько грустно, но удалять 1 элемент из файла не загружая всё его содержимое в память - в любом случае плохая затея… Хотя опять же, если с последовательностями заморочиться - можно наверное и сделать без загрузки всего файла, то есть чтоб делалось всё как у вас сейчас, но только по феншую :wink:.


#3

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


#4

Вот как… Кстати вы ещё собирались рассказать


#5

Не совсем так. Я лишь задал вопрос, но ответа не получил пока.


#6

Я немного не правильно выразился в той теме, сейчас перечитал и понял это… Я имел в виду ваши мысли по этому поводу с конкретным примером, но моё “c” выглядело больше как “тему с” чем “давайте в отдельную тему ваши идеи с”. Или моего ответа не достаточно?))


#7

Увы, недостаточно. Для себя я могу просто написать класс с набором нужных методов и скачи оно все конём. Мне важно знать мнение разработчика. Если разработчики считают, что в этой части в языке все чудесно и ничего добавлять не нужно, для чего тратить время, свое и чужое, на все эти гейзеры идей?