Как сохранить / загрузить набор типов?

17

У меня есть этот код

type
  TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType6, xsTyp7, xsType8); // up to FXSample30;  
..

private
  FXSample = Set of TXSample;  
..

published
  property Sample: TXSample read FXSample  write FXSample; 
..

  //if Sample has a value of
  Sample := [xsType2, xsType4, xsType5, xsType6, xsTyp7];

как я могу сохранить / загрузить свойство Sample?
Я хотел бы сохранить его в базе данных.
это возможно?

    
задан XBasic3000 04.03.2012 в 09:40
источник
  • У вас может быть куча логических полей. Или вы можете сжимать целое число или целое число, используя один бит для каждого флага. Я бы использовал первое. –  David Heffernan 04.03.2012 в 09:58
  • Посмотрите на TReader.ReadSet и WriteSet в TWriter.WriteProperty в Classes.pas. Вот как потоки VCL устанавливают свойства для файлов dfm и из них. Если вы их используете, вам понадобится поле строки в вашей базе данных для хранения выписанного набора. Обратите внимание, что переход на этот путь облегчит чтение и запись, чем хранение значений в отдельных логических полях, но может сделать фильтрацию вашего набора данных с использованием SQL намного сложнее. –  Marjan Venema 04.03.2012 в 10:24
  • Не то, чтобы это было правильно или неправильно, но почему вы не делаете тип, называемый TXSamples = набор TXSample, а затем поле FXSample: TXSamples ... Я смущен, почему вы заявляете это так в частное поле. Вам гораздо лучше сделать еще один тип TXSamples, а затем использовать этот тип для FXSample ... По крайней мере, так я всегда видел это и делал сам. –  Jerry Dodge 04.03.2012 в 21:14
  • @ Джерри Додж, извините, если это не так ясно. Я отредактировал вопрос для деталей. –  XBasic3000 05.03.2012 в 09:24
  • @ XBasic3000 Не то, чтобы было что-то непонятное, просто я бы посоветовал определить тип этого набора, а не бросать его каждый раз, когда вам нужно его использовать. –  Jerry Dodge 05.03.2012 в 16:38

9 ответов

25

Если ваш набор никогда не превысит 32 возможностей ( Ord(High(TXSample)) <= 31 ), то вполне можно типизировать набор в Integer и обратно:

type
  TXSamples = set of TXSample;
var 
  XSamples: TXSamples;
begin
  ValueToStoreInDB := Integer(XSamples);
  Integer(XSamples) := ValueReadFromDB;
end;

Чтобы быть более точным: SizeOf(TXSamples) должен быть точно равен SizeOf(StorageTypeForDB) . Таким образом, следующие диапазоны применяются для Ord(High(TXSample)) , когда Typecasting TXSamples to:

  • Byte: Ord(High(TXSample)) < 8
  • Word: 8 <= Ord(High(TXSample)) < 16
  • Longword: 16 <= Ord(High(TXSample)) < 32
  • UInt64: 32 <= Ord(High(TXSample)) < 64
ответ дан NGLN 04.03.2012 в 11:41
  • Это действительно работает? При приведении набора в целое число появляется ошибка при ошибке типа Недействительная E2089. –  dan-gph 25.06.2013 в 01:52
  • @ Dangph Да. Вы проверяли границу максимального размера / перечисления? (Например, когда ваш набор состоит из перечисления с 16 возможностями, вы должны отдать набор в Word. Когда максимальный установленный размер равен 17, вы должны указать в Integer.) –  NGLN 27.06.2013 в 21:26
  • Я вижу, спасибо. Мне нужно было передать байт, а не целому. Я предполагал, что компилятор просто будет автоматически расширять значение до целого. –  dan-gph 28.06.2013 в 08:28
  • Большое спасибо, это действительно здорово, я использовал следующий код, чтобы сконденсировать 8 вкладышей в один: CustomFont.Style: = TFontStyles (Byte ((ord (fsBold) и (Ord (isBold) shl Byte (ord ( fsBold)))) или (ord (fsItalic) и (Ord (isItalic) shl Byte (ord (fsItalic)))) или (ord (fsStrikeOut) и (Ord (isStrikeout) shl Byte (ord (fsStrikeOut)))) или (ord (fsUnderline) и (Ord (isUnderline) shl Byte (ord (fsUnderline)))))); где isBold и т. д. являются булевыми –  Peter Turner 13.02.2014 в 17:18
  • Вы не можете наложить на UInt64 размер от 32 до 64 в 32-битном Delphi. См. Мой ответ здесь о различиях между 32 бит и 64 бит. stackoverflow.com/questions/30336620/... –  Graymatter 20.05.2015 в 05:32
5

Непосредственное приведение типов к заданной переменной в Delphi невозможно, но внутри Delphi хранит набор в виде байтового значения. Используя нетипизированный ход, его легко скопировать в целое число. Обратите внимание, что эти функции имеют размер только 32 (целое число). Чтобы увеличить границы, используйте вместо этого Int64 .

function SetToInt(const aSet;const Size:integer):integer;
begin
  Result := 0;
  Move(aSet, Result, Size);
end;

procedure IntToSet(const Value:integer;var aSet;const Size:integer);
begin
  Move(Value, aSet, Size);
end;

Демонстрационный

type
  TMySet = set of (mssOne, mssTwo, mssThree, mssTwelve=12);
var
  mSet: TMySet;
  aValue:integer;
begin
  IntToSet(7,mSet,SizeOf(mSet));
  Include(mSet,mssTwelve);
  aValue := SetToInt(mSet, SizeOf(mSet));
end;
    
ответ дан Ramon Speets 16.01.2013 в 15:46
  • Было бы неплохо проверить где-то размер sizeof (TMySet) = sizeof (целое число) на случай, если ваш набор слишком мал или слишком велик. –  dan-gph 25.06.2013 в 09:03
4

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

Конечно, вы можете упаковать набор в целочисленное значение, представив его как набор битов. Или вы можете создать текстовое представление набора.

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

    
ответ дан David Heffernan 04.03.2012 в 11:41
  • Это зависит. Если бы у вас было, скажем, более 20 элементов в перечислении набора, создание столбца для каждого элемента могло бы быть просто потерей ресурсов. Я бы лично сделал колонки для максимум 5 элементов. Для большего числа из них я бы использовал побитовые операторы в SQL-запросах (не очень удобно), или для тех, кто знает SQL, но не может использовать побитовые операторы, я бы делал табличные представления. –  TLama 20.07.2014 в 21:07
3

Самый простой способ сохранить набор в базе данных (как упомянуто в комментарии @DavidHeffernan) - это преобразовать ваш набор в битовую маску. в значении int32 (integer) у вас есть 32 бита, и вы можете сохранить set до 32 полей; Delphi имеет тип TIntegerSet (см. Ссылка ), определенный в SysUtils . он объявлен как:

TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;

поэтому, используя его, просто преобразовать set в целое число и обратно (просто приведение TIngeterSet к целому числу или наоборот);

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

также вы можете создать отдельную таблицу в вашей БД для хранения заданного содержимого (основная таблица (id, ...) и setValuesTable (main_id, setElementValue)) (эта опция удобна для использования в запросах базы данных)

вот пример использования TIntegerSet :

program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils;

type
    TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6,  xsType7, xsType8);
    TSampleSet = set of TXSample;



    function SampleSetToInteger(ss : TSampleSet) : integer;
    var intset : TIntegerSet;
        s : TXSample;
    begin
        intSet := [];
        for s in ss do
            include(intSet, ord(s));

        result := integer(intSet);
    end;

    function IntegerToSampleSet(mask : integer) : TSampleSet;
    var intSet : TIntegerSet;
        b : byte;
    begin
        intSet := TIntegerSet(mask);
        result := [];
        for b in intSet do
            include(result, TXSample(b));
    end;

var xs : TSampleSet;
    mask : integer;
begin
    xs := [xsType2, xsType6 .. xsType8];

    mask := SampleSetToInteger(xs);     //integer mask
    xs := IntegerToSampleSet(mask);
end.
    
ответ дан teran 04.03.2012 в 11:04
3

Лично я бы преобразовал набор в целое число и сохранил бы его в базе данных в виде поля INT , как предлагали другие. @teran предложил использовать тип TIntegerSet , и вот мой подход к работе с нативными целыми числами с использованием битовых операций.

Обратите внимание, что вы можете использовать SampleInInteger() , чтобы определить, присутствует ли определенный элемент из перечисления в целочисленной маске, созданной SampleSetToInteger() .

Вот код:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  { .: TXSample :. }
  TXSample = (xsType1 = 0, xsType2, xsType3, xsType4, xsType5,
    xsType6, xsType7, xsType8); // up to FXSample30;
  TXSampleSet = set of TXSample;

// Converts a TXSampleSet to an integer.
function SampleSetToInteger(const S: TXSampleSet): Integer;
var
  Sample: TXSample;
begin
  Result := 0;

  for Sample := Low(TXSample) to High(TXSample) do
    if (Sample in S) then
      Result := Result or (1 shl Ord(Sample));
end;

// Converts an integer to TXSampleSet.
function IntegerToSampleSet(const Int: Integer): TXSampleSet;
var
  I: Integer;
begin
  Result := [];

  for I := 0 to Ord(High(TXSample)) do
    if Int and (1 shl I) <> 0 then
      Result := Result + [TXSample(I)];
end;

// Checks if a TXSample is present in the integer.
function SampleInInteger(const S: TXSample; const Int: Integer): Boolean;
begin
  Result := Int and (1 shl Ord(S)) <> 0;
end;

var
  XSample, XSample1: TXSampleSet;
  Tmp: Integer;
begin
  XSample := [xsType2, xsType4, xsType5, xsType6, xsType7];
  XSample1 := [xsType1];
  Tmp := SampleSetToInteger(XSample);

  Writeln(Tmp);
  XSample1 := IntegerToSampleSet(Tmp);
  if (xsType5 in XSample1) then
    Writeln('Exists');
  if (SampleInInteger(xsType1, Tmp)) then
    Writeln('Exists in int');


  Readln;
end.
    
ответ дан Pateman 04.03.2012 в 11:12
3

Заданные переменные могут быть успешно сохранены в потомке TStream. Вот пример.

Просто создайте новое приложение для форм vcl, добавьте к нему два компонента TButton и заполните события OnClick для каждой кнопки, как показано в примере ниже.

Это было создано в XE4, так что условие использования может отличаться для других версий Delphi, но это должно быть тривиально изменить, удаляя индикаторы пространства имен перед каждым модулем в предложении использования. Сохранение переменной заданного типа с четко сформулированными значениями возможно в двоичном файле и легко с Delphi. Другими словами,

Также предлагается взглянуть на блок TypInfo, если у вас есть источник, или просто использовать предоставленные функции, которые делают распространение типов Set вплоть до их текстового представления довольно простым, хотя здесь нет примеров. Это рекомендуется, если вы хотите включить сохранение в файл конфигурации или ini-файл или в постоянном формате, который можно редактировать в тексте.

Ниже приведен самый простой из известных мне. Просмотр двоичного вывода типа набора, сохраненного в потоке, подобном приведенному ниже, подразумевает, что он сохраняется в наименьшем возможном растровом представлении для набора на основе его размера. Нижеследующий отображает один байт на диске (значение равно 5), что означает, что каждое значение должно быть сопоставлено степени 2 (seThis = 1, seThat = 2, seTheOther = 4), точно так же, как созданные вручную постоянные значения битовой маски. Компилятор, вероятно, следит за соблюдением правил, заставляющих набор сохранять свою ординальность. Этот пример был протестирован в Delphi XE4.

Надеюсь, это поможет.

Брайан Джозеф Джонс

unit Unit1;

interface

uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
     Vcl.StdCtrls;

type
  TSomeEnum = (seThis, seThat, seTheOther);
  TSomeEnumSet = set of TSomeEnum;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var

  Form1: TForm1;
  SomeSetVar: TSomeEnumSet;
  SomeBoolean: Boolean;
  SomeInt: Integer;

implementation

{$R *.dfm}


procedure TForm1.Button1Click(Sender: TObject);
begin
  SomeSetVar := [seThis, seTheOther];
  SomeBoolean := True;
  SomeInt := 31415;

  with TFileStream.Create('SetSave.bin',fmCreate or fmOpenWrite or fmShareCompat) do
  try
    Write(SomeSetVar,SizeOf(SomeSetVar));
    Write(SomeBoolean,SizeOf(Boolean));
    Write(SomeInt,SizeOf(Integer));
  finally
    Free;
  end;
  SomeSetVar := [];
  SomeInt := 0;
  SomeBoolean := False;

end;

procedure TForm1.Button2Click(Sender: TObject);
var
  ResponseStr: string;
begin
  with TFileStream.Create('SetSave.bin',fmOpenRead or fmShareCompat) do
  try
    Read(SomeSetVar,SizeOf(SomeSetVar));
    Read(SomeBoolean,SizeOf(Boolean));
    Read(SomeInt,SizeOf(Integer));
  finally
    Free;
  end;

  ResponseStr := 'SomeSetVar = ';
  if (seThis in SomeSetVar) then
    ResponseStr := ResponseStr + 'seThis ';

  if (seThat in SomeSetVar) then
    ResponseStr := ResponseStr + 'seThat ';

  if (seTheOther in SomeSetVar) then
    ResponseStr := ResponseStr + 'seTheOther ';

  ResponseStr := ResponseStr + ' SomeBoolean = ' + BoolToStr(SomeBoolean);

  ResponseStr := ResponseStr + ' SomeInt = ' + IntToStr(SomeInt);

  ShowMessage(ResponseStr);

end;

end.
    
ответ дан Brian Johns 03.01.2014 в 01:43
1

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

type
  // Controls.TCMMouseWheel relies on TShiftState not exceeding 2 bytes in size 
  TShiftState = set of (ssShift, ssAlt, ssCtrl,
                        ssLeft, ssRight, ssMiddle, 
                        ssDouble, ssTouch, ssPen, 
                        ssCommand, ssHorizontal); 

var 
  Shifts : TShiftState;
  Value :  Integer;
begin
  Shifts := TShiftState((Pointer(@Value))^):

  Value  := (PInteger(@Shifts))^;

  if ssShift in TShiftState((Pointer(@Value))^) then 
     Exit;
end;

Бывает, что неиспользуемые (верхние) биты установлены (или нет), но это не влияет на операции set ( in , = , + , - , * ..) .

Эта строка в Delphi:

Shifts := TShiftState((Pointer(@Value))^);

похож на это в Ассемблере (Delphi XE6):

lea eax,[ebp-$0c]
mov ax,[eax]
mov [ebp-$06],ax

В Delphi 2007 (где TShiftState меньше, поэтому можно использовать Byte ) этот Ассемблер:

movzx eax,[esi]
mov [ebp-$01],al
    
ответ дан Radek Secka 20.04.2016 в 16:53
0

Вы можете использовать это устройство для преобразования набора в int. если вам нужно больше функций settoint, вы можете добавить свои, посмотрев код ниже.

Установка может занимать только 1 байт памяти. Таким образом, вы можете получить размер вашего набора и получить результат как модуль этого результата.

пример: ваш установленный размер: 1 байт, вы можете получить результат - >

Результат: = pINT ^ mod maxVal

Вы должны получить maxval путем вычисления maxvalue типа переменной.

maxVal = Power (2, (8 * sizeof (MySet) -1))

    unit u_tool;

interface
uses Graphics;

type
  TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType6, xsTyp7, xsType8); // up to FXSample30;
  FXSample = Set of TXSample;

  function FXSampleToInt(FXSample: FXSample ): Integer;
  function IntToFXSample(Value: Integer): FXSample;


  function FontStyleToInt(FontStyle: TFontStyles ): Integer;
  function IntToFontStyle(Value: Integer): TFontStyles;

implementation


function FXSampleToInt(FXSample: FXSample ): Integer;
var
  pInt: PInteger;
begin
  pInt := @FXSample;
  Result := pInt^;
end;

function IntToFXSample(Value: Integer): FXSample;
var
  PFXSample: ^FXSample;
begin
  PFXSample := @Value;
  Result := PFXSample^;
end;





function FontStyleToInt(FontStyle: TFontStyles ): Integer;
var
  pInt: PInteger;
begin
  pInt := @FontStyle;
  Result := pInt^;
end;

function IntToFontStyle(Value: Integer): TFontStyles;
var
  PFontStyles: ^TFontStyles;
begin
  PFontStyles := @Value;
  Result := PFontStyles^;
end;






end.
    
ответ дан Nihat Erim inceoğlu 04.12.2014 в 10:48
0

Самое простое решение - обработка набора непосредственно в виде числовой переменной. "Абсолют" - это ключевое слово:

procedure Foo(FXSample: TFXSample);
var
  NumericFxSample: Byte absolute FXSample;
begin
WriteLn(YourTextFile, NumericFxSample);//numeric value from a set
end;

Если ваш тип шире, чем 8 бит, вам нужно использовать более широкий числовой тип, например слово (до 16 элементов в наборе) или слово.

    
ответ дан Patman 20.06.2016 в 21:29