Что нового

[Данные, строки] Cравнение двух текстовых файлов и удаление повторов

marmisha

Новичок
Сообщения
41
Репутация
2
Добрый вечер. Подскажите как реализовать следующее:
-имеются test1.txt и test2.txt.
Необходимо сравнить их и удалить в файле test2.txt одинаковые строки, используемые в test1.txt.
Объясню более понятно. Файл test1.txt содержит построчно(qwer, asdf, zxcv), test2.txt содержит построчно(qwer, tyui, zxcv).
После выполнения скрипта в файле test2.txt должно остаться (tyui). Файл test1.txt изменять нельзя, он берется как основной, с которым все и сравнивается построчно.
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
marmisha
подробнее о повторах

Код:
#include <File.au3>
Global $aT1, $aT2, $_3j5d2f8k_=2
_FileReadToArray(@ScriptDir&'\test1.txt', $aT1) ; читаем файл в массив
$aT1=_Check($aT1) ; удалить повторы, создать переменные
_FileReadToArray(@ScriptDir&'\test2.txt', $aT2) ; читаем файл в массив
$aT2=_Check($aT2) ; игнорируя повторы формируется новый массив
_FileWriteFromArray(@ScriptDir&'\test2.txt', $aT2) ; пишем массив в файл

Func _Check($aArr)
	Local $k=0
	For $i = 1 To $aArr[0]
		Assign($aArr[$i]&'_3j5d2f8k_', Eval($aArr[$i]&'_3j5d2f8k_')+1, 2) ; создаём глобальные переменные или увеличиваем значение для уже созданных
		If Eval($aArr[$i]&'_3j5d2f8k_') = 1 Then
			$aArr[$k]=$aArr[$i]
			$k+=1
		EndIf
	Next
	ReDim $aArr[$k]
	Return $aArr
EndFunc
 
Автор
M

marmisha

Новичок
Сообщения
41
Репутация
2
в этой строке ошибка
10.014.jpg
 

Bloodrinker

<Блуждающий...>
Сообщения
228
Репутация
19
просто компилируй как x86 а не x64. такая ошибка вылетает во многих скриптах на x64 машинах
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
626
Bloodrinker [?]
просто компилируй как x86 а не x64.
по ошибке видно, что он не компилирует, а запускает из скрипта.
marmisha, приведи код скрипта, который выдает ошибку. но для начала, ты указал правильные пути до файлов?
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
marmisha
1. Для простоты скрипта, понятия идеи я не вставлял проверки на ошибки, такие как проверка массива, проверка открытия файлов, проверка, что файл не пустой.
2. Если этот код требуется вставить в какое то приложение или использовать несколько раз для сравнения с несколькими файлами, то требуется переписать код, чтоб генерируемые переменные находились в локальном пространстве, а функция принимала только два параметра - пути к сравниваемым файлам.
3. И для примера хотелось бы глянуть сравниваемые файлы, на которых вылетает ошибка. Может придётся сравнивать медленным способом, то есть сравнивать каждую строку одного файла со строками второго файла. Количество шагов при этом пропорционально произведению строк.


Новый вариант с проверками на ошибки:

Код:
; Извлекает уникальные строки из второго файла, которых нет в первом файле. Причём строки будут в единственном экземпляре, даже если они повторялись во втором файле.

; данный способ Assign имеет проблему со спец-символом "[", поэтому в функцию добавлен участок кода, временно заменяющий этот символ на любой другой из 256 символов, который отсутствует в текстах, а в результирующем тексте символ будет востановлен. Остальные спец-символы !@#$%^&*()-+<>?/\|{}] не имеют проблем. 

; http://autoit-script.ru/index.php?topic=2930.msg21226#msg21226
; http://autoit-script.ru/index.php?topic=4861.new#new

$Path_In1 = @ScriptDir & '\test_in1.txt'
$Path_In2 = @ScriptDir & '\test_in2.txt'
$Path_Out = @ScriptDir & '\test_Out.txt'
$sText1 = FileRead($Path_In1)
$sText2 = FileRead($Path_In2)

$sText_Out = _Unique_Lines_Text2($sText1, $sText2)
If @error Then
	MsgBox(0, 'Error', 'Error = ' & @error)
	Exit
Else
	$hFile = FileOpen($Path_Out, 2) ; пишем в файл
	FileWrite($hFile, $sText_Out)
	FileClose($hFile)
EndIf

; @error = 2 - Not found
; @error = 2 - Не найдено
; не учитывает регистр String = StRiNg = STRING
; not case sensitive, String = StRiNg = STRING
Func _Unique_Lines_Text2($sText1, $sText2, $sep = @CRLF)
	Local $i, $k, $aText, $s, $Trg = 0, $LenSep

	If StringInStr($sText1 & $sText2, '[') And $sep <> '[' Then ; если сбойный символ есть до заменяем его
		For $i = 0 To 255
			$s = Chr($i)
			If Not StringInStr($sText1 & $sText2, $s) Then
				If StringInStr($sep, $s) Then ContinueLoop
				$sText1 = StringReplace($sText1, '[', $s)
				$sText2 = StringReplace($sText2, '[', $s)
				$Trg = 1
				ExitLoop
			EndIf
		Next
		If Not $Trg Then Return SetError(1, 0, '')
	EndIf

	$LenSep = StringLen($sep)

	$aText = StringSplit($sText1, $sep, 1) ; Создаём переменные первого файла
	For $i = 1 To $aText[0]
		Assign($aText[$i] & '/', 2, 1)
	Next
	Assign('/', 2, 1)

	$aText = StringSplit($sText2, $sep, 1)

	$k = 0
	$sText1 = ''
	For $i = 1 To $aText[0]
		Assign($aText[$i] & '/', Eval($aText[$i] & '/')+1, 1) ; создаём локальные переменные или увеличиваем значение для уже созданных
		If Eval($aText[$i] & '/') = 1 Then
			$sText1 &= $aText[$i] & $sep
			$k += 1
		EndIf
	Next
	If $k = 0 Then Return SetError(2, 0, '')
	If $Trg Then $sText1 = StringReplace($sText1, $s, '[')
	Return StringTrimRight($sText1, $LenSep)
EndFunc
Кстати, понял причину ошибки: если два файла одинаковы, то выходной массив длжен быть с размерностью 0, а такого быть не может, в результате вылет.
 

Suppir

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

Код:
global $lines[1], $test1, $test2

$test1=FileRead("test1.txt")
$test2=FileRead("test2.txt")

;~ получаем массив строк из файла test1
$lines = StringRegExp($test1, "(?m)^(.+)$", 3)

;~ удаляем из файла test2 любые строки, которые совпадают с теми, что в массиве
For $x = 0 to Ubound ($lines) - 1
	$test2 = StringRegExpReplace($test2, "(?m)" & $lines[$x] & "[\r\n]?", "")
Next

;~ выводим оставшиеся строки
FileWrite("test3.txt", $test2)
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Bloodrinker [?]
просто компилируй как x86 а не x64. такая ошибка вылетает во многих скриптах на x64 машинах
Ошибка не будет вылетать если правильно обращаться к массивам (вместо $Arr[0] использовать Ubound($Arr)-1).
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
Suppir
1. Вместо StringRegExp можно использовать StringSplit (проще).
2. StringRegExpReplace - корректно удалит только строки, которые не содержат спец-символов. Для такого метода нужно вставить ещё один StringRegExpReplace, который подготовит шаблон замены, то есть закомментирует все спец-сиволы в строке.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
AZJIO [?]
StringRegExpReplace - корректно удалит только строки, которые не содержат спец-символов. Для такого метода нужно вставить ещё один StringRegExpReplace, который подготовит шаблон замены, то есть закомментирует все спец-сиволы в строке.
Не нужно, для этого есть отменяющие операторы в рег. выражениях:

Код:
For $x = 0 to Ubound ($lines) - 1
    $test2 = StringRegExpReplace($test2, "(?m)\Q" & $lines[$x] & "\E[\r\n]?", "")
Next
 
Автор
M

marmisha

Новичок
Сообщения
41
Репутация
2
Спасибо всем за помошь. Остановился на варианте AZJIO, так как работает без замечаний. Для теста взял test1.txt = 9.63Mb и test2.txt = 5.66Mb. Скрипт обработал примерно за 10-13 секунд, на выходе test3.txt = 2.32Mb. Вариант от Suppir тоже подошел, но при обработке этих же файлов test3.txt так и не появился, сколько времени затрачено на обработку не знаю, не дождался, но с файлами не больших размеров работает так же хорошо.
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
CreatoR
\Q...\E этот вариант спотыкается на поиске строки, который в шаблоне выглядит так: \QD:\Edit\1.txt\E


marmisha
Вариант от Suppir тоже подошел
В этот вариант перед StringRegExpReplace нужно добавить строку:

Код:
$lines[$x]=StringRegExpReplace($lines[$x], '[][{}()*+?.\\^$|=<>#]', '\\$0')


Иначе все строки содержащие символы ][{}()*+?.\\^$|=<># обрабатываться не будут.
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
При тесте способа с Assign обнаружил, открытая квадратная скобка "[" не даёт создать переменную. То есть пример не работает идеально для текста с символом "[". Остальные спец-символы !@#$%^&*()-+<>?/\|{}] без проблем.
 

spy686

Новичок
Сообщения
12
Репутация
0
а окончательный вариант можно в студию )


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

Может кто-то будет искать, есть готовое решение DupKill.exe
 

spy686

Новичок
Сообщения
12
Репутация
0
спасиб. супер волшебная программочка )
 
Верх