сохранение массива строк на диск

Тема в разделе "Delphi - FAQ", создана пользователем semantics, 30 июн 2010.

  1. semantics

    semantics Гость

    есть массив или список типа string. Длина строк меняется при работе программы.
    Как сохранять этот массив на диск, не фиксируя длину строк?
     
  2. vital

    vital Больной Компом Детектед
    Команда форума Web Team

    Регистрация:
    29 янв 2006
    Сообщения:
    2.470
    Симпатии:
    27
    ну просто в файл записать никак?
     
  3. VahaC

    VahaC Well-Known Member

    Регистрация:
    10 янв 2007
    Сообщения:
    116
    Симпатии:
    0
    TStrings? TStringList?
     
  4. sinkopa

    sinkopa Well-Known Member

    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    Безусловно, самый "незаморочистый" способ сохранения в файл динамического массива строк, это использование промежуточного Стринг листа (TStringList)
    Код (Delphi):
    //...

    type
    // Объявляем тип (Delphi "не любит" работать с <array of string> как параметр внутри процедуры )
    TStringArray = array of string;
    TOptArrayCommand = (opSaveToFile, opLoadFromFile); // Опции управления функцией

    //...

    var
    SArray: TStringArray;

    implementation

    function ArrayOperation1(const FileName: string; var strArray: TStringArray; Opt: TOptArrayCommand):Integer;
    var
    SList: TStringList;
    i: Integer;
    begin
    Result := 0;
    case Opt of
    opSaveToFile  : begin // Запись в файл

    if (Length(strArray) < 1) then Exit;

    SList := TStringList.Create;

    for i := Low(strArray) to High(strArray) do
    SList.Add(strArray[i]);

    SList.SaveToFile(FileName);
    Result := SList.Count;

    SList.Free;
    end;

    opLoadFromFile : begin  // Чтение из файла
    // обнулили массив
    SetLength(strArray,0);

    if not FileExists(FileName) then Exit;

    SList := TStringList.Create;
    SList.LoadFromFile(FileName);

    SetLength(strArray,SList.Count);

    for i := Low(strArray) to High(strArray) do
    strArray[i] := SList[i];

    Result := SList.Count;
    SList.Free;
    end;
    end;
    end;
    Но это НЕПРАВИЛЬНЫЙ способ :)
    Если вдруг окажется, что строчки (элементы массива) содержат в себе символы разрыва строки (перевода каретки) то?...
    Правильно, прочитанный из файла массив, будет содержать совсем не то количество элементов которое мы сохраняли...
    Сам массив станет несколько длиннее, а строчки (его элементы) несколько короче... :KillMe:
    хотя... не исключаю случаев когда такое поведение приведенного выше метода может быть как раз полезным...

    Тем не менее, если вы хотите прочитать из файла данные в том же виде (состоянии) в каком сохраняли, предлагаю способ, который считаю более ПРАВИЛЬНЫМ
    Код (Delphi):
    //...

    type
    // Объявляем тип (Delphi "не любит" работать с <array of string> как параметр внутри процедуры )
    TStringArray = array of string;
    TOptArrayCommand = (opSaveToFile, opLoadFromFile); // Опции управления функцией

    //...

    var
    SArray: TStringArray;

    implementation

    function ArrayOperation2(const FileName: string; var strArray: TStringArray; Opt: TOptArrayCommand):Integer;
    const
    RES_ID = 'MYSTRARRAY';
    var
    ms: TMemoryStream;
    i,len: Integer;
    s: string;
    begin
    Result := 0;
    case Opt of
    opSaveToFile  : begin   // Запись в файл

    len := Length(strArray);
    if (len < 1) then Exit;

    ms := TMemoryStream.Create;
    ms.Seek(0,soFromBeginning);

    // записали имя ресурса
    // чтобы потом (при чтении) быть уверенным что это наш массив
    ms.WriteBuffer(RES_ID,Length(RES_ID)*SizeOf(Char));

    // записали общую длину массива
    ms.WriteBuffer(Pointer(len),SizeOf(Integer));

    Result := len;

    for i := Low(strArray) to High(strArray) do
    begin
    len := Length(strArray[i]);
    // записали длину i-той строчки
    ms.WriteBuffer(Pointer(len),SizeOf(Integer));
    // записали i-тую строчку
    if (len > 0) then
    ms.WriteBuffer(strArray[i][1],len*SizeOf(Char));
    end;
    ms.Seek(0,soFromBeginning);
    try
    ms.SaveToFile(FileName);
    except
    Result := 0; // а вдруг не удалось записать файл?
    end;
    ms.Free;
    end;

    opLoadFromFile : begin // Чтение из файла
    // обнулили массив
    SetLength(strArray,0);

    if not FileExists(FileName) then Exit;
    ms := TMemoryStream.Create;
    ms.LoadFromFile(FileName);
    ms.Seek(0,soFromBeginning);

    // прочитали имя ресурса
    SetLength(s,Length(RES_ID));
    ms.ReadBuffer(s[1],Length(RES_ID)*SizeOf(Char));

    // Убедились что это именно то что мы хотим
    // прочитать в массив строк
    if (s <> RES_ID) then
    begin
    ms.Free;
    Exit;
    end;

    // прочитали общую длину массива
    ms.ReadBuffer(Pointer(len),SizeOf(Integer));

    // выставили длину массива
    SetLength(strArray,len);

    for i := Low(strArray) to High(strArray) do
    begin
    // прочитали длину i-той строчки
    ms.ReadBuffer(Pointer(len),SizeOf(Integer));
    if (len > 0) then
    begin
    // выставили длину i-того элемента массива
    SetLength(strArray[i],len);
    // прочитали i-тую строчку
    ms.ReadBuffer(strArray[i][1],len*SizeOf(Char));
    end
    else
    strArray[i] := '';
    end;

    Result := Length(strArray);
    ms.Free;
    end;
    end;
    end;
    Пример использования для обоих методов

    Код (Delphi):
    procedure TForm1.Button1Click(Sender: TObject);
    var
    ResultCount: Integer;
    begin
    ResultCount := ArrayOperation1('c:\arraydata1.txt',SArray,opSaveToFile);
    ShowMessage(IntToStr(ResultCount));

    ResultCount := ArrayOperation1('c:\arraydata1.txt',SArray,opLoadFromFile);
    ShowMessage(IntToStr(ResultCount));

    if (ResultCount > 0) then
    ShowMessage(SArray[0]);

    ResultCount := ArrayOperation2('c:\arraydata2.txt',SArray,opSaveToFile);
    ShowMessage(IntToStr(ResultCount));

    ResultCount := ArrayOperation2('c:\arraydata2.txt',SArray,opLoadFromFile);
    ShowMessage(IntToStr(ResultCount));

    if (ResultCount > 0) then
    ShowMessage(SArray[0]);
    end;
     
  5. semantics

    semantics Гость

    Спасибо, я именно это хотел узнать - какие есть стандартные средства для работы с динамическими массивами строк.
     
  6. Василий

    Регистрация:
    22 фев 2015
    Сообщения:
    9
    Симпатии:
    0
    Описанный Вами способ мне очень подошел. Но возникла неприятная особенность. При переходе на Delphi XE3 пришлось добавить ansi к string и char. Но сохранить в файл не удалось. В строке
    1. ms.WriteBuffer(Pointer(len),SizeOf(Integer)); ошибка $C0000005.
      Что нужно, чтобы в Delphi XE3 заработало?
     
  7. sinkopa

    sinkopa Well-Known Member

    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    Тип Pointer в XE3 имеет разную размерность для Win32 и Win64.
    Надо типизированный указатель использовать (или можно просто сам var в качестве буффера подставить)
    Вот так будет работать правильно и без ошибок:
    Код (Delphi):
      // ...
    type
      // Объявляем тип (Delphi "не любит" работать с <array of string> как параметр внутри процедуры )
      TStringArray = array of AnsiString;
      TOptArrayCommand = (opSaveToFile, opLoadFromFile); // Опции управления функцией
     
    // ...
     
    function ArrayOperation2(const FileName: AnsiString; var strArray: TStringArray;
      Opt: TOptArrayCommand): Integer;
    const
      RES_ID: PAnsiChar = 'MYSTRARRAY';
    var
      ms: TMemoryStream;
      i, len,n: Integer;
      s: AnsiString;
    begin
      Result := 0;
      n := StrLen(RES_ID);
      case Opt of
        opSaveToFile:
          begin // Запись в файл
            len := Length(strArray);
            if (len < 1) then
              Exit;
            ms := TMemoryStream.Create;
            ms.Seek(0, soFromBeginning);
            // записали имя ресурса
            // чтобы потом (при чтении) быть уверенным что это наш массив
            ms.WriteBuffer(RES_ID^, n { SizeOf(RES_ID)});
            // записали общую длину массива
            ms.WriteBuffer(len, SizeOf(Integer));
            Result := len;
            for i := Low(strArray) to High(strArray) do
            begin
              len := Length(strArray[i]);
              // записали длину i-той строчки
              ms.WriteBuffer(len, SizeOf(Integer));
              // записали i-тую строчку
              if (len > 0) then
                ms.WriteBuffer(PAnsiChar(strArray[i])^, len);
            end;
            ms.Seek(0, soFromBeginning);
            try
              ms.SaveToFile(FileName);
            except
              Result := 0; // а вдруг не удалось записать файл?
            end;
            ms.Free;
          end;
        opLoadFromFile:
          begin // Чтение из файла
            // обнулили массив
            SetLength(strArray, 0);
            if not FileExists(FileName) then
              Exit;
            ms := TMemoryStream.Create;
            ms.LoadFromFile(FileName);
            ms.Seek(0, soFromBeginning);
            // прочитали имя ресурса
            SetLength(s, n);
            ms.ReadBuffer(s[1], n);
            // Убедились что это именно то что мы хотим
            // прочитать в массив строк
            if (s <> RES_ID) then
            begin
              ms.Free;
              Exit;
            end;
     
            // прочитали общую длину массива
            ms.ReadBuffer(len, SizeOf(Integer));
     
            // выставили длину массива
            SetLength(strArray, len);
     
            for i := Low(strArray) to High(strArray) do
            begin
              // прочитали длину i-той строчки
              ms.ReadBuffer(len, SizeOf(Integer));
              if (len > 0) then
              begin
                // выставили длину i-того элемента массива
                SetLength(strArray[i], len);
                // прочитали i-тую строчку
                ms.ReadBuffer(strArray[i][1], len);
              end
              else
                strArray[i] := '';
            end;
            Result := Length(strArray);
            ms.Free;
          end;
      end;
     
    end;
     
  8. Василий

    Регистрация:
    22 фев 2015
    Сообщения:
    9
    Симпатии:
    0
    Спасибо! Я примерно так и доработал Вашу идею:
    Procedure SaveToFile( var strArray: TStringArray);
    const
    RES_ID = 'MAIN_FILE';
    var
    ms: TMemoryStream;
    i,len: Integer;
    s: ansistring;
    Result:integer;
    begin
    Result := 0;
    len := Length(strArray);
    if (len < 1) then Exit;
    ms := TMemoryStream.Create;
    ms.Seek(0,soFromBeginning);
    // записали имя ресурса
    // чтобы потом (при чтении) быть уверенным что это наш массив
    len := Length(RES_ID);
    // ms.WriteBuffer(len,SizeOf(Integer));
    ms.WriteBuffer(RES_ID[1],Len*SizeOf(ansiChar));
    // записали общую длину массива
    len := Length(strArray);
    ms.WriteBuffer(len,SizeOf(Integer));
    Result := len;
    for i := Low(strArray) to High(strArray) do
    begin
    len := Length(strArray);
    // записали длину i-той строчки
    ms.WriteBuffer(len,SizeOf(Integer));
    // записали i-тую строчку
    if (len > 0) then
    ms.WriteBuffer(strArray[1],len*SizeOf(ansiChar));
    end;
    ms.Seek(0,soFromBeginning);
    try
    ms.SaveToFile(frmMain.FName);
    except
    Result := 0; // а вдруг не удалось записать файл?
    end;
    ms.Free;
    end;
    Кстати Win 7 32 bit.
     
  9. sinkopa

    sinkopa Well-Known Member

    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    При посте Delphi-кода пожалуйста пользуйтесь тэгами (в редакторе Кнопочка "Код")

    Имелась в виду не сама Ваша операционная система, а целевая платформа указанная в свойствах проекта, в опциях компилятора
     
  10. Василий

    Регистрация:
    22 фев 2015
    Сообщения:
    9
    Симпатии:
    0
    Хорошо, спасибо!
     
Загрузка...

Поделиться этой страницей