Что нового

[Данные, строки] Удалить из файла все повторения

ZanMax

Тестер
Сообщения
120
Репутация
5
Есть файл на 3 - 5 мб.
Нужно удалить из файла все повторения.
Строка имеет такой вид :
123123;5465645646;45645645645;456456456;;
456666;456456666;456456456;456456456456;;

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

P.s. Повторения значит полное совпадение 2-ух строк.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
Длина строк строго одинаковая?
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
622
Код:
$sOrigin = @ScriptDir & "\file1.txt"
$sResulut = @ScriptDir & "\file2.txt"
$hRead = FileOpen($sOrigin, 0)
$hWrite = FileOpen($sResulut, 2)
$sBuffer = ''
$lineSum = ''
While 1
        $line = FileReadLine($hRead)
        If @error = -1 Then ExitLoop
        If Not StringInStr($lineSum, $line) Then
            $sBuffer &= $line & @CRLF
            $lineSum &= $line & "|"
        EndIf
WEnd
FileClose($hRead)
FileWrite($hWrite, $sBuffer)
FileClose($hRead)
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Еще 1 вариант :
Код:
#include <file.au3>

Dim $aLines,$sSum

$sFile = @ScriptDir & "\Test.txt"
_FileReadToArray($sFile,$aLines)
$sText= FileRead($sFile)

For $i=UBound($aLines)-1 To 1 Step -1
   If StringRegExp($sSum,'(?m)\n\r?' & $aLines[$i] & '\n\r?',0) Then $iProc= _FileWriteToLine($sFile, $i,'',1); Корректен
   ; If StringRegExp($sSum,$aLines[$i]&'\n\r?',0) Then $iProc= _FileWriteToLine($sFile, $i,'',1); оказалось не совсем корректен
   ;If StringRegExp($sSum,$aLines[$i],0) Then $iProc= _FileWriteToLine($sFile, $i,'',1) ; некорректен
   ;If StringInStr($sSum,$aLines[$i]) Then $iProc= _FileWriteToLine($sFile, $i,'',1);некорректен
   $sSum &= $aLines[$i]& @LF
Next

Правда неизвестно как скажется большой размер файла ?
Но использование рег.выражения вместо StringInStr должно увеличить скорость выполнения и корректность проверки


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

Однако все варианты некорректны для такого текста ,например :
Код:
1456666;456456666;456456456;456456456456;;
123123;5465645646;45645645645;456456456;;
456666;456456666;456456456;456456456456;;
123123;5465645646;45645645645;456456456;;
456666;456456666;456456456;456456456456;;
123123;5465645646;45645645645;456456456;
456666;456456666;456456456;456456456456;
123123;5465645646;45645645645;456456456;;
456666;456456666;456456456;456456456456;;
456666;456456666;456456456;456456456456;;
11456666;456456666;456456456;456456456456;;
Искать надо глобальные совпадения а не вхождение


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

Где-то встречалось у Creator'а помоему ? С помощью рег.выражений.


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

Похоже получилось , хотя не уверен что для всех вариантов :
Код:
If StringRegExp($sSum,$aLines[$i]&'\n\r?',0) Then $iProc= _FileWriteToLine($sFile, $i,'',1); Оказалось не рассматривает начало и конец текста



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

Так будет корректно :
Код:
If StringRegExp($sSum,'(?m)\n\r?' & $aLines[$i] & '\n\r?',0) Then $iProc= _FileWriteToLine($sFile, $i,'',1)
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
ZanMax
Вот так у меня отрабатывает ~ за 2 минуты файл размером 45,9 Мб
Код:
#include <file.au3>
#include <Array.au3>

Dim $aFileNew[1], $afileOld
$u = 0
$timeout = TimerInit()
_FileReadToArray("111.txt", $afileOld)
For $i = 1 To UBound($afileOld) - 1
	_ArraySearch($aFileNew, $afileOld[$i])
	If @error = 6 Then
		_ArrayAdd($aFileNew, $afileOld[$i])
		$u += 1
		$aFileNew[0] = $u
	EndIf
Next
_FileWriteFromArray("222.txt", $aFileNew, 1)
$timeout = Round(TimerDiff($timeout))
MsgBox(0, "", $timeout / 1000 & "sec")
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Так еще будет быстрее :
Код:
#include <file.au3>

Dim $aLines,$sSum

$sFile = @ScriptDir & "\Test.txt"
_FileReadToArray($sFile,$aLines)

For $i=UBound($aLines)-1 To 1 Step -1
   If StringRegExp($sSum,'(?m)\n\r?' & $aLines[$i] & '\n\r?',0) Then 
     $iProc= _FileWriteToLine($sFile, $i,'',1)
     ContinueLoop
   EndIf
   $sSum &= $aLines[$i]& @LF
Next



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

[?]
Вот так у меня отрабатывает ~ за 2 минуты файл размером 45,9 Мб
madmasles
Можно попросить сравнить время с последним вариантом на рег. выражениях.
Должно быть намного быстрее
. _ArraySearch должен здорово томозить. Ради интереса.
Хотя постоянное открывание файла и стирание строки займет еще больше времени
 
Автор
Z

ZanMax

Тестер
Сообщения
120
Репутация
5
Вот реальный файл :
2644;4820028940180;Крупа ячна Колосок 1кг.;1,000000;
39474;4606895000017;Журнал Cosmopolitan;1,000000;
11963;8591177012139;Provence Свічка коніч 1шт 560107/02;1,000000;
4101;4820048613651;Соус супергострий Новаро с/б 220гр.;1,000000;
49783;2853864000000;Печиво Лісовичок Лукас кг.;1,000000;
55954;4823003503288;Лампа Іскра 3U 230В 15Вт Е27т/б;1,000000;
31472;4820026678542;Рандеву 230г;1,000000;
71082;4820067170203;Макарони Спіраль тр.Щедрі брати 450гр;1,000000;
58062;9789666721696;Атлас Космоса (р)Ранок;1,000000;
27651;4820068317843;Подушка-37 ;1,000000;
2644;4820028940180;Крупа ячна Колосок 1кг.;1,000000;
39474;4606895000017;Журнал Cosmopolitan;1,000000;
11963;8591177012139;Provence Свічка коніч 1шт 560107/02;1,000000;
4101;4820048613651;Соус супергострий Новаро с/б 220гр.;1,000000;
49783;2853864000000;Печиво Лісовичок Лукас кг.;1,000000;
55954;4823003503288;Лампа Іскра 3U 230В 15Вт Е27т/б;1,000000;
31472;4820026678542;Рандеву 230г;1,000000;
71082;4820067170203;Макарони Спіраль тр.Щедрі брати 450гр;1,000000;
58062;9789666721696;Атлас Космоса (р)Ранок;1,000000;
27651;4820068317843;Подушка-37 прямокутна;1,000000;
825;4820000191272;Напій газований Лимонад Оболонь 1л;1,000000;
825;4820000191272;Напій газований Лимонад Оболонь 1л;1,000000;

И такого там от 3 до 5 мб.Длина строк разная.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Все мой вариант полностью отпадает. Постоянно открывать файл и стирать строку - это мазохизм.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,664
Репутация
2,462
Ещё вариант:

Код:
$sFile = @DesktopDir & "\Test.txt"

#Region Create file (30+- kb)
$sData = ""
$iRandom1 = Random(1, 50, 1)
$iRandom2 = Random(1, 50, 1)

For $i = 1 To 1024*5 ;1024*1024 ;7+ mb
	$sData &= $i & @CRLF
	
	If $i = $iRandom1 Or $i = $iRandom2 Then
		$sData &= $i & @CRLF
		ConsoleWrite($i & @LF) ;Display the duplicates
	EndIf
Next

$hFile = FileOpen($sFile, 2)
FileWrite($hFile, $sData)
FileClose($hFile)
#EndRegion Create file (30+- kb)

$Ret = _FileDeleteDuplicates($sFile, 1)

If $Ret = 1 Then
	MsgBox(64, "Results", "Was deleted <" & @extended & "> duplicates in file [" & $sFile & "]")
ElseIf @error Then
	MsgBox(48, "Error", "File [" & $sFile & "] was not found.")
Else
	MsgBox(64, "Results", "No duplicates found.")
EndIf

Func _FileDeleteDuplicates($sFile, $sDirection=-1)
	If Not FileExists($sFile) Or StringInStr(FileGetAttrib($sFile), "D") Then Return SetError(1, 0, 0)
	
	Local $FileStr = FileRead($sFile), $FileHasEndCRLF = True, $Extended = 0
	Local $ReplDirectVal = '\1\2'
	If $sDirection <> -1 Then $ReplDirectVal = '\2\1'
	
	If StringRight($FileStr, 2) <> @CRLF Then
		$FileStr &= @CRLF
		$FileHasEndCRLF = False
	EndIf
	
	Do
		$FileStr = StringRegExpReplace($FileStr, '(?sm)(^[^\r\n]+\r\n?)(.*?)^\1', $ReplDirectVal)
		$Extended += @extended
	Until @extended = 0
	
	If $Extended = 0 Then Return SetExtended(0, 0)
	
	If Not $FileHasEndCRLF Then $FileStr = StringTrimRight($FileStr, 2)
	
	Local $hFile = FileOpen($sFile, 2)
	FileWrite($hFile, $FileStr)
	FileClose($hFile)
	
	Return SetExtended($Extended/2, 1)
EndFunc


с большими файлами не проверял, терпения не хватило :smile:
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
ZanMax
Скопировал Ваш текст. Сделал 200000 строк. Файл получился размером 11,1 Мб. Мой код из Ответа 5 отрабатывает за 24.809 сек.

gregaz
Ваш код работал ~ 5 минут, я его остановил.

CreatoR,
Ваш код отработал за 25,403 сек., только в файле Test.txt он оставил вместо строк их номера, из 200000 строк оставил 5120. Непонятное число какое-то.
 
Автор
Z

ZanMax

Тестер
Сообщения
120
Репутация
5
madmasles сказал(а):
ZanMax
Скопировал Ваш текст. Сделал 200000 строк. Файл получился размером 11,1 Мб. Мой код из Ответа 5 отрабатывает за 24.809 сек.

gregaz
Ваш код работал ~ 5 минут, я его остановил.

CreatoR,
Ваш код отработал за 25,403 сек., только в файле Test.txt он оставил вместо строк их номера, из 200000 строк оставил 5120. Непонятное число какое-то.

Вот мой файлик : autoit.rv.ua/files/Other/tov.rar - 3627 кб
Запускаю ваш скрипт жду 1 - 2 - 3 минуты а результата никакого.
В чом может быть проблема ?
Спасибо большое.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
ZanMax [?]
Запускаю ваш скрипт жду 1 - 2 - 3 минуты а результата никакого.
В чом может быть проблема ?

Скорее всего проблема в том , что мы все время тестировали на файлах , где почти все строки повторялись (из 200000 строк получали в результате до 10 ).
При этом соответственно и _ArrayAdd работал только до 10 раз.
А все ф-ии массивов очень тормозят.
Как только встали на реальный файл , то получили :( и даже не получили.

Как ни парадоксально вариант Kaster"а получился самым скоростным.
Правда он не корректно удалял строки отличающиеся началом и концом.
Я сравнивал его скорость со скоростью варианта madmasles"а .
Разница порядка 4 раз и это при том,что _ArrayAdd отработал только 7 раз .
200000 строк обрабатывает за 2,5 сек (против 10 там ).
На твоем файле не мог пробовать т.к. трафик сейчас на пределе.

Вот тот вариант подредакированный мной :
Код:
$sOrigin = @ScriptDir & "\111.txt"
$sResulut = @ScriptDir & "\222.txt"
$timeout = TimerInit()
$hRead = FileOpen($sOrigin, 0)
$hWrite = FileOpen($sResulut, 2)
$sBuffer = ''
$lineSum = ''
While 1
        $line = FileReadLine($hRead)
        If @error = -1 Then ExitLoop
        If StringRegExp ($lineSum, '(?m)\|' & $line & '\|', 0 )=0 Then
       ;If Not StringInStr($lineSum, $line ) Then
            $sBuffer &= $line & @CRLF
            $lineSum &= $line & "|"
        EndIf
WEnd
FileClose($hRead)
FileWrite($hWrite, $sBuffer)
FileClose($hRead)

$timeout = Round(TimerDiff($timeout))
MsgBox(0, "", $timeout / 1000 & "sec")

Попробуй на своем файле
Все варианты с массивами на большом файле не пройдут , однозначно
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
622
хех... на скорость не проверял, написал первое что пришло в голову. и про строки которые сами могут содержать другие строки как то не подумал. вот исправленный вариант. проверьте на правильность
Код:
$sOrigin = @ScriptDir & "\file1.txt"
$sResulut = @ScriptDir & "\file2.txt"
$hRead = FileOpen($sOrigin, 0)
$hWrite = FileOpen($sResulut, 2)
$sBuffer = ''
$lineSum = '|'
While 1
	$line = FileReadLine($hRead)
	If @error = -1 Then ExitLoop
	If Not StringInStr($lineSum, "|" & $line & "|") Then $sBuffer &= $line & @CRLF
	$lineSum &= $line & "|"
WEnd
FileClose($hRead)
FileWrite($hWrite, $sBuffer)
FileClose($hWrite)
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
gregaz
Уходил по делам, оставил работать свой код ( с функциями массивов) на файле ZanMax. Вернулся через 1 час 40 минут, код еще не отработал.
Вывод: мой код для больших файлов не пригоден.
Оставил в файле ZanMax 1000 строк - 3, 2 сек.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Kaster [?]
хех... на скорость не проверял
Да еще раз убедился в несравнимой скорости Рег. выражений по сравнению со строковым сравнением.
Kaster , твой последний вариант это тоже ,что у меня только вместо рег. - строковое сравнение.
На 40 несовпадений твой
вариант дает около 30 сек , а при рег. выр. - 4 сек.
на 75 несовпадений 45 сек и ...................... 5.5 сек соответственно
на 130 несовпадений 87 сек. и ...................... 7.7 сек
при 5000 несовпадениях ????? и уже ........около 4 мин
Дальше будет тупик
Несомненое превосходство Регулярных выражений.
Отсюда :
Можно добиться приемлемого времени если удастся обойтись без цикла.
Почитать файл ( FileRead ) в текст
Заменить все переходы на новую строку каким то символом.
Составить 1 регулярное выражение для удаления дубликатов во всей строке - это у меня не получается.
Вариант Creator"а для поиска дубликатов в строке не работает .
Может и можно такое составить?



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

madmasles [?]
Вывод: мой код для больших файлов не пригоден.
Да и здесь с массивами - тупик
 
Автор
Z

ZanMax

Тестер
Сообщения
120
Репутация
5
:wacko: не думал что така сложная проблема ....
Может есть смысл реализовывать на чом то другом ? например С или С# ?

Пока в поисках нормального алгоритма.
Спасибо большое.

Если у кого то будут идеи. Буду очень рад.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,664
Репутация
2,462
Быстрее этого думаю уже не сделать на AutoIt:

Код:
$aLines = StringSplit(StringStripCR(FileRead($sFile)), @LF)
$sData = @CRLF

For $i = 1 To $aLines[0]
	If Not StringInStr($sData, @CRLF & $aLines[$i] & @CRLF, 1, -1) Then
		$sData &= $aLines[$i] & @CRLF
	Else
		ConsoleWrite($i & @LF)
	EndIf
Next

$sData = StringTrimLeft($sData, 2)
$sData = StringTrimRight($sData, 2)

$hFile = FileOpen($sFile, 2)
FileWrite($hFile, $sData)
FileClose($hFile)
 
Верх