Что нового

Сравнить две строки и показать разницу между ними

Suppir

Продвинутый
Сообщения
967
Репутация
62
Есть две переменные:

Код:
$line1 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений в отдельные муниципальные правовые акты"'

$line2 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесений изменений и дополнений в отдельные муниципальные правовые акты"'


Необходимо сравнить их и показать, где именно эти строки отличаются.


Результат должен выглядеть примерно так:

Код:
$result = '
Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений в отдельные муниципальные правовые акты"
Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесени<<й>> изменений <<и дополнений>> в отдельные муниципальные правовые акты"
'


OffTopic:
Это довольно сложная задача. Существуют программы, которые делают сравнения файлов (например, WinMerge), но нужно реализовать хотя бы простенькую функцию на AutoIt.



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

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

SECTOR

Продвинутый
Сообщения
398
Репутация
58
Как то так? :D
Код:
$line1 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений в отдельные муниципальные правовые акты"'
$line2 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесений изменений и дополнений в отдельные муниципальные правовые акты"'

MsgBox(0,"Сравнение","Различия в первой строке: "&_MyFunc($line1,$line2)&@CRLF&@CRLF& _
					"Различия во второй строке: "&_MyFunc($line2,$line1))

Func _MyFunc($s1,$s2)

	If Not IsString($s1) Or $s1 = "" Then Return SetError(1,1,"")
	If Not IsString($s2) Or $s2 = "" Then Return SetError(1,2,"")

	Local $a = StringSplit($s1," "), $ret = ""

	For $n = 1 To $a[0]
		If Not StringInStr($s2,$a[$n]) Then
			$ret &= "<<"&$a[$n]&">> "
		Else
			$ret &= $a[$n]&" "
		EndIf
	Next

	If StringRight($ret,1) = " " Then $ret = StringTrimRight($ret,1)

	Return $ret

EndFunc



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

А вообще, мне кажется, что интелектуально сравнить две малооличающиеся строки ооооооочень сложно... Причин много...
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
SECTOR
Почему-то "и" не захватилось в угловые скобки? Хотя этого "и" нет в первой строке.
 

SECTOR

Продвинутый
Сообщения
398
Репутация
58
Suppir, да эт я побаловался) там идет поиск по словам... В случае с "и" ищется "и" а не "_и_" (_-пробел)... Сделать так чтоб находились все отличия как надо, очень трудно... Ведь даже предложения могут быть составлены по разному, значит строки для сравнения будут совсем разными... Я если чесно тут безсилен((, но может кто нибудь сможет осуществить)
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Будем ждать местного гуру по обработке текста, который покажет нам, как можно решить эту задачу :smile:
 

dwerf

Использует ArchLinux
Сообщения
478
Репутация
218
"О внесений изменений и дополнений..."
Подпись: ... Евгений

Попробовал написать, сам не понял что получилось.
Задумка была такая:

Из предложения номер 1 создаётся регулярное выражение "слово1 .*слово2 .* слово3".
Проверяется соответствует ли предложение номер 2 этому шаблону.

Если да, то в первом предложении нет слов которые надо было бы выделять.
Если нет, то выражение составляется без первого слова: "слово2 .*слово3"

Проверяется соответствует ли предложение номер 2 этому шаблону.
Если да, то выделяется слово, которое было удалено из выражения.
Если нет то удаляется следующее слово (потом два слова, три слова итд.)

И так продолжается, пока второе предложение не будет соответствовать шаблону.

Потом предложения меняются местами.
Код:
#include <array.au3>

$line1 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений в отдельные муниципальные правовые акты"'

$line2 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений и дополнений в отдельные муниципальные правовые акты"'


If $line1 = $line2 Then MsgBox(0, '', $line1 & @CRLF & $line2)

Local $Result1, $Result2
Local $aTokens1, $aTokens2

$aTokens1 = StringRegExp(StringReplace($line1, '.', '\.'), '[^ ]+', 3)
$aTokens2 = StringRegExp(StringReplace($line2, '.', '\.'), '[^ ]+', 3)
Dim $aExclude1[UBound($aTokens1)], $aExclude2[UBound($aTokens2)]

MsgBox(0, '', Search($line2, $aTokens1, $aExclude1) & @CRLF & Search($line1, $aTokens2, $aExclude2))

Func Search(ByRef $sLine, ByRef $aTokens, ByRef $aExclude, $iExclude = 0)
	For $i = 0 To UBound($aExclude)-1 Step +1
		If $i < $iExclude Then
			$aExclude[$i] = 1
		Else
			$aExclude[$i] = 0
		EndIf
	Next

	Do
		$sExpression = CreateExpression($aTokens, $aExclude)
		If StringRegExp($sLine, $sExpression) Then
			Return CreateString($aTokens, $aExclude)
		EndIf
	Until ExcludeNext($aExclude, $iExclude) = -1

	Return Search($sLine, $aTokens, $aExclude, $iExclude+1)
EndFunc

Func CreateExpression(ByRef $aTokens, ByRef $aExclude)
	Local $sRet
	For $i = 0 To UBound($aTokens)-1 Step +1
		If $aExclude[$i] = 0 Then $sRet &= $aTokens[$i] & ' .*'
	Next

	$sRet = StringTrimRight($sRet, 3)
	Return $sRet
EndFunc

Func ExcludeNext(ByRef $aExclude, $iExclude, $iLevel = 1)
	If $iLevel > $iExclude Then Return -1

	If $aExclude[UBound($aExclude)-$iLevel] = 1 Then
		$aExclude[UBound($aExclude)-$iLevel] = 0
		If ExcludeNext($aExclude, $iExclude, $iLevel+1) = -1 Then Return -1
		For $i = UBound($aExclude)-$iLevel-1 To 0 Step -1
			If $aExclude[$i] = 1 Then
				$aExclude[$i+1] = 1
				Return
			EndIf
		Next
	Else
		For $i = UBound($aExclude)-1 To 0 Step -1
			If $aExclude[$i] = 1 Then
				$aExclude[$i] = 0
				$aExclude[$i+1] = 1
				Return
			EndIf
		Next
	EndIf
EndFunc

Func CreateString(ByRef $aTokens, ByRef $aExclude)
	Local $sRet = ''
	Local $fDif = False
	For $i = 0 To UBound($aTokens)-1 Step +1
		If Not $fDif And $aExclude[$i] = 1 Then
			$sRet &= '<<'
			$fDif = True
		ElseIf $fDif And $aExclude[$i] = 0 Then
			$sRet = StringTrimRight($sRet, 1)
			$sRet &= '>> '
			$fDif = False
		EndIf
		$sRet &= $aTokens[$i] & ' '
	Next
	Return $sRet
EndFunc
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
dwerf
код работает, но у тебя изменены $line1 и $line2. Если поменять изначальное условие:

$line1 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений в отдельные муниципальные правовые акты"'

$line2 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесений изменений и дополнений в отдельные муниципальные правовые акты"'

то захватывается лишнее слова "изменений" (хотя оно не менялось), а союз "и" не захватывается, хотя должен. Сам алгоритм довольно интересный.


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

Вот как справился WinMerge:



Как он это делает? :scratch:

Вот такую еще штуку нашел (теоретические выкладки алгоритма разницы между двумя строками)
http://www.xmailserver.org/diff2.pdf
 

VladUs

Скриптер
Сообщения
621
Репутация
180
Может быть проще считывать строки в массив байтов.
А далее уже сравнивать побайтно один массив байтов с другим массивом байтов ? :scratch:
И там где несовпадение - отмечать это.
 

dwerf

Использует ArchLinux
Сообщения
478
Репутация
218
Исправил регулярное выражение, теперь вроде бы работает правильно.

Код:
$line1 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений в отдельные муниципальные правовые акты"'

$line2 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесений изменений и дополнений в отдельные муниципальные правовые акты"'


If $line1 = $line2 Then MsgBox(0, '', $line1 & @CRLF & $line2)

Local $Result1, $Result2
Local $aTokens1, $aTokens2

$aTokens1 = StringRegExp(StringReplace($line1, '.', '\.'), '[^ ]+', 3)
$aTokens2 = StringRegExp(StringReplace($line2, '.', '\.'), '[^ ]+', 3)
Dim $aExclude1[UBound($aTokens1)], $aExclude2[UBound($aTokens2)]

MsgBox(0, '', Search($line2, $aTokens1, $aExclude1) & @CRLF & Search($line1, $aTokens2, $aExclude2))

Func Search(ByRef $sLine, ByRef $aTokens, ByRef $aExclude, $iExclude = 0)
	For $i = 0 To UBound($aExclude)-1 Step +1
		If $i < $iExclude Then
			$aExclude[$i] = 1
		Else
			$aExclude[$i] = 0
		EndIf
	Next

	Do
		$sExpression = CreateExpression($aTokens, $aExclude)
		If StringRegExp($sLine, $sExpression) Then
			Return CreateString($aTokens, $aExclude)
		EndIf
	Until ExcludeNext($aExclude, $iExclude) = -1

	Return Search($sLine, $aTokens, $aExclude, $iExclude+1)
EndFunc

Func CreateExpression(ByRef $aTokens, ByRef $aExclude)
	Local $sRet
	For $i = 0 To UBound($aTokens)-1 Step +1
		If $aExclude[$i] = 0 Then $sRet &= $aTokens[$i] & ' (?:.* )?'
	Next
	$sRet = StringTrimRight($sRet, 9)
	Return $sRet
EndFunc

Func ExcludeNext(ByRef $aExclude, $iExclude, $iLevel = 1)
	If $iLevel > $iExclude Then Return -1

	If $aExclude[UBound($aExclude)-$iLevel] = 1 Then
		$aExclude[UBound($aExclude)-$iLevel] = 0
		If ExcludeNext($aExclude, $iExclude, $iLevel+1) = -1 Then Return -1
		For $i = UBound($aExclude)-$iLevel-1 To 0 Step -1
			If $aExclude[$i] = 1 Then
				$aExclude[$i+1] = 1
				Return
			EndIf
		Next
	Else
		For $i = UBound($aExclude)-1 To 0 Step -1
			If $aExclude[$i] = 1 Then
				$aExclude[$i] = 0
				$aExclude[$i+1] = 1
				Return
			EndIf
		Next
	EndIf
EndFunc

Func CreateString(ByRef $aTokens, ByRef $aExclude)
	Local $sRet = ''
	Local $fDif = False
	For $i = 0 To UBound($aTokens)-1 Step +1
		If Not $fDif And $aExclude[$i] = 1 Then
			$sRet &= '<<'
			$fDif = True
		ElseIf $fDif And $aExclude[$i] = 0 Then
			$sRet = StringTrimRight($sRet, 1)
			$sRet &= '>> '
			$fDif = False
		EndIf
		$sRet &= $aTokens[$i] & ' '
	Next
	Return $sRet
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
Вот что у меня получилось.

Написал простую функцию поиска отличий в строке (_StringCompare - возвращает массив отличий и их позиций в строках), и применил к выдаваемому ей результату мою библиотеку GUICtrlCreateTFLabel.au3. Получилось вроде неплохо (хотя ошибки могут быть, особо не тестил):



Код:
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <Array.au3>

#include "GUICtrlCreateTFLabel.au3"

$iGUI_Width = @DesktopWidth - 200
$iGUI_Height = 600

$hGUI = GUICreate("_StringCompare with _GUICtrlCreateTFLabel Example", $iGUI_Width, $iGUI_Height)

$sString1 = "This is a test1, test of _StringCompare functiond"
$sString2 = "This is a quest, qest of _StringCompare functions"

;~ $sString1 = _
;~ 	'Постановление администрации муниципального образования город ' & _
;~ 	'Краснодар от 16 июня 2011 г. N 4226 "О внесении изменений в отдельные муниципальные правовые акты"'

;~ $sString2 = _
;~ 	'Постановление администрации муниципального образования город ' & _
;~ 	'Краснодар от 16 июня 2011 г. N 4226 "О внесений изменений и дополнений в отдельные муниципальные правовые акты"'

$aCompare = _StringCompare($sString1, $sString2)
;~ _ArrayDisplay($aCompare)

Dim $aColors[5] = ['green', 'red', 'purple', 'pink', 'blue']
$iClr_Cnt = 0

For $i = UBound($aCompare)-1 To 1 Step -1
	$sFontStr1 = '<font color="white" bkcolor="' & $aColors[$iClr_Cnt] & '">' & $aCompare[$i][1] & '</font>'
	$sFontStr2 = '<font color="white" bkcolor="' & $aColors[$iClr_Cnt] & '">' & $aCompare[$i][2] & '</font>'
	
	$sString1 = _
		StringLeft($sString1, $aCompare[$i][0] - 1) & _
		$sFontStr1 & _
		StringMid($sString1, $aCompare[$i][0] + StringLen($aCompare[$i][1]))
	
	$sString2 = _
		StringLeft($sString2, $aCompare[$i][0] - 1) & _
		$sFontStr2 & _
		StringMid($sString2, $aCompare[$i][0] + StringLen($aCompare[$i][2]))
	
	$iClr_Cnt += 1
	If $iClr_Cnt > 4 Then $iClr_Cnt = 0
Next

$nLabel1 = _GUICtrlCreateTFLabel($sString1, 20, 20, $iGUI_Width - 40, ($iGUI_Height / 2) - 5)
_GUICtrlCreateSeparator(2, ($iGUI_Height / 2) - 2, $iGUI_Width - 4, 4)
$nLabel2 = _GUICtrlCreateTFLabel($sString2, 20, ($iGUI_Height / 2) + 5, $iGUI_Width - 40, ($iGUI_Height / 2) - 5)

GUISetState(@SW_SHOW, $hGUI)

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
	EndSwitch
WEnd

Func _StringCompare($sString1, $sString2)
	Local $aRet, $iLen1, $iLen2, $iBiggestLen, $i, $sChar1, $sChar2, $s_Char1, $s_Char2, $iPos
	
	$iLen1 = StringLen($sString1)
	$iLen2 = StringLen($sString2)
	
	$iBiggestLen = $iLen1
	If $iLen2 > $iLen1 Then $iBiggestLen = $iLen2
	
	Dim $aRet[$iBiggestLen+1][3] = [[0]]
	
	For $i = 1 To $iBiggestLen
		$sChar1 = StringMid($sString1, $i, 1)
		$sChar2 = StringMid($sString2, $i, 1)
		
		Select
			Case $sChar1 <> $sChar2
				$s_Char1 = $sChar1
				$s_Char2 = $sChar2
				
				$iPos = $i
				
				While $s_Char1 <> $s_Char2
					$i += 1
					
					$s_Char1 = StringMid($sString1, $i, 1)
					$s_Char2 = StringMid($sString2, $i, 1)
					
					If $s_Char1 <> $s_Char2 Then
						$sChar1 &= $s_Char1
						$sChar2 &= $s_Char2
					EndIf
				WEnd
				
				$aRet[0][0] += 1
				$aRet[$aRet[0][0]][0] = $iPos
				$aRet[$aRet[0][0]][1] = $sChar1
				$aRet[$aRet[0][0]][2] = $sChar2
		EndSelect
	Next
	
	ReDim $aRet[$aRet[0][0]+1][3]
	Return $aRet
EndFunc

Func _GUICtrlCreateSeparator($iLeft, $iTop, $iLenght, $iWidth = -1, $nStyle = $SS_ETCHEDHORZ)
	Local $nSetStyle = $nStyle, $iTmpSwap
	
	If $iWidth > 0 Then
		$nSetStyle = $SS_SUNKEN
	EndIf
	
	Switch $nStyle
		Case $SS_ETCHEDHORZ
			$iTmpSwap = $iWidth
			$iWidth = $iLenght
			$iLenght = $iTmpSwap
		Case $SS_ETCHEDVERT
			If $nSetStyle <> $SS_SUNKEN Then
				$iWidth = -1
			EndIf
		Case Else
			If $nSetStyle <> $SS_SUNKEN Then
				Return SetError(1, 0, 0)
			EndIf
	EndSwitch
	
	If $iLenght = -1 Then
		$iLenght = 1
	EndIf
	
	If $iWidth = -1 Then
		$iWidth = 1
	EndIf
	
	Return GUICtrlCreateLabel("", $iLeft, $iTop, $iWidth, $iLenght, $nSetStyle)
EndFunc
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
dwerf

На начальных примерах работает!

Правда, я для интереса изменил начальные условия (удалил слово "акты"):

Код:
$line1 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесений изменений в отдельные муниципальные правовые"'

$line2 = 'Постановление администрации муниципального образования город Краснодар от 16 июня 2011 г. N 4226 "О внесений изменений и дополнений в отдельные муниципальные правовые акты"'
после чего алгоритм выдал что-то явно не то. Даже без закрывающей правой скобки.


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

CreatoR
CreatoR сказал(а):
Интересная библиотека! Только на начальных примерах зачем-то одинаковый текст (весь конец строки) выделяется зеленым цветом.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
Suppir [?]
Интересная библиотека
Библиотека то интересная, но мне лично больше сам пример понравился :smile:

Только на начальных примерах зачем-то одинаковый текст (весь конец строки) выделяется зеленым цветом.
Он отличается от конца второй строки :smile:
Это побуквенное сравнение, не как в WinMerge, но это начало, позже попробую сделать относительное сравнение, для каждого слова отдельно.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
К теме только косвенно относится, но вот пример сравнения двух файлов используя Online-сервис:

Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <IE.au3>
#include <File.au3>

FileCopy(@ScriptFullPath, @TempDir & "\" & @ScriptName, 1)

_ReplaceStringInFile(@TempDir & "\" & @ScriptName, "#include <File.au3>", "")
_ReplaceStringInFile(@TempDir & "\" & @ScriptName, "oIE", "aIE")
FileWriteLine(@TempDir & "\" & @ScriptName, @CRLF & "EndOfFile" & @CRLF)

$sString1 = FileRead(@ScriptFullPath)
$sString2 = FileRead(@TempDir & "\" & @ScriptName)

_IEErrorHandlerRegister()
$oIE = _IECreateEmbedded()

$hGUI = GUICreate("Сравнение файлов - http://realcode.ru/diff", _
	@DesktopWidth - 100, @DesktopHeight - 100, -1, -1, _
	BitOR($GUI_SS_DEFAULT_GUI, $ws_sizebox, $WS_CLIPSIBLINGS, $WS_CLIPCHILDREN))

$GUIActiveX = GUICtrlCreateObj($oIE, 10, 40, @DesktopWidth - 120, @DesktopHeight - 180)
GUICtrlSetResizing(-1, $GUI_DOCKAUTO)

_IENavigate($oIE, "http://realcode.ru/diff/")

$oForms = _IEFormGetCollection($oIE)
$oForm = $oForms("diff")

$oEdit_A = _IEFormElementGetObjByName($oForm, "textA")
$oEdit_B = _IEFormElementGetObjByName($oForm, "textB")

_IEFormElementSetValue($oEdit_A, $sString1)
_IEFormElementSetValue($oEdit_B, $sString2)

$oFormCollection = _IEFormElementGetCollection($oForm)
$oFormCollection("send").Click

GUISetState()

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			GUIDelete()
			Exit
	EndSwitch
WEnd


Кстати, особо внимательные могли заметить, что я использовал недокументированный метод получения объекта из коллекций объектов. Т.е вместо того, чтобы перебирать объекты в цикле «For...In», я получаю объект через индекс ID элемента ((0), (1) и т.д. ("id")), почти также как с элементами массива, только вместо квадратных скобок используются круглые.
 

Garrett

Модератор
Локальный модератор
Сообщения
3 999
Репутация
964
CreatoR [?]
я получаю объект через индекс элемента ((0), (1) и т.д.)
Но это в том случае если объект не имеет ID или Name, и когда вы точно знаете индекс объекта, который вам нужен! Для поиска всё же придется прибегнуть к For...In

Кстати у объекта есть ID: ;)
Код:
$oForm = $oForms("diff")
 

Garrett

Модератор
Локальный модератор
Сообщения
3 999
Репутация
964
CreatoR [?]
Конечно, мы же получаем посредством _IEFormGetCollection() объект, который содержит коллекцию объектов.
Вот как это дело выглядит
Код:
; ...
ConsoleWrite(ObjName($oIE) & @CRLF) 						; Object 'IWebBrowser2'
ConsoleWrite(ObjName($oIE.document) & @CRLF) 				; Object 'DispHTMLDocument'
ConsoleWrite(ObjName($oIE.document.forms) & @CRLF) 			; Object collections 'DispHTMLElementCollection' for tag <form>...</form>
ConsoleWrite(ObjName($oIE.document.forms(0)) & @CRLF) 		; Object 'DispHTMLFormElement'
ConsoleWrite($oIE.document.forms(0).id & @CRLF) 			; Properties Object 'DispHTMLFormElement' (id) (Get object by index)
ConsoleWrite($oIE.document.forms("diff").action & @CRLF) 	; Properties Object 'DispHTMLFormElement' (action) (Get object by id or name)
; ...
 
Верх