Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 27.06.2017, 13:59   #1
Ship_1
Форумчанин
 
Регистрация: 10.02.2014
Сообщений: 526
По умолчанию Динамический массив с динамическими массивами: есть ли возможность всё это записать в файл и читать из файла?

Здравствуйте!
У меня программа работает вот с такой структурой:
Код:
type
  TSegmOrient = (soHor, soVert);
  TFloatPoint = record
    X, Y: Real;
  end;
  TParPoint = array [0..1] of TFloatPoint;
  TPolyPoint = record
    QuarTan: integer;
    Point: TParPoint;
  end;
  TPolyPointArray = Array of TPolyPoint;
  TLineSegm = record
    Orient: TSegmOrient;
    Place: string;
    Points: TPolyPointArray;
    Length: real;
    Scale: real;
    PolyLineHandle: string;
  end;
  TLineSegmArray = Array of TLineSegm;
  TLineTrace = record
    StartPlace: string;
    EndPlace: string;
    PlaceHandle: string;
    StartPoint: TFloatPoint;
    HasStartPoint: boolean;
    Length: real;
    Segments: TLineSegmArray;
  end;
  TTraces = Array of TLineTrace;

var
  Traces: TTraces;
Но мне необходимо после работы данные сохранить в файл, а в дальнейшем загрузить из файла.
Догадываюсь, что string надо перевести в, например, string[255], чтобы записать его в файл. Но как записать динамический массив с динамическими массивами?
Всё, на что меня хватает - это
Код:
procedure TForm1.Button8Click(Sender: TObject);
var
  TraceFile: File of TLineTrace;
  FName: string;
  i: integer;
begin
   If SaveDialog1.Execute then
   begin
     FName:=SaveDialog1.FileName;
     AssignFile(TraceFile,FName);
     Rewrite(TraceFile);
     For i:=0 to Length(Traces)-1 do
     begin
       Write(TraceFile, Traces[i]);
     end;
     CloseFile(TraceFile);
   end;
end;
Но, конечно же, программа так работать не хочет:
Цитата:
Type 'TLineTrace' needs finalization - not allowed in file type
Может ли кто-нибудь доходчиво объяснить, как быть в таких случаях? Как сохранить и загрузить данные?
Ship_1 вне форума Ответить с цитированием
Старый 27.06.2017, 14:08   #2
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Пример
http://www.programmersforum.ru/showt...m+MemoryStream
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 27.06.2017, 14:15   #3
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

если вкратце, то по простому не получится.
нельзя задать File Of <ВашТип> и записать его.

и string здесь самая малая из проблем (хотя суть проблемы в том же, что и с массивами, string - это указатель)

если Вы попытаетесь записать TLineTrace, то в состав записи входит динамический массив Segments: TLineSegmArray;
а динамический массив в структуре представлен указателем на данные (на выделенную под массив область памяти).

поэтому, Вам нужно или отказываться от динамических массивов, или использовать свою структуру записи: в том месте, где нужно записать данные по указателю (String или динамический массив), в файл нужно писать
размер данных в файле, после - собственно сами данные.
ну и читать так же - прочитали в целочисленную переменную размер данных CurSize,
сделали SetLength(S, CurSize)
потом прочитали данные f.Read(s[1], CurSize);
и т.д.

в связи с тем, что у Вас динамические массивы вложены в динамические массивы, которые являются динамическими массивами - это будет достаточно заморочено.
но реально!


Цитата:
Сообщение от Аватар Посмотреть сообщение
вот. Это оно! В точку!!

Последний раз редактировалось Serge_Bliznykov; 27.06.2017 в 14:22.
Serge_Bliznykov вне форума Ответить с цитированием
Старый 27.06.2017, 14:22   #4
Ship_1
Форумчанин
 
Регистрация: 10.02.2014
Сообщений: 526
По умолчанию

Аватар, спасибо за пример, попробую его понять когда времени будет побольше.
Serge_Bliznykov, а какой при этом указывать тип файла? Вообще нетипизированный?
Ship_1 вне форума Ответить с цитированием
Старый 27.06.2017, 14:36   #5
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

Цитата:
Сообщение от Ship_1 Посмотреть сообщение
а какой при этом указывать тип файла? Вообще нетипизированный?
либо нетипизированный, либо file of byte;
но лучше не использовать old-school стиль через файловую переменную.
Используйте обёртки. Очень рекомендую - потомки от TStream, например, TFileStream.
Кстати, пример от Аватар как раз с TStream и работает.
Serge_Bliznykov вне форума Ответить с цитированием
Старый 28.06.2017, 09:26   #6
Ship_1
Форумчанин
 
Регистрация: 10.02.2014
Сообщений: 526
По умолчанию

По поводу примера в целом, вроде, понятно, но возникли вопросы:
1. Может кто-нибудь доходчиво объяснить что это за строка?
Код:
      if Len>0 then Stream.WriteBuffer(Pointer(m[i][j].X)^,Len*SizeOf(Real));
Условно догадываюсь, что при длине массива больше нуля (т.е. если он есть) в поток передаётся указатель на него и длина, умноженная на размер элемента. Но не слишком это мне понятно. Я так и не смог осознать суть работы с указателями. Объясните, пожалуйста, на пальцах что при этом происходит? Ведь при записи в нетипизированный файл тоже передаётся указатель в подобном случае? На что указывает указатель? Правильно ли я понимаю, что это указатель на ячейку памяти; память - пространство байт, в которое записывается массив, указатель указывает на первый байт данных? А при записи в файл происходит считывание с этого первого байта количество байт, переданных вторым параметром?
Второй вопрос: что-то поменяется, если будет не TStream, а TFileStream, или можно просто заменить одно на другое, больше ничего не меняя в коде, типа как в некоторых случаях ничего не изменится, если TStrings заменить на TStringList?
И третий: а зачем вообще нужен в подобных случаях TStream? Что с ним дальше делать?

Сохранение и загрузку на данный момент для своего случая сделал через циклы и TStringList:
Код:
procedure TForm1.Button8Click(Sender: TObject);
var
  FName: string;
  i,j,k: integer;
  OutStrL: TStringList;
begin
   If SaveDialog1.Execute then
   begin
     FName:=SaveDialog1.FileName;
     OutStrL:=TStringList.Create;
     For i:=0 to Length(Traces)-1 do
     begin
       OutStrL.Add('[Trace]');
       OutStrL.Add('  StartPlace: '+Traces[i].StartPlace);
       OutStrL.Add('  EndPlace: '+Traces[i].EndPlace);
       OutStrL.Add('  PlaceHandle: '+Traces[i].PlaceHandle);
       OutStrL.Add('  StartPoint: '+FloatToStr(Traces[i].StartPoint.X)+';'+FloatToStr(Traces[i].StartPoint.Y));
       if Traces[i].HasStartPoint then OutStrL.Add('  HasStartPoint: 1') else OutStrL.Add('  HasStartPoint: 0');
       OutStrL.Add('  Length: '+FloatToStr(Traces[i].Length));
       For j:=0 to Length(Traces[i].Segments)-1 do
       begin
         OutStrL.Add('  [Segment]');
         if Traces[i].Segments[j].Orient=soHor then
           OutStrL.Add('    Orient: soHor')
           else
           OutStrL.Add('    Orient: soVert');
         OutStrL.Add('    Place: '+Traces[i].Segments[j].Place);
         OutStrL.Add('    Length: '+FloatToStr(Traces[i].Segments[j].Length));
         OutStrL.Add('    Scale: '+FloatToStr(Traces[i].Segments[j].Scale));
         OutStrL.Add('    PolyLineHandle: '+Traces[i].Segments[j].PolyLineHandle);
         For k:=0 to Length(Traces[i].Segments[j].Points)-1 do
         begin
           OutStrL.Add('    [Point]');
           OutStrL.Add('      QuarTan: '+FloatToStr(Traces[i].Segments[j].Points[k].QuarTan));
           OutStrL.Add('      Point: '+FloatToStr(Traces[i].Segments[j].Points[k].Point.X)+';'+FloatToStr(Traces[i].Segments[j].Points[k].Point.Y));
         end;
       end;
     end;
     OutStrL.SaveToFile(FName);
     OutStrL.Free;
   end;
end;

function IsTrace(InpStrL: TStringList; Item: Integer): boolean;
begin
  If Item>=InpStrL.Count then
  begin
    Result:=false;
    exit;
  end;
  if InpStrL[Item]='[Trace]' then Result:=true else Result:=false;
end;

function IsSegment(InpStrL: TStringList; Item: Integer): boolean;
begin
  If Item>=InpStrL.Count then
  begin
    Result:=false;
    exit;
  end;
  if InpStrL[Item]='  [Segment]' then Result:=true else Result:=false;
end;

function IsPoint(InpStrL: TStringList; Item: Integer): boolean;
begin
  If Item>=InpStrL.Count then
  begin
    Result:=false;
    exit;
  end;
  if InpStrL[Item]='    [Point]' then Result:=true else Result:=false;
end;

procedure TForm1.Button9Click(Sender: TObject);
var
  FName: string;
  i,j,k: integer;
  OutStrL: TStringList;
  ItTrace, ItSegm, ItPoint: integer;
begin
  If OpenDialog1.Execute then
  begin
    FName:=OpenDialog1.FileName;
    OutStrL:=TStringList.Create;
    OutStrL.LoadFromFile(FName);
    i:=0;
    While IsTrace(OutStrL,i) do
    begin
      ItTrace:=AddTrace(Traces);
      Traces[ItTrace].StartPlace:=copy(OutStrL[i+1],14+1,Length(OutStrL[i+1])-14);
      Traces[ItTrace].EndPlace:=copy(OutStrL[i+2],12+1,Length(OutStrL[i+2])-12);
      Traces[ItTrace].PlaceHandle:=copy(OutStrL[i+3],15+1,Length(OutStrL[i+3])-15);
      Traces[ItTrace].StartPoint.X:=StrToFloat(copy(OutStrL[i+4],14+1,pos(';',OutStrL[i+4])-14-1));
      Traces[ItTrace].StartPoint.Y:=StrToFloat(copy(OutStrL[i+4],pos(';',OutStrL[i+4])+1,Length(OutStrL[i+4])-pos(';',OutStrL[i+4])));
      if copy(OutStrL[i+5],17+1,Length(OutStrL[i+5])-17)='1' then Traces[ItTrace].HasStartPoint:=true else Traces[ItTrace].HasStartPoint:=false;
      if Traces[ItTrace].HasStartPoint then Memo1.Lines.Add('HasStartPoint!');
      Traces[ItTrace].Length:=StrToFloat(copy(OutStrL[i+6],10+1,Length(OutStrL[i+6])-10));
      Inc(i,7);
      While IsSegment(OutStrL,i) do
      begin
        ItSegm:=AddSegm(Traces[ItTrace].Segments);
        if copy(OutStrL[i+1],12+1,Length(OutStrL[i+1])-12)='soHor' then Traces[ItTrace].Segments[ItSegm].Orient:=soHor else Traces[ItTrace].Segments[ItSegm].Orient:=soVert;
        Traces[ItTrace].Segments[ItSegm].Place:=copy(OutStrL[i+2],11+1,Length(OutStrL[i+2])-11);
        Traces[ItTrace].Segments[ItSegm].Length:=StrToFloat(copy(OutStrL[i+3],12+1,Length(OutStrL[i+3])-12));
        Traces[ItTrace].Segments[ItSegm].Scale:=StrToFloat(copy(OutStrL[i+4],11+1,Length(OutStrL[i+4])-11));
        Traces[ItTrace].Segments[ItSegm].PolyLineHandle:=copy(OutStrL[i+5],20+1,Length(OutStrL[i+5])-20);
        Inc(i,6);
        While IsPoint(OutStrL,i) do
        begin
          SetLength(Traces[ItTrace].Segments[ItSegm].Points,Length(Traces[ItTrace].Segments[ItSegm].Points)+1);
          ItPoint:=Length(Traces[ItTrace].Segments[ItSegm].Points)-1;
          Traces[ItTrace].Segments[ItSegm].Points[ItPoint].QuarTan:=StrToFloat(copy(OutStrL[i+1],15+1,Length(OutStrL[i+1])-15));
          Traces[ItTrace].Segments[ItSegm].Points[ItPoint].Point.X:=StrToFloat(copy(OutStrL[i+2],13+1,pos(';',OutStrL[i+2])-13-1));
          Traces[ItTrace].Segments[ItSegm].Points[ItPoint].Point.Y:=StrToFloat(copy(OutStrL[i+2],pos(';',OutStrL[i+2])+1,Length(OutStrL[i+2])-pos(';',OutStrL[i+2])));
          Inc(i,3);
        end;
      end;
    end;
    OutStrL.Free;
  end;
end;

Последний раз редактировалось Serge_Bliznykov; 28.06.2017 в 10:19.
Ship_1 вне форума Ответить с цитированием
Старый 28.06.2017, 10:50   #7
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

Цитата:
Сообщение от Ship_1 Посмотреть сообщение
1. Может кто-нибудь доходчиво объяснить что это за строка?
Код:
      if Len>0 then Stream.WriteBuffer(Pointer(m[i][j].X)^,Len*SizeOf(Real));
Условно догадываюсь, что при длине массива больше нуля (т.е. если он есть) в поток передаётся указатель на него и длина, умноженная на размер элемента. Но не слишком это мне понятно.
Объясняю. после того, как мы записали в поток (в файл) длину массива, мы проверяем, не является ли этот массив пустым. Если он не пустой (количество элементов в массиве Len больше нуля, тогда
мы записываем в выходной поток (в файл) собственно данные. Это мы делаем так, получаем адрес массива X, находящегося в массиве M в i-строке j-м стролбце ([i][j].X) ( в том коде это X: TArrayOfreal; array of real)
т.к. массив состоит из real - то размер памяти, который занимают данные (и который нужно записать) составляет количество * размер(real)

так понятней?

Цитата:
Сообщение от Ship_1 Посмотреть сообщение
Второй вопрос: что-то поменяется, если будет не TStream, а TFileStream, или можно просто заменить одно на другое, больше ничего не меняя в коде,
точно так. TFileStream это класс-наследник от TStream, он наследует всего его методы и свойства (переопределяя нужные), позволяя работать с файловым потоком (т.е. писать в файл, читать из файла).
Если Вы вдруг не знакомы, ознакомьтесь с принципами ООП.


Цитата:
Сообщение от Ship_1 Посмотреть сообщение
И третий: а зачем вообще нужен в подобных случаях TStream? Что с ним дальше делать?
А разве ответ на второй вопрос не позволяет понять, для чего нужен в подобных случаях TStrеam и что с ним дальше делать?!

Цитата:
Сообщение от Ship_1 Посмотреть сообщение
Сохранение и загрузку на данный момент для своего случая сделал через циклы и TStringList:
Воля ваша. Есть свои плюсы и минусы в выбранном вами решении.
Плюсы - Вы получаете текстовый файл, данные можно посмотреть и даже (очень аккуратно) подредактировать в текстовом редакторе.

Минусы - большой размер (в десятки/сотни раз больше бинарного файла), низкая скорость - как записи, так и чтения, сложный и очень ненадёжный (особенно в вашей реализации) код чтения и т.д.

но ещё раз повторю - Вы программист, Вам и выбирать решение, которое Вас устроит.
Serge_Bliznykov вне форума Ответить с цитированием
Старый 28.06.2017, 13:23   #8
Ship_1
Форумчанин
 
Регистрация: 10.02.2014
Сообщений: 526
По умолчанию

Serge_Bliznykov, спасибо за пояснения! Но:
1. Нет, не понятней. Как раз то, что Вы написали, я и понимаю. Я не понимаю, почему в этом случае на запись передаётся указатель, а не переменная (тем более, что в случае динамических массивов переменная содержит не сам массив, а указатель на него)? Особенность реализации процесса в Delphi или что-то более логичное?
2. Тут всё понятно, спасибо.
3. Из предыдущего понятно зачем TFileStream: его можно записать в файл, создать из файла. А просто TStream? Используется ли он сам по себе? Или это нечто абстрактное типа TString, TGrid, которое само по себе не используется, но используется в модифицированных вариантах (как TStringList, TStringGrid)?
Ship_1 вне форума Ответить с цитированием
Старый 28.06.2017, 13:57   #9
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

Цитата:
Сообщение от Ship_1 Посмотреть сообщение
Я не понимаю, почему в этом случае на запись передаётся указатель
А где Вы увидели указатель? посмотрите, что делает ^

Цитата:
Сообщение от Ship_1 Посмотреть сообщение
Особенность реализации процесса в Delphi или что-то более логичное?
простите, я не понимаю, о чём Вы...

Цитата:
Сообщение от Ship_1 Посмотреть сообщение
А просто TStream? Используется ли он сам по себе?
по моему, это тоже АБСТРАКТНЫЙ класс, как и TStrings (кстати, если Вы посмотрите методы у TStrings, то увидите методы, которые работают с потоком:
procedure LoadFromStream(Stream: TStream); virtual;
procedure SaveToStream(Stream: TStream); virtual;
)

Базовые классы TStream и THandleStream
Цитата:
В основе иерархии классов потоков лежит класс Tstream. Он обеспечивает выполнение основных операций потока безотносительно к реальному носителю информации. Основными из них являются чтение и запись данных.

Класс Tstream порожден непосредственно от класса TObject.

Потоки также играют важную роль в чтении/записи компонентов из файлов ресурсов (DFM). Большая группа методов обеспечивает взаимодействие компонента и потока, чтение свойств компонента из ресурса и запись значений свойств в ресурс
Serge_Bliznykov вне форума Ответить с цитированием
Старый 28.06.2017, 15:45   #10
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,526
По умолчанию

Цитата:
Я не понимаю, почему в этом случае на запись передаётся указатель
Цитата:
Условно догадываюсь, что при длине массива больше нуля (т.е. если он есть) в поток передаётся указатель на него и длина,
массив в Delphi это не просто данные ( НЕ указатель на данные).
Это некая "структура" содержащая
1. указатель на данные
2. размерность массива(число элементов)
если точнее, то указатель на нее(структуру).
пока не будем вдаваться как именно она организована в "физическом" плане, поскольку это "закрыто" компилятором.

Для записи в Stream нужны "чистые" данные, указатель на ИХ(данных) первый(начальный) байт.
программа — запись алгоритма на языке понятном транслятору

Последний раз редактировалось evg_m; 28.06.2017 в 15:51.
evg_m вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Файловый ввод/вывод в языке С: двоичный файл и записать в него двумерный целочисленный массив размером n*m, записать в файл F2... Анастасия_А Помощь студентам 0 28.03.2016 14:07
Есть файл с текстом. Нужно записать ззадом на перед текст с файла. vova_makr Помощь студентам 7 17.10.2015 21:26
Создать 3 числовых файла. Найти кол-во совпадающих элементов и записать это число в другой файл. Astronomer Паскаль, Turbo Pascal, PascalABC.NET 1 05.04.2014 02:55
Динамический массив - или всё таки не динамический? vedro-compota Общие вопросы C/C++ 30 10.12.2010 23:22
Как записать в файл динамический массив. Вадим Буренков Общие вопросы Delphi 9 24.07.2009 15:12