Что нового

Как посчитать количество уникальных записей за один проход. Уже замучался...

vaf

Новичок
Сообщения
190
Репутация
2
Хелп. Казалось бы простая задача, но замучался решать. Деградирую наверное.
В общем дан текстовый файл с IP адресами (примерно 80 тыс. строк)
пример:
192.168.121.88
192.168.121.88
192.168.58.67
192.168.27.80
192.168.5.19
192.168.5.19
192.168.121.88
192.168.58.67 и т.д.
нужно посчитать количество каждых уникальных строк (IP адресов), т.е.
192.168.121.88 - 3
192.168.58.67 - 2
192.168.27.80 - 1
192.168.5.19 - 2 и т.д.
пробовал сначала брать первую строку, пройтись по всем остальным, подсчитать кол-во, потом вторую, если она еще не бралась так же считать все... но это нужно будет на каждую строку проходить весь файл, а это 80 000 записей, в общем это будет на неделю. пробовал поместить все IP адреса в массив, но массив из 80 000 индексов, это просто комп. виснет.
Решил сделать так: берем 1ю строку, заносим ее в массив скажем ipaddr[1] = "192.168.121.88" в параллельный массив пишем
nnaddr[1] = 1, берем вторую строку, производим ее поиск в массиве, если такая нашлась, то nnaddr[1] += 1 если не нашлась значит она уникальна и значит добавляем элемент массива
_ArrayAdd ($ipaddr,"192.168.58.67"), а в параллельный nnaddr[2] = 1
и так далее. вся сложность в том, что не могу реализовать - наверное я заработался или уже голова от недосыпаний не варит.
может кто подскажет как реализовать, запутался уже...
заранее спасибо.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,487

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
Ещё один вариант :smile:
Код:
#include <Array.au3>
$hIP = FileOpen("ip.txt", 0)

If $hIP = -1 Then
    MsgBox(0, "Error", "Не удается открыть файл.")
    Exit
EndIf

$sIP = FileRead($hIP)
If @error = -1 Then Exit
$aIP = StringSplit(StringStripCR($sIP), @LF)
_ArrayDelete($aIP, 0)
$aUniqueIP = _ArrayUnique($aIP)
_ArrayDelete($aUniqueIP, 0)

$hReport = FileOpen("report.txt", 2)

For $i = 0 To UBound($aUniqueIP)-1
    $aResult = _ArrayFindAll($aIP, $aUniqueIP[$i])
    FileWriteLine($hReport, $aUniqueIP[$i] & " - " & UBound($aResult) & @CRLF)
Next

FileClose($hIP)
FileClose($hReport)
 

kzru_hunter

Осваивающий
Сообщения
144
Репутация
49
С помощью Assign будет намного быстрее сверяться (~350мс для слабого компа с кол-вом записей 10000).

Вот пример:

Код:
$hIpFile = FileRead("ip.txt")
$array = StringSplit($hIpFile, @CRLF, 1)
$count = 0

For $i=1 to $array[0]
	$array[$i] = StringStripWS($array[$i], 3)
	If not Eval("__ip_" & $array[$i]) Then $count += 1
	Assign("__ip_" & $array[$i], $i, 1)
Next

MsgBox(0,"", "$count = " & $count)
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,487
Garrett [?]
Ещё один вариант
Очень медленный.

kzru_hunter [?]
С помощью Assign будет намного быстрее сверяться
Но результат совсем не тот, где тут показано сколько раз дублируется каждый ip?

P.S
Оказывается, через Assign можно присвоить переменную вида $Var.1 :blink:
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
CreatoR [?]
Очень медленный.
Да, заметил. Ещё интересно и то, что как ваш вариант, так и мой, на слабых машинах при 80 тыс. строк загружает процессор на 100%! :(
 

kzru_hunter

Осваивающий
Сообщения
144
Репутация
49
CreatoR
Так ему же надо всего лишь подсчитать кол-во уникальных записей и не больше.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Ну, если у него все IP начинаются с "192.168.", то можно так (cчитает почти мгновенно!):

Код:
#Include <Array.au3>

$Timer = TimerInit()

$aList = StringSplit(StringStripCR(FileRead('IP_List.txt')), @LF)

Dim $aIP[256][256]

For $i = 1 To $aList[0]
	$aData = StringSplit($aList[$i], '.')
	If $aData[0] > 3 Then
		$aIP[Number($aData[3])][Number($aData[4])] += 1
	EndIf
Next
$sData = ''
For $i = 0 To 255
	For $j = 0 To 255
		If $aIP[$i][$j] Then
			$sData &= '192.168.' & $i & '.' & $j & ' - ' & $aIP[$i][$j] & '|'
		EndIf
	Next
Next
$aData = StringSplit(StringTrimRight($sData, 1), '|', 2)

ConsoleWrite(TimerDiff($Timer) & @CR)

_ArrayDisplay($aData)

Если все 4 цифры в IP произвольные, то можно так:

Код:
#Include <Array.au3>

$Timer = TimerInit()

$aList = StringSplit(StringStripCR(FileRead('IP_List.txt')), @LF)

For $i = 1 To $aList[0]
	$aNumb = StringSplit($aList[$i], '.')
	If $aNumb[0] > 3 Then
		$aList[$i] = BitShift(Number($aNumb[1]), -24) + BitShift(Number($aNumb[2]), -16) + BitShift(Number($aNumb[3]), -8) + Number($aNumb[4])
	Else
		$aList[$i] = 0
	EndIf
Next

Dim $aData[$aList[0] + 1][2] = [[0]]

For $i = 1 To $aList[0]
	If $aList[$i] Then
		For $j = 1 To $aData[0][0]
			If $aData[$j][0] = $aList[$i] Then
				$aData[$j][1] += 1
				ContinueLoop 2
			EndIf
		Next
		$aData[0][0] += 1
		$aData[$aData[0][0]][0] = $aList[$i]
		$aData[$aData[0][0]][1] = 1
	EndIf
Next

ReDim $aData[$aData[0][0] + 1][2]

For $i = 1 To $aData[0][0]
	$aData[$i][0] = BitAND(BitShift($aData[$i][0], 24), 255) & '.' & BitAND(BitShift($aData[$i][0], 16), 255) & '.' & BitAND(BitShift($aData[$i][0], 8), 255) & '.' & BitAND($aData[$i][0], 255)
Next

ConsoleWrite(TimerDiff($Timer) & @CR)

_ArrayDisplay($aData)

Тоже самое, но только с применением WinAPI:

Код:
#Include <Array.au3>

$Timer = TimerInit()

$aList = StringSplit(StringStripCR(FileRead('IP_List.txt')), @LF)

For $i = 1 To $aList[0]
    $aRet = DllCall('ws2_32.dll', 'ulong', 'inet_addr', 'str', $aList[$i])
    If Not @error Then
        $aList[$i] = $aRet[0]
    Else
        $aList[$i] = 0
    EndIf
Next

Dim $aData[$aList[0] + 1][2] = [[0]]

For $i = 1 To $aList[0]
    If $aList[$i] Then
        For $j = 1 To $aData[0][0]
            If $aData[$j][0] = $aList[$i] Then
                $aData[$j][1] += 1
                ContinueLoop 2
            EndIf
        Next
        $aData[0][0] += 1
        $aData[$aData[0][0]][0] = $aList[$i]
        $aData[$aData[0][0]][1] = 1
    EndIf
Next

ReDim $aData[$aData[0][0] + 1][2]

For $i = 1 To $aData[0][0]
    $aRet = DllCall('ws2_32.dll', 'str', 'inet_ntoa', 'ulong', $aData[$i][0])
    If Not @error Then
        $aData[$i][0] = $aRet[0]
    EndIf
Next

ConsoleWrite(TimerDiff($Timer) & @CR)

_ArrayDisplay($aData)

Результаты (файл в аттаче):

Способ 1 - 0.25 (!) сек
Способ 2 - 44/48 сек
Способ CreatoR'а - 96 сек
 

amel27

Продвинутый
Сообщения
146
Репутация
55
Garrett
как ваш вариант, так и мой, на слабых машинах при 80 тыс. строк загружает процессор на 100%
для одноядерных CPU так и должно быть... ну, если конечно мы хотим подсчитать побыстрее... :smile:

CreatoR
результат совсем не тот, где тут показано сколько раз дублируется каждый ip?
ну это можно подправить... ;)

Код:
Local $s="", $a=StringRegExp(FileRead("ip.txt"),"\S++",3)

For $i=0 to UBound($a)-1
	Assign($a[$i], Eval($a[$i])+1)
	If Eval($a[$i])=1 Then $s&= $a[$i] &@TAB&"$"& $a[$i] &"$"&@CRLF
Next

$ExpandVarStrings = Opt("ExpandVarStrings",1)
ConsoleWrite($s &@CRLF)
Opt("ExpandVarStrings", $ExpandVarStrings)
 

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
Автор
V

vaf

Новичок
Сообщения
190
Репутация
2
CreatoR - большой респект, элегантно реализовано. Взял для примера файл из 20 тыс. строк, обработал оч.быстро

Redline
_FileReadToArray('iplist.txt', $aList_1)
я уже пробовал вариант с массивами, не дождался пока он 80000 записей в массив затолкает, это для мощных компов :smile:
 

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
vaf [?]
RedlineЦитата
_FileReadToArray('iplist.txt', $aList_1)
я уже пробовал вариант с массивами, не дождался пока он 80000 записей в массив затолкает, это для мощных компов
_FileReadToArray('iplist.txt', $aList_1) равноценна
Код:
$sList = StringStripCR(FileRead("IP_List.txt")) & @LF
$aList = StringSplit($sList, @LF)

А тормоза возможны из-за
Код:
_ArrayDisplay($aList_1)
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,487
Yashied [?]
Способ CreatoR'а - 96 сек
Я не знаю как у тебя такое получилось, но у меня с твоим файлом результат другой (36870.3000690993).
 

amel27

Продвинутый
Сообщения
146
Репутация
55
:gathering:
всё равно наш быстрее и короче :blum:
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,487
amel27 [?]
ну это можно подправить
Ухты!

Вот получился самый быстрый вариант с требуемым результатом:

Код:
#Include <Array.au3>

$Timer = TimerInit()

;=================================================================================================
$aList = StringSplit(StringStripCR(StringStripWS(FileRead(@DesktopDir & "\ip_list.txt"), 3)), @LF)
$sList = ""

For $i = 1 To $aList[0]
    Assign($aList[$i], Eval($aList[$i])+1)
	
    If Eval($aList[$i]) = 1 Then
		$sList &= $aList[$i] & "|$" & $aList[$i] & "$" & @LF
	EndIf
Next

$iOpt_EVS = Opt("ExpandVarStrings", 1)
$aList = StringSplit(StringStripWS($sList, 2), @LF)
Opt("ExpandVarStrings", $iOpt_EVS)

Dim $aDupsArray[$aList[0]+1][2] = [[$aList[0]]]

For $i = 1 To $aList[0]
	$aSplit = StringSplit($aList[$i], "|")
	$aDupsArray[$i][0] = $aSplit[1]
	$aDupsArray[$i][1] = $aSplit[2]
Next
;=================================================================================================

ConsoleWrite(TimerDiff($Timer) & @LF)
_ArrayDisplay($aDupsArray)
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
CreatoR сказал(а):
Я не знаю как у тебя такое получилось, но у меня с твоим файлом результат другой (36870.3000690993).

У меня 4 ядра и, как следствие, 25% загрузки.
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
kzru_hunter Отличное решение!
amel27 Хорошо скорректировано!
:ok:

IMHO вердикт.
Великолепное решение поставленной проблемы в данной теме!
Всё генеральное просто :smile:
Ноутбук 1.5 GHz 256 Mb результат скрипта ниже (на основе решений от kzru_hunter и amel27) составляет 3 сек для 80 000 строк CPU 100%
Код:
$Timer = TimerInit()

$hList = FileOpen("ip.txt", 0)
If $hList = -1 Then
    MsgBox(0, "Error", "Не удается открыть файл.")
    Exit
EndIf

$hReport = FileOpen("report.txt", 2)

$sList = FileRead($hList)
$aList = StringSplit(StringStripCR(StringStripWS($sList, 3)), @LF)
$sList = _UniqueValues($aList)

FileWrite($hReport, $sList)

FileClose($hList)
FileClose($hReport)
ConsoleWrite(Round(TimerDiff($Timer)/1000) & @LF)


Func _UniqueValues($a_Values)
    
    Local $sList = ""
    If Not IsArray($a_Values) Then Return SetError(1, 0, 0)

    For $i = 1 To UBound($a_Values) -1
        Assign($a_Values[$i], Eval($a_Values[$i])+1)
        If Eval($a_Values[$i]) = 1 Then
            $sList &= $a_Values[$i] & "|" & "$" & $a_Values[$i] & "$" & @CRLF
        EndIf
    Next

    $ExpandVarStrings = Opt("ExpandVarStrings",1)
        Return SetError(0, 0 , $sList)
    Opt("ExpandVarStrings", $ExpandVarStrings)
    
EndFunc


Однако я тоже был удивлен, как и CreatoR [?]
Оказывается, через Assign можно присвоить переменную вида $Var.1
в отношение Assign :blink: Интересный факт, упрощающий многие решения :smile:


Добавлено:
Сообщение автоматически объединено:

Ноутбук 1.5 GHz 256 Mb
Yashied
1 способ 3.647 сек. CPU 100%
2 способ terminated time out 5 min CPU 100%
3 способ terminated time out 5 min CPU 100%

CreatoR
1 способ terminated time out 5 min CPU 100%

Redline
1 способ 5 сек. CPU 100%
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,487
Yashied [?]
У меня 4 ядра и, как следствие, 25% загрузки.
Ты хочешь сказать, что 4 ядерный процессор отрабатывает медленнее чем один разделённый на двое (у меня так :smile:)?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
CreatoR сказал(а):
Yashied [?]
У меня 4 ядра и, как следствие, 25% загрузки.
Ты хочешь сказать, что 4 ядерный процессор отрабатывает медленнее чем один разделённый на двое (у меня так :smile:)?

Да. У меня на QX9650 получается медленнее, чем тоже самое на каком-то P4 c HyperThreading и с меньшей тактовой частотой. Факт. Возможно еще сказалась Windows 7...
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,487
Yashied [?]
Хм, только что проверил на ноутбуке с процессором i3 (под Win7), действительно там медленнее, мой скрипт отработал 143 секунды, а твой 63.

Это ставит под вопрос оптимизацию скриптов. Я надеюсь это связанно только с Win7, хотя в любом случае получается несхожесть, ведь на том же Pentium Dual Core (под WinXP) мой пример оказался быстрее немного, и как теперь знать, для какой системы/машины какой использовать метод оптимизаций?
 
Верх