Страницы: 1
RSS
Альтернативный способ сравнения вхождения ip-адреса в группу
 
Добрый день.  
Есть макрос, выполняющий нехитрую работу поиска названия страны ip-адреса по базе ip-адресов (Maxmind). Как известно, существует множество (более 120 тысяч) групп ip-адресов, с каждой из которых ассоциирована отдельная страна. Чтобы было проще искать в базе было придумано контрольное число, вычисляющиеся по формуле:  
         ipnum = 16777216*w + 65536*x + 256*y + z      
                 где IP Address = w.x.y.z  
 
Макрос при поиске 3000 ip-адресов в общей базе справляется за 50 секунд. Ищется альтернативный, более быстрый способ.  
Знаю про использование словаря, но в данном случае не хватает знаний, чтобы применить его.  
Пример в аттаче содержит урезанную версию базы ip-адресов.  
Временные отметки:  
Substring 0,086    
Compare columns 48,25    
Write  0,172    
Total 48,508
 
А вот сам макрос:  
 
Option Explicit  
Sub IP_Number()  
Const d = True ' таймер  
If d Then: Dim t!, tt!: t = Timer: tt = t ' таймер  
 
Dim IP As String  
Dim w As Long, x As Long  
Dim ipnum As Single  
Dim br_arr, er_arr, fr_arr, ilr&, dlr&, rng As Range, dish_arr(), ip_arr(), i&, j&  
 
ilr = Sheets("history").Cells(Rows.Count, 1).End(xlUp).Row ' лист с нужными ip-адресами  
dlr = Sheets("data").Cells(Rows.Count, 1).End(xlUp).Row ' лист с полным списком диапозонов ip-адресов  
Set rng = Range(Sheets("data").Cells(2, 1), Sheets("data").Cells(dlr, 6))  
 
Application.ScreenUpdating = False  
Application.Calculation = xlCalculationManual  
 
Sheets("history").Select  
ReDim ip_arr(1 To (ilr - 1), 1 To 1)  
For w = 2 To ilr  
IP = Trim(Range("F" & w).Value) ' удаление лишних пробелов  
ipnum = Val(Substring(IP, ".", 1)) * 16777216 + Val(Substring(IP, ".", 2)) * 65536 + Val(Substring(IP, ".", 3)) * 256 + Val(Substring(IP, ".", 4)) ' вычисление контрольного числа  
ip_arr(w - 1, 1) = ipnum  
Next w  
If d Then Debug.Print "Substring", Round(Timer - t, 3): t = Timer ' таймер  
br_arr = rng.Columns(3)    
er_arr = rng.Columns(4)  
fr_arr = rng.Columns(6)    
ilr = ilr - 1  
dlr = dlr - 1  
ReDim dish_arr(1 To ilr, 1 To 1)    
For i = 1 To ilr  
       For j = 1 To dlr  
           If ip_arr(i, 1) <= er_arr(j, 1) And ip_arr(i, 1) >= br_arr(j, 1) Then  
               dish_arr(i, 1) = fr_arr(j, 1)  
               Exit For  
           End If  
       Next  
   Next  
   If d Then Debug.Print "Compare columns", Round(Timer - t, 3): t = Timer  
   Sheets("history").Cells(2, 11).Resize(ilr) = dish_arr    
 
Application.Calculation = xlCalculationAutomatic  
Application.ScreenUpdating = True  
 
If d Then tt = Timer - tt: Debug.Print "Write", Round(Timer - t, 3); vbLf & "Total", Round(tt, 3); vbLf; "----" ' таймер  
End Sub  
 
Function Substring(Txt, Delimiter, n) As String ' функция деления текста по определенному разделяющему элементу  
Dim x As Variant  
   x = Split(Txt, Delimiter)  
   If n > 0 And n - 1 <= UBound(x) Then  
       Substring = x(n - 1)  
   Else  
       Substring = ""  
   End If  
End Function
 
Поищите по форуму. Кажется недавно (до 3-х недель) слэн выкладывал решение по поиску ТЛФ кодов. Там было большое обсуждение и использование словарей.
 
{quote}{login=Longines}{date=15.06.2010 07:29}{thema=Альтернативный способ сравнения вхождения ip-адреса в группу}{post}Добрый день.  
Чтобы было проще искать в базе было придумано контрольное число, вычисляющиеся по формуле: ipnum = 16777216*w + 65536*x + 256*y + z      
где IP Address = w.x.y.z{/post}{/quote}Очень разумно, т.к. именно так IP-адрес выглядит с точкт зрения машины.  
 
Если отсортировать начальные адреса в порядке возрастания, то обычная ф-я ВПР с последним аргументом ИСТИНА найдет соотв. начальный адрес. Конечно, при условии, что справочник содержит все адреса, которые могут встретиться.
 
Приложенный файл показывает поиск по начальному адресу и, в качестве опции, поиск по конечному адресу и сравнение результатов, которое позволяет выявить адреса, не входящие в справочник.
 
ВПР, к сожалению, не подходит, так как появляются ошибки.
 
{quote}{login=Longines}{date=17.06.2010 10:15}{thema=}{post}ВПР, к сожалению, не подходит, так как появляются ошибки.{/post}{/quote}Ошибки можно обработать. Просто ВПР должно работать на 1-2 порядка быстрее, чем макрос.  
Можешь привести пример ошибки?
 
Диапазоны ip-адресов разрываемы. Из-за этого появляются ошибки.  
 
1-2 порядка? Это невозможно. 3000 ip-адресов, которые еще надо перевести в контрольные числа...
 
{quote}{login=Казанский}{date=17.06.2010 10:50}{thema=Re: }{post}{quote}{login=Longines}{date=17.06.2010 10:15}{thema=}{post}ВПР, к сожалению, не подходит, так как появляются ошибки.{/post}{/quote}Ошибки можно обработать. Просто ВПР должно работать на 1-2 порядка быстрее, чем макрос.  
Можешь привести пример ошибки?{/post}{/quote}  
 
впр - как раз очень неторопливая функция :)  
 
вообще, правильный макрос работает практически так же быстро, как и встроеная функция
Живи и дай жить..
 
{quote}{login=Longines}{date=17.06.2010 11:07}{thema=}{post}Диапазоны ip-адресов разрываемы. Из-за этого появляются ошибки.{/post}{/quote}Ты прочитал мои позавчерашние посты, посмотрел пример? Там как раз есть разрыв в диапазоне.    
{quote}{login=Longines}{date=17.06.2010 11:07}{thema=}{post}1-2 порядка? Это невозможно. 3000 ip-адресов, которые еще надо перевести в контрольные числа...{/post}{/quote}Я имел в виду только поиск "контрольного числа" по диапазону "контрольных чисел", который, как я понял, основное время занимает.
 
Че за фигня - в посте может быть только одна цитата?  
 
Я хотел сказать - в примере как раз есть разрыв в диапазоне.
 
если бы у нас было 18Гб оперативной памяти, то можно было бы просто все адреса занести в целочисленный массив размерности 4 и извлекать просто как            ip(w,x,y,z) :)
Живи и дай жить..
 
а зачем двойная проверка?  
 
If ip_arr(i, 1) <= er_arr(j, 1) And ip_arr(i, 1) >= br_arr(j, 1) Then  
 
 
не достаточно разве ip_arr(i, 1) <= er_arr(j, 1)? при упорядоченной-то записи оных?
Живи и дай жить..
 
а сколько всего диапазонов?  
 
и сколько стран?
Живи и дай жить..
 
Слэн, автор упомянул Maxmind, наверно это тут, "CSV format"  
http://www.maxmind.com/app/geolitecountry  
 
Кстати база "Updated monthly, at the beginning of each month", так что можно было бы сделать авт. обновление.
 
Там API тоже есть, в т.ч. для VB.NET: http://www.maxmind.com/app/vb  
Может, "все уже украдено до нас"?
 
Слэн, есть отсутствующие диапазоны (база ведь старая). Поэтому двойная проверка.  
 
Полная версия прилагается (6 мб).  
 
База не обновляется уже давно, на самом деле.  
 
Я пробовал смотреть скрипт на VB 7.0, но переделать под 6.0 не смог.  
 
http://www.fayloobmennik.net/53246
 
все равно проверка лишняя :)  
 
вторую надо делать только при выполнении первого условия, а не все время..  
 
но я уже в самом деле делал для телефонных номеров - там получилось в 500 раз быстрее сделать, чем у автора.. до долей секунды дошло..  
 
щас уже не интересно, только если на коммерческой основе :)
Живи и дай жить..
 
{quote}{login=слэн}{date=17.06.2010 02:22}{thema=}{post}  
щас уже не интересно, только если на коммерческой основе :){/post}{/quote}  
 
Ну для этого у меня есть другая задача :)  
Кстати, все говорят про ту тему с телефонными номерами, но не помнят, что она моя. :D  
Кстати, и в этой теме для вас отдельное спасибо: убрав второе условие вниз под еще один IF, я получил уменьшение времени в 2 раза... :)
Страницы: 1
Читают тему
Наверх