Что нового

[Данные, строки] Генератор комбинаций символов из входной строки

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
AutoIt: 3.3.0
Версия: 1.0

Категория: Разное

Описание: Утилита для перебора символов из входной строки с задаваемой длиной выходных слов. Есть возможность удалять повторяющиеся символы из входной строки и словах на выходе. Полученные слова могут сохраняться напрямую файл(рекомендуется если комбинаций больше нескольких миллионов, иначе выдача в GUI занимает много времени). Прерывание работы скрипта при переборе - Ctrl-F9.

Код:
Код:
#include <EditConstants.au3>
#include <GUIConstants.au3>
#include <WindowsConstants.au3>
#include <ButtonConstants.au3>
#include <ComboConstants.au3>
#include <StaticConstants.au3>
#include <ProgressConstants.au3>

Global $sCombinations, $iMaxLength, $iMinLength, $fNoMix, $fIgnore, $sWord, $fStop, $iTotal
Global $iCount, $iSimbols, $aS, $sOut, $hFile = @ScriptDir & '\dumb.txt', $hFileWr

$hGUI = GUICreate('Великий комбинатор', 480, 680)
GUICtrlCreateLabel('Введите символы:', 5, 5, 120, 21)
$hLabelCombs = GUICtrlCreateLabel('Количество возможных комбинаций - ', 150, 5, 300, 21)
$hInput = GUICtrlCreateInput('', 5, 25, 470, 17, -1, $WS_EX_STATICEDGE)
GUICtrlCreateLabel('Мин. длина слова:', 5, 50, 110, 21)
$hComboMin = GUICtrlCreateCombo('2', 120, 45, 40, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
GUICtrlSetData(-1, '3|4|5|6|7|8|9|10', '2')
GUICtrlCreateLabel('Макс. длина слова:', 5, 70, 110, 21)
$hComboMax = GUICtrlCreateCombo('2', 120, 65, 40, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
GUICtrlSetData(-1, '3|4|5|6|7|8|9|10', '3')
$hCheckUniq = GUICtrlCreateCheckbox('Повторять символы в словах на выходе', 170, 45, 300, 20)
GUICtrlSetState(-1, $GUI_CHECKED)
$hCheckTrim = GUICtrlCreateCheckbox('Игнорировать одинаковые символы входной строки', 170, 65, 300, 20)
GUICtrlSetState(-1, $GUI_CHECKED)
GUICtrlCreateLabel('Вывод результата:', 5, 90, 110, 21)
$hComboOutput = GUICtrlCreateCombo('Только в окно программы', 120, 85, 200, 20, BitOR($GUI_SS_DEFAULT_COMBO, $CBS_DROPDOWNLIST))
GUICtrlSetData(-1, 'Только в файл|В окно программы и в файл', 'Только в окно программы')
$hFileOutput = GUICtrlCreateLabel(@ScriptDir & '\out.txt', 5, 110, 470, 17, -1, $WS_EX_STATICEDGE)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlCreateLabel('Слова:', 5, 130, 100, 21)
$hOutput = GUICtrlCreateEdit('', 5, 150, 470, 490, $ES_READONLY + $WS_VSCROLL, $WS_EX_STATICEDGE)
$hProgress = GUICtrlCreateProgress(5, 650, 360, 20, $PBS_SMOOTH)
$hStart = GUICtrlCreateButton('Генерировать', 375, 645, 100, 30, $BS_DEFPUSHBUTTON + $BS_FLAT)
GUISetState(@SW_SHOW)
GUIRegisterMsg($WM_COMMAND, 'WM_COMMAND')

While 1
	$msg = GUIGetMsg()
	Select
		Case $msg = -3
			Exit
		Case $msg = $hComboOutput
			If StringInStr(GUICtrlRead($hComboOutput), 'файл') Then
				GUICtrlSetState($hFileOutput, $GUI_ENABLE)
				GUICtrlSetData($hOutput, 'Кликните по ссылке к файлу, чтобы изменить путь')
			Else
				GUICtrlSetState($hFileOutput, $GUI_DISABLE)
			EndIf
		Case $msg = $hFileOutput
			$sFileOut = FileSaveDialog('Выберите выходной файл:', @ScriptDir, 'Text (*.txt)', 16, 'out.txt')
			If $sFileOut = '' Then
				GUICtrlSetData($hFileOutput, @ScriptDir & '\out.txt')
			Else
				GUICtrlSetData($hFileOutput, $sFileOut)
			EndIf
		Case $msg = $hStart
			GUISetState(@SW_DISABLE)
			If GUICtrlRead($hInput) = '' Or StringLen(GUICtrlRead($hInput)) = 1 Then
				MsgBox(0, 'Ошибка', 'Количество введенных символов слишком мало')
			Else
				GUICtrlSetData($hOutput, '')
				$sCombinations = GUICtrlRead($hInput)
				$iMinLength = Number(GUICtrlRead($hComboMin))
				$iMaxLength = Number(GUICtrlRead($hComboMax))
				If $iMinLength > $iMaxLength Then
					MsgBox(0, 'Ошибка', 'Минимум не может превышать максимум')
				Else
					If GUICtrlRead($hCheckUniq) = $GUI_CHECKED Then
						$fNoMix = False
					Else
						$fNoMix = True
					EndIf
					If GUICtrlRead($hCheckTrim) = $GUI_CHECKED Then
						$fIgnore = True
					Else
						$fIgnore = False
					EndIf
					WinSetTitle($hGUI, '', 'Великий комбинатор - [ нажмите Ctrl-F9 для отмены ]')
					$fStop = False
					HotKeySet('^{F9}', '_Terminate')
					GUICtrlSetData($hOutput, 'Обработка...')
					_generate($sCombinations, $iMinLength, $iMaxLength, $fNoMix, $fIgnore)
					If @error = 1 Then
						MsgBox(0, 'Ошибка', 'Введенных символов недостаточно' & @CRLF & 'отмените опцию <Повторять символы в словах> и запустите снова')
						GUICtrlSetData($hOutput, '')
					EndIf
					WinSetTitle($hGUI, '', 'Великий комбинатор')
					HotKeySet('^{F9}')
				EndIf
			EndIf
			GUISetState(@SW_ENABLE)
			GUICtrlSetState($hInput, $GUI_FOCUS)
			GUICtrlSetData($hProgress, 0)
	EndSelect
WEnd

Func _generate($sAlphabet, $iLengthMin, $iLengthMax, $fMx, $fIgn)
	If $fIgn Then ; перевод алфавита в массив
		$sAlphabet_new = ''
		For $m = 1 To StringLen($sAlphabet) ; удаление повторяющихся символов, если выбрана данная опция
			$sChar = StringMid($sAlphabet, $m, 1)
			If Not StringInStr($sAlphabet_new, $sChar) Then $sAlphabet_new &= $sChar
		Next
		$aS = StringSplit($sAlphabet_new, '')
	Else
		$aS = StringSplit($sAlphabet, '')
	EndIf
	Global $iSimbols = UBound($aS) - 1 ; длина алфавита
	$iTotal = _countofcombs($iSimbols, $iLengthMin, $iLengthMax, Not $fMx) ; общее количество комбинаций
	If $iTotal <= 0 Then ; проверка соответствия длины алфавита к ограничителям длины слова
		Return SetError(1)
	EndIf
	If $iTotal > 50000 Then
		If FileExists($hFile) Then FileDelete($hFile) ; очистка файла под временное хранение данных
		Global $hFileWr = FileOpen($hFile, 1) ; открытие файла на запись с допиской в конец файла
	EndIf
	Global $iMinLength = $iLengthMin ; минимальная длина слова
	Global $iMaxLength = $iLengthMax ; максимальная длина слова
	Global $fNoMix = $fMx ; флаг уникальности всех символов в слове
	Global $sOut = '' ; выходной текст
	For $z = 1 To $iSimbols ; цикл перебора алфавита
		GUICtrlSetData($hProgress, Round(100 * ($z / $iSimbols), 0))
		_solid($aS[$z])
		If @error = 1 Then ExitLoop
		$iMaxLength = $iLengthMax ; сброс счетчика максимальной длины слова
	Next
	If $iTotal > 50000 Then
		GUICtrlSetData($hOutput, 'Выдача результата...')
		FileWrite($hFileWr, $sOut) ; запись последней порции слов в файл
		FileClose($hFileWr)
		If StringInStr(GUICtrlRead($hComboOutput), 'файл') Then FileCopy($hFile, GUICtrlRead($hFileOutput), 9) ; заносим все слова в файл
		If StringInStr(GUICtrlRead($hComboOutput), 'окно') Then
			GUICtrlSetData($hOutput, FileRead($hFile)) ; заносим все слова в Edit
		Else
			GUICtrlSetData($hOutput, 'Данные успешно записаны в файл:' & @CRLF & GUICtrlRead($hFileOutput)) ; заносим все слова в Edit
		EndIf
		FileClose($hFile)
		FileDelete($hFile)
	Else
		GUICtrlSetData($hOutput, 'Выдача результата...')
		If StringInStr(GUICtrlRead($hComboOutput), 'файл') Then
			$hFileOpen = FileOpen(GUICtrlRead($hFileOutput), 2)
			FileWrite($hFileOpen, $sOut) ; заносим все слова в файл
			FileClose($hFileOpen)
		EndIf
		If StringInStr(GUICtrlRead($hComboOutput), 'окно') Then
			GUICtrlSetData($hOutput, $sOut) ; заносим все слова в Edit
		Else
			GUICtrlSetData($hOutput, 'Данные успешно записаны в файл:' & @CRLF & GUICtrlRead($hFileOutput)) ; заносим все слова в Edit
		EndIf
	EndIf
EndFunc   ;==>_generate

Func _solid($sIn) ; рекурсивная функция перебора символов алфавита с занесением полученных в текст
	If $fStop Then Return SetError(1)
	If $iMaxLength = 1 Then ; достигнута максимальная длина слова
		Return
	ElseIf StringLen($sOut) > 100000 And $iTotal > 50000 Then ; если объем текста превышает 100К символов, то заносим его в файл(экономия ресурсов памяти)
		FileWrite($hFileWr, $sOut)
		$sOut = ''
	Else
		$iMaxLength -= 1 ; длина слова в следующем цикле увеличивается, соответсвенно уменьшаем счетчик
		For $i = 1 To $iSimbols
			$sWord = $sIn & $aS[$i] ; дописываем символ к уже существующей строке
			If StringLen($sWord) >= $iMinLength Then ; если длина слова удовлетворяет минимальной длине...
				If $fNoMix And Not StringRegExp($sWord, '(.).*\1', 0) Then ; если включена проверка на уникальность символов в слове и проверка пройдена
					$sOut &= $sWord & @CRLF ; заносим полученное слово в текст
					_solid($sWord) ; рекурсия
				ElseIf Not $fNoMix Then ; проверка на уникальность выключена
					$sOut &= $sWord & @CRLF
					_solid($sWord) ; рекурсия
				EndIf
			Else ; если длина слова меньше минимальной, то сразу идем глубже
				If $fNoMix And Not StringRegExp($sWord, '(.).*\1', 0) Then ; если включена проверка на уникальность символов в слове и проверка пройдена
					_solid($sWord) ; рекурсия
				ElseIf Not $fNoMix Then ; проверка на уникальность выключена
					_solid($sWord) ; рекурсия
				EndIf
			EndIf
		Next
		$iMaxLength += 1 ; после перебора всех символов алфавита плюсуем максимальную длину слова, иначе не будет вызвана следующая рекурсия,
		; для предыдущего более короткого слова, как-то так =))))
	EndIf
EndFunc   ;==>_solid

Func WM_COMMAND($hWnd, $nMsg, $wParam, $lParam)
	Local $nNotifyCode = BitShift($wParam, 16)
	Local $nID = BitAND($wParam, 0xFFFF)
	Local $hCtrl = $lParam

	Switch $nID
		Case $hInput, $hComboMin, $hComboMax, $hCheckUniq ; были внесены изменения в данных элементах
			Switch $nNotifyCode
				Case $EN_UPDATE, $CBN_SELCHANGE, $BN_CLICKED ; сообщения: обновление для InputBox, выбор элемента для ComboBox, клик по CheckBox
					If GUICtrlRead($hCheckUniq) = $GUI_CHECKED Then
						$fUn = True
					Else
						$fUn = False
					EndIf
					If GUICtrlRead($hCheckTrim) = $GUI_CHECKED Then
						$sInp_old = GUICtrlRead($hInput)
						$sInp_new = ''
						For $d = 1 To StringLen($sInp_old)
							$sChar = StringMid($sInp_old, $d, 1)
							If Not StringInStr($sInp_new, $sChar, 1) Then $sInp_new &= $sChar
						Next
					Else
						$sInp_new = GUICtrlRead($hInput)
					EndIf
					$iInp_new = StringLen($sInp_new)
					$iMinimum = GUICtrlRead($hComboMin)
					$iMaximum = GUICtrlRead($hComboMax)
					$iResult = _countofcombs($iInp_new, $iMinimum, $iMaximum, $fUn)
					GUICtrlSetData($hLabelCombs, 'Количество возможных комбинаций - ' & StringRegExpReplace($iResult, '(\d)(?=(\d{3})+$)', '$1 '))
			EndSwitch
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

Func _countofcombs($iInp, $iMin, $iMax, $fUniq) ; немного комбинаторики
	If $fUniq Then ; на выходе строки без повторяющихся символов
		$iCombs = 0
		For $y = $iMin To $iMax
			$iCombs += $iInp ^ $y
		Next
		Return $iCombs ; NumCombs = n^k + n^(k-1) + n^(k-2) + ...
	Else ; все строки
		$iCombs = 0
		$iFactor_max = 1
		For $h = 2 To $iInp
			$iFactor_max *= $h
		Next
		For $y = $iMin To $iMax
			If $iInp >= $y Then
				$iFactor_next = 1
				For $f = 1 To ($iInp - $y)
					$iFactor_next *= $f
				Next
				$iCombs += $iFactor_max / $iFactor_next
			EndIf
		Next
		Return $iCombs ; NumComb = n!/(n-k)! + n!/(n-(k-1))! + n!/(n-(k-2))! + ...
	EndIf
EndFunc   ;==>_countofcombs

Func _terminate()
	$fStop = True
	GUICtrlSetData($hOutput, 'Обработка прервана пользователем!!' & @CRLF)
EndFunc   ;==>_terminate

Снимок:


Автор: Redline
 

ViktorSPB

Новичок
Сообщения
109
Репутация
0
Redline, здравствуйте! Спасибо за утилиту!
Скажите, пожалуйста, в какой строке формируется следующая комбинация? Мне нужно включить правило на формирование..
Код:
For $i = 1 To $iSimbols
            $sWord = $sIn & $aS[$i] ; дописываем символ к уже существующей строке
            If StringLen($sWord) >= $iMinLength Then ; если длина слова удовлетворяет минимальной длине...
                If $fNoMix And Not StringRegExp($sWord, '(.).*\1', 0) Then ; если включена проверка на уникальность символов в слове и проверка пройдена
;***************
; ПРАВИЛО СЮДА???                     
;***************
$sOut &= $sWord & @CRLF ; заносим полученное слово в текст
                    _solid($sWord) ; рекурсия
                ElseIf Not $fNoMix Then ; проверка на уникальность выключена
                    $sOut &= $sWord & @CRLF
                    _solid($sWord) ; рекурсия
                EndIf
            Else ; если длина слова меньше минимальной, то сразу идем глубже
                If $fNoMix And Not StringRegExp($sWord, '(.).*\1', 0) Then ; если включена проверка на уникальность символов в слове и проверка пройдена
                    _solid($sWord) ; рекурсия
                ElseIf Not $fNoMix Then ; проверка на уникальность выключена
                    _solid($sWord) ; рекурсия
                EndIf
            EndIf
        Next


В общем, под свою задачу сделал, но кривовато) Идеальным было б появление в скрипте зоны для внесения правила. Но это только пожелание, утилита и так отличная!
Еще выяснил, что проверка на одинаковые символы не чувствительна к регистру. Хорошо, что проверил)
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
Правило для формирования или для проверки подходит ли данное слово под какой-то шаблон?
По первому варианту сложно что-то сказать, т.к. все формирование происходит перебором по порядку, а вот второе можно добавить в указанное вами место, если нужно продолжать генерацию строк, или вставить правило в это условие
Код:
If $fNoMix And Not StringRegExp($sWord, '(.).*\1', 0) Then

и тогда дальнейшая генерация не пойдет.

ViktorSPB, напиши подробнее какие рода правила нужны, посмотрю что можно сделать

А вообще изначально утилита создавалась для генерации pin-кода пластиковой карты, который я забыл, но помнил некоторые цифры :smile: , поэтому с формированием я не заморачивался.
 

ViktorSPB

Новичок
Сообщения
109
Репутация
0
PIN это круто))))
Да, мне нужно было, чтобы комбинаци в файл записывались только те, которые удовлетворяют шаблону. Например в пароле должны быть буквы И цифры. Соответственно, внес условие и только цифровые или только буквенные комбинации игнорируются) Добавил Sleep, чтобы не грузить ядро полностью, и сделал окно доступным, чтобы можно было его двигать по экрану и сварачивать) Когда, надеюсь, скоро))) все комбинации будут записаны, буду опять же по условиям перебирать, подбирать забытый пароль, зная несколько значений. Так что дорабатывать наверное не надо, но блок закомментированный для внесения шаблона пароля, думаю, не помешал бы) Вводить этот шаблон в интерфейс, задачка, думаю не простая.. Ведь надо будет тогда по-хорошему как-то расчитывать общее кол-во комбинаций, расширять этот шаблон по И,ИЛИ,НЕ.. В общем, задачка усложнится. Так что можно оставить так) И так супер утилитка!
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
ViktorSPB
Понятно, имеется ввиду подбор по маске (п*р*ль), где можно ограничивать значение каждой позиции списком. Как-то уже думал над этим, но лень, может вернусь к реализации.
Загрузку процессора одним процессом можно регулировать утилитой BES, рекомендую.
 

ViktorSPB

Новичок
Сообщения
109
Репутация
0
Понял, приберегу)) Еще нюанс полезный можно добавить в утилиту) Но я сам сделаю, когда закончится формирование файла :stars:
Я бы в окошко вывел на одну строку номер порядковый и значение сформированного пароля.. А то вот жду уже несколько часов, полоса прогресса в саааааамом начале) и никаких подсказок, что что-то делается нет))
 
Верх