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

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

  1. semantics

    semantics Гость

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

    vital Больной Компом Детектед

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

    VahaC Well-Known Member

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

    sinkopa Well-Known Member

    Репутация:
    0
    Регистрация:
    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 Гость

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

    Репутация:
    0
    Регистрация:
    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

    Репутация:
    0
    Регистрация:
    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. Василий

    Репутация:
    0
    Регистрация:
    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

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

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

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

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