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

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
источник

9 ответов

23

Если ваш набор никогда не будет превышать 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)) применяются следующие диапазоны при приведении типов TXSamples в:

  • 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
источник
4

Непосредственно typecasting заданной переменной невозможно в 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
источник
4

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

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

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

    
ответ дан David Heffernan 04.03.2012 в 11:41
источник
3

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

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

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

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

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

вот пример использования 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 forms, добавьте к нему два компонента TButton и заполните события OnClick для каждой кнопки, как показано в примере ниже.

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

Также предлагается взглянуть на модуль TypInfo, если у вас есть источник или просто с использованием предоставленных функций, которые делают рассечение типов набора до их текстового представления довольно простым, хотя здесь не приводится никакого примера. Это рекомендуется, если вы хотите включить сохранение в конфигурационный файл или 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))^);

подобен этому в Assembler (Delphi XE6):

lea eax,[ebp-
movzx eax,[esi]
mov [ebp-],al
c] mov ax,[eax] mov [ebp-],ax

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

%pre%     
ответ дан Radek Secka 20.04.2016 в 16:53
источник
0

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

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

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

Результат: = 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 бит, вам нужно использовать более широкий цифровой тип, например word (до 16 элементов в наборе) или dword.

    
ответ дан Patman 20.06.2016 в 21:29
источник