Форум: "Базы";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.02.21;
Скачать: [xml.tar.bz2];




Вниз

сортировка IP адресов и не только.... 


Дремучий   (2002-01-24 17:45) [0]

подскажите идейку-алгоритм как проще всего упорядочить вот такой массив:

1.2.4.
1.1.12.
1.2.
1.2.9.
9.2.3.2.
5.2.14.
11.
10.8.2.9.
12.3.1.

чтобы получить такой:

1.1.12.
1.2.
1.2.4.
1.2.9.
5.2.14.
9.2.3.2.
10.8.2.9.
11.
12.3.1.

для желающих разомять мозги - задачку усложню/упрощу
Реально как отсортировать таблицу по столбцу с такими данными
средствами (локального) SQL



Digitman   (2002-01-24 17:54) [1]

1. преобразуй каждый адрес в Intel DWORD и сортируй на здоровье любым алгоритмом сортировки целых чисел

2. алгоритм преобразования включи в UDF.



Дремучий   (2002-01-24 18:07) [2]

2 Digitman
есть маленькие нюансы
1) 1.2. < 1.2.4. и 1.2. < 11.
2) 1.1.12. < 11.

т.е. нужно учитывать уровень розряда и умножать число(макс двузначное) этого розряда на 10*НомерРозряда(1,2,... макс 4)
ЗАТЕМ ПРОПИСЫВАТЬ НАЙДЕНУЮ СУМУ УМНОЖЕННЫХ РОЗРЯДОВ В ОТДЕЛЬНОЕ
ПОЛЕ И ПО НЕМУ СОРТИРОВАТЬ.

1(4 розр).1(3 розр).12(2 розр). < 11(4 розр).

2 Digitman
так не получиться
1_1_12 > 11
правильно так
12*100+1*1000+4*10000 < 11*10000

А ЕСТЬ ЛИ МЕТОД ПОПРОЩЕ?



Chris   (2002-01-25 09:30) [3]

Можно попробовать такой вариант:

s1:="1.1.12.";
s2:="11.";
s1_:="";
s2_:="";
k:=0;
for i:=1 to length(s1) do
begin
if s1[i]="." then
begin
s1_:="."+FormatFloat("000",k)+s1_;
k:=0;
end;
else
k:=10*k+ord(s1[i])-48;
end;
k:=0;
for i:=1 to length(s2) do
begin
if s2[i]="." then
begin
s2_:="."+FormatFloat("000",k)+s2_;
k:=0;
end;
else
k:=10*k+ord(s2[i])-48;
end;

Теперь s1_=".012.001.001", а s2_=".011"
и теперь можно смело сортировать элементы.



Дмитрий   (2002-01-25 09:59) [4]

type
TIP = record
case byte of
0: (value: integer);
1: (fourthb, thirdb, secondb, firstb:byte);
end;

var ip: TIp;
ip.firstb = $FF;
..
ip.fourthb = 15;

// ip.value - это интегер. Сортитруй. Потом можешь преобразовать обратно



Johnmen   (2002-01-25 11:12) [5]

Пишешь свою UDF в MyDLL.dll типа (не забудь потом в БД сделать DECLARE EXTERNAL FUNCTION ...) :

function MySpicialSubstr(S: string; P: integer) : integer;
begin
try
Result:=StrToInt(ExtractWord(P,S,["."]));
except
Result:=0;
end;
end;

А потом смело выполняешь типа:

SELECT ipfield,
MySpicialSubstr(ipfield,1) AS F1,
MySpicialSubstr(ipfield,2) AS F2,
MySpicialSubstr(ipfield,3) AS F3,
MySpicialSubstr(ipfield,4) AS F4
FROM MyTable
ORDER BY F1, F2, F3, F4

(вышеописанное на уровне идеи)



Дремучий   (2002-01-25 12:24) [6]

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

eщe раз всем спасибо.

сие преобразование можно делать в событии OnAfterEdit, но...
а такой вопросик (наглею) можно ли написать UPDATE запрос
для перевода 12.1.11 -> 012.001.011



Digitman   (2002-01-25 13:18) [7]


uses WinSock;

type
EIPSpellCheckError = class(Exception);

const
IllegalIP = "Недопустимое строковое представление адреса : %s";

function ParseIp(Address: String; InAddr: PInAddr): Boolean;
var
PartNum, PartValue, PartCharNum, DelimiterCount: Integer;
psrc: PChar;
pdst: PByteArray;
NextChar: Char;
begin
Result:= False;
InAddr.S_addr:= 0;
PartNum:= 0;
PartCharNum:= 0;
PartValue:= 0;
DelimiterCount:= 0;
Address:= Trim(Address);
psrc:= PChar(Address);
pdst:= PByteArray(InAddr);
while psrc^ <> #0 do begin
NextChar:= psrc^;
case NextChar of
"0".."9":
if PartNum <= 4 then
if PartCharNum < 3 then begin
if DelimiterCount > 0 then
DelimiterCount:= 0
else if PartNum = 0 then
Inc(PartNum);
Inc(PartCharNum);
PartValue:= PartValue * 10 + byte(NextChar) - byte("0");
if PartValue > 255 then
Exit;
end else
Exit
else
exit;
".":
if DelimiterCount = 0 then
if PartNum > 0 then begin
pdst[PartNum - 1]:= Byte(PartValue);
PartValue:= 0;
Inc(PartNum);
PartCharNum:= 0;
PartValue:= 0;
Inc(DelimiterCount);
end else
Exit
else
Exit;
else
Exit;
end;
Inc(psrc);
end;
if PartNum > 0 then begin
if DelimiterCount = 0 then
pdst[PartNum - 1]:= Byte(PartValue);
Result:= True;
end;
end;


function CompareAddresses(List: TStringList; Index1, Index2: Integer): Integer;
var
InAddr: TInAddr;
Addr1, Addr2: DWord;
begin
if ParseIp(List[Index1], @inaddr) then
Addr1:= ntohl(InAddr.S_addr)
else
raise EIPSpellCheckError.CreateFmt(IllegalIP, [List[Index1]]);
if ParseIp(List[Index2], @inaddr) then
Addr2:= ntohl(InAddr.S_addr)
else
raise EIPSpellCheckError.CreateFmt(IllegalIP, [List[Index2]]);
if Addr1 > Addr2 then
Result:= 1
else if Addr1 < Addr2 then
Result:= -1
else
Result:= 0
end;

procedure Test;
var
AddrList: TStringList;
i, k: Integer;
s: string;
begin
AddrList:= TStringList.Create;
try
for i:= 0 to 9 do begin
s:= "";
for k:= 1 to Random(4) + 1 do
s:= s + IntToStr(Random(256)) + ".";
AddrList.Add(s);
end;
AddrList.CustomSort(@CompareAddresses);
s:= "";
for i:= 0 to AddrList.Count - 1 do
s:= s + AddrList[i] + #10;
ShowMessage(s);
finally
AddrList.Free;
end;
end;



Вызови процедуру Test(), она сгенерирует список из десятка строк, имитирующих список неких произвольных заданных тобой IP-адресов (в указанном тобой формате), отсортирует их и выведет окно с результатами сортировки



Дремучий   (2002-01-25 14:09) [8]

2 Digitman
написано в лучшем виде, въезжал правда медленно
новичком себя не считаю, но у тебя есть чему поучиться ;)
я так понял ты уже на том уровне, когда
сразу пишется оптимизированный код
Cool!



Digitman   (2002-01-25 14:36) [9]

>Дремучий
Ну, положим, он далеко не оптимизирован) Главная идея - отказаться от множества преобразований подстроки в число и наоборот, а также воспользоваться готовыми ф-циями, работающими с IP-адресами в разных представлениях. И если бы исходный массив у тебя был в полном корректном формате IP-адреса (XXX.XXX.XXX.XXX), парсер вообще можно было бы не использовать - все уже готово (см. хэлп на ф-цию inet-addr()) к преобразованию и сравнению : Intel_dword_addrvalue:= ntohl(inet_addr(string_addrvalue)).

А ты сразу в дебри полез с какими-то там "нюансами")... Нюанс один только - старайся поиметь на входе корректное строковое представление IP-адресов ... а все остальное "мягкие" и Борланд уже за тебя сделали))

Успехов)



Дремучий   (2002-01-25 16:06) [10]

2 Digitman
>>И если бы исходный массив у тебя был в полном корректном
>>формате IP-адреса (XXX.XXX.XXX.XXX)...
сортировка IP адресов и не только....

Дело в том, что данный список у меня не есть IP адреса,
а в действительности пункты файла отчетности, который хранится в парадоксовской таблице....
А про Ip я дописал, чтоб вопрос интересней звучал, и потому что это мне тоже вскоре нужно будет для айпи-сканера. Вот так два зайца пеоимел...
;)




Digitman   (2002-01-25 16:16) [11]

Если этот самый "файл отчетности" - твоей разработки, то это еще печальней. Глубоко сомневаюсь, что нельзя было при разработке структуры таблицы/запроса предусмотреть возможность форимрования подобного НД в целочисленных типах данных



Дремучий   (2002-01-25 17:27) [12]

суть такова, что пункты должны отображаться в определенной последовательности, т.е. должен быть индекс, по которому они сортируються. Из этого следует, что
1)что этот индекс не может быть по инкременту (добавление в середину)
2)требуется индекс (по полю понятному юзеру типа порядковый номер) - если использовать целочисельное поле, то оно по всей видимости будет не одно - разве это будет оптимальней?
3)если были два номера подряд 24 и 25 нужно вставить между ними
запись - вставка будет "более" проблематичной - хвост длиннее, чем в случае когда 2.4 и 2.5 вставляется запись
4)пункты в виде 2.4 и 2.5 все равно нужно выводить в таблице и на печать
5)возможность делать связку мастер-деталь по этому индексу(полю) с другими таблицами

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

интересно будет выслушать доводы за целочисельное поле(я) для решения подобной задачи.

С уважением Дремучий.



Digitman   (2002-01-25 18:04) [13]

У тебя полный кавардак в терминологии, извини уж. Приведи структуры таблиц. связей между ними и структуру запроса, формирующего НД, результат которого ты привел в первоначальном вопросе



Дремучий   (2002-01-25 18:33) [14]

2 Digitman © (25.01.02 18:04)
>>У тебя полный кавардак в терминологии
извини, заканчивал эк. вуз ;)
а объяснять-то и нечего

Table1 - таблица мастер - НД для отчета
xCode Char(12)
xName Char(100)
xSum Number


Table2 - таблица деталь - здесь храняться параметры по которым рассчитываются xSum (связь через xCode)
xCode Char(12)
xParam1 int
xParam2 int

задача 1 - обеспечить юзеру удобное добавление записей в
Table1(постановка добавленой записи на нужное место в базе),
записи Table1 либо в xCode либо в xName должны объязательно содержать кодировку в виде XX.XX.XX.XX

задача 2 - обеспечить удобный синтаксис выборки кусков НД
по заданому интервалу пунктов (от 2.1.1.0 до 3.9.9.9)

вот и все



Anatoly Podgoretsky   (2002-01-26 21:31) [15]

если с учетом применения для ИП адресов, то размерность должна быть 15 символов и преобразования при вводе/выводе для подавления/добавления незначащих нулей.
то есть хранишь в рормализованном виде XXX.XXX.XXX.XXX



Digitman   (2002-01-28 18:56) [16]

Таблицы твоей разработки ? Есть возможность добавить/изменить поля ?
Если есть, то:
- добавляешь в Table1 поле Integer, делаешь по нему уник.индекс по возрастанию; это будет PrimaryKey; поле code, в принципе, можно удалить : то, что в нем содержится, всегда можно получить из нового Integer-поляж
- изменяешь в Table2 поле Code на Integer, делаешь его индексированным; это будет ForeignKey;
- всякий раз при добавлении в Table1 записи преобразовываешь (в триггере, к примеру, или в SP) исх.строковое значение в формате XX.XX.XX.XX в Integer , например, парсером, код которого я привел (ну, с доработками, конечно); запись автоматически становится на свое место - об этом позаботится PK-индекс;
- при запросе к Table2 выполняешь обратное преобразование из Integer в char(12)

Вот и все !




Форум: "Базы";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.02.21;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.77 MB
Время: 0.019 c
1-66539           SireX                 2002-02-04 16:17  2002.02.21  
Проблема со скачиванием файлов


3-66427           ___Nikolay            2002-01-26 16:58  2002.02.21  
!!! СРОЧНО PLZ !!! ___КАК АРХИВИРОВАТЬ ФАЙЛЫ В БД____


6-66642           jdr                   2001-12-01 03:07  2002.02.21  
сокеты


3-66488           Зинец Виктор          2002-01-28 18:01  2002.02.21  
Можно ли как-то


1-66533           Паша                  2002-02-05 12:27  2002.02.21  
Изменение размеров шрифта экрана искажает содержимое формы