Войти

Показать полную графическую версию : [решено] Удаление дубликатов строк из текстового файла со сравнением по две строки


Страниц : [1] 2 3

Uragan66
26-06-2019, 23:26
Доброго времени суток всем.
Знаю что тема уже изъезжена, но готового решения найти не получается.
Есть плейлисты (по сути текстовые файлы), при их создании может получаться большое количество дублирующих строк.
Например:
#EXTINF:0, 1+1
http://193.106.211.2:1234/udp/225.1.1.103:4001
#EXTINF:0, ICTV
http://193.106.211.2:1234/udp/225.1.1.104:4001
#EXTINF:0, 5 канал
http://193.106.211.2:1234/udp/225.1.1.105:4001
#EXTINF:0, СТБ
http://193.106.211.2:1234/udp/225.1.1.106:4001
#EXTINF:0, ТРК «Украина»
http://193.106.211.2:1234/udp/225.1.1.107:4001
#EXTINF:0, Сити
http://193.106.211.2:1234/udp/225.1.1.108:4001
#EXTINF:0, Тонис
http://193.106.211.2:1234/udp/225.1.1.111:4001
#EXTINF:0, ТЕТ
http://193.106.211.2:1234/udp/225.1.1.110:4001
#EXTINF:0, ТРК «Киев»
http://193.106.211.2:1234/udp/225.1.1.114:4001
#EXTINF:0, Тонис
http://193.106.211.2:1234/udp/225.1.1.111:4001
#EXTINF:0, Мега
http://193.106.211.2:1234/udp/225.1.1.112:4001
#EXTINF:0, М1
http://193.106.211.2:1234/udp/225.1.1.113:4001
#EXTINF:0, канал «Киев»
http://193.106.211.2:1234/udp/225.1.1.114:4001
#EXTINF:0, 2+2
http://193.106.211.2:1234/udp/225.1.1.115:4001
#EXTINF:0, ТРК «Украина»
http://190.106.210.2:1234/udp/225.1.1.107:4001
Строки обязательно должны считываться попарно: название канала - адрес потока. В примере три дубликата, в первом повторяются и название и адрес, во втором - только адрес, в третьем - только название. Из этих дублирующихся строк нужно удалить только те, где повторяются и название и адрес, то есть в примере только первую пару.
Пересмотрел кучу различных скриптов, сам пробовал батники писать, но все они считывают по одной строке и в выходном файле может получится к названию канала несколько адресов или наоборот.
К примеру такой батник:
@echo off
setlocal disableDelayedExpansion
set "file=%~1"
set "line=%file%.line"
set "deduped=%file%.deduped"
::Define a variable containing a linefeed character
set LF=^


::The 2 blank lines above are critical, do not remove
>"%deduped%" (
for /f usebackq^ eol^=^%LF%%LF%^ delims^= %%A in ("%file%") do (
set "ln=%%A"
setlocal enableDelayedExpansion
>"%line%" (echo !ln:\=\\!)
>nul findstr /xlg:"%line%" "%deduped%" || (echo !ln!)
endlocal
)
)
>nul move /y "%deduped%" "%file%"
2>nul del "%line%"
в выходном файле делает так:
#EXTINF:-1 group-title="Познавательные",Brodilo.TV
http://brodilo.tv/channel.php
#EXTINF:-1 group-title="Познавательные",Da Vinci Learning
http://hls.peers.tv/streaming/da_vinci/16/tvrecw/playlist.m3u8|User-Agent=DuneHD/1.0.3
http://194.28.155.10:81/udp/225.0.55.26:1234
#EXTINF:-1 group-title="Прочие каналы:",112 Украина
#EXTINF:-1 group-title="Прочие каналы:",112 Украина HD
http://112hd-hls3.cosmonova.net.ua/hls/112hd_ua_mid/index.m3u8
http://app.live.112.events/hls-ua/112hd_mid/index.m3u8
#EXTINF:-1 group-title="Прочие каналы:",112 Украина [FHD]
#EXTINF:-1 group-title="Прочие каналы:",112 Україна
#EXTINF:-1 group-title="Прочие каналы:",112 Україна HD
#EXTINF:-1 group-title="Прочие каналы:",12 Канал (Омск)
http://12channel.bonus-tv.ru:80/stream549837052987/tracks-v1a1/mono.m3u8
#EXTINF:-1 group-title="Познавательные",Discovery Channel
http://st6.allplay.uz/iptv/4/index.m3u8
#EXTINF:-1 group-title="Познавательные",Discovery Channel HD
http://st6.allplay.uz/iptv/74/index.m3u8
Это отрывок из другого плейлиста, он слишком длинный для помещения в пример.
Другие скрипты делают аналогично.
Они сравнивают по одной строке.
Может кто подскажет как реализовать сравнивание строк попарно, чтобы не нарушалась структура плейлиста ?
Премного буду благодарен за помощь.

Iska
27-06-2019, 04:07
Например: »
Так:
$aContent = Get-Content -Path 'C:\Мои проекты\0266\0001.txt'

@(for($i = 0; $i -le $aContent.Length - 1; $i = $i + 2) {
$aContent[$i] + "`r`n" + $aContent[$i + 1]
}) | Sort-Object -Unique

#EXTINF:0, 1+1
http://193.106.211.2:1234/udp/225.1.1.103:4001
#EXTINF:0, 2+2
http://193.106.211.2:1234/udp/225.1.1.115:4001
#EXTINF:0, 5 канал
http://193.106.211.2:1234/udp/225.1.1.105:4001
#EXTINF:0, ICTV
http://193.106.211.2:1234/udp/225.1.1.104:4001
#EXTINF:0, канал «Киев»
http://193.106.211.2:1234/udp/225.1.1.114:4001
#EXTINF:0, М1
http://193.106.211.2:1234/udp/225.1.1.113:4001
#EXTINF:0, Мега
http://193.106.211.2:1234/udp/225.1.1.112:4001
#EXTINF:0, Сити
http://193.106.211.2:1234/udp/225.1.1.108:4001
#EXTINF:0, СТБ
http://193.106.211.2:1234/udp/225.1.1.106:4001
#EXTINF:0, ТЕТ
http://193.106.211.2:1234/udp/225.1.1.110:4001
#EXTINF:0, Тонис
http://193.106.211.2:1234/udp/225.1.1.111:4001
#EXTINF:0, ТРК «Киев»
http://193.106.211.2:1234/udp/225.1.1.114:4001
#EXTINF:0, ТРК «Украина»
http://190.106.210.2:1234/udp/225.1.1.107:4001
#EXTINF:0, ТРК «Украина»
http://193.106.211.2:1234/udp/225.1.1.107:4001
?

Uragan66
27-06-2019, 07:09
Iska, спасибо! Да, выходной файл должен быть именно таким. Но у меня Ваш код почему-то не срабатывает. Входной файл остаётся без изменений.


P.S. Мои извинения, всё работает, это я с запуском затупил. Сейчас запуская скрипт батником:
@echo off
Powershell -File ./sortes.ps1 >> test.txtфайл test.txt получается без дублей.
Но есть некоторые нюансы. Скрипт ps1 нормально читает файлы только в кодировке ANSI, а в выходном файле кодировка OEM866 (это говорит AkelPad, а Notepad++ определяет как ANSI).
Можно как-то переделать на UTF-8 ?
А то на выходе, если в входном файле есть кавычки типа:
ТРК «Киев»получается:
ТРК <Киев>Спасибо!

greg zakharov
27-06-2019, 09:02
Iska, (ради спортивного интереса) задачу можно решить короче через (MINGW|Cygwin|WSL):
sed 'N;s/\n/ - /' input.log | sort | sed 'N;/^\(.*\)\n\1$/!P;D' | sed 's/ - /\n/g'
Можно, правда, еще короче:
sed 'N;s/\n/ - /' input.log | sort | uniq | sed 's/ - /\n/g'
Если лог большой, лучше через parallel, а еще лучше и вовсе на чистом perl (также однострочник). Но это дело вкуса.

YuS_2
27-06-2019, 11:27
(ради спортивного интереса) задачу можно решить короче »
gc test.txt -r 2|%{"$($_[0])`n$($_[1])"}|sort -uni

greg zakharov
27-06-2019, 13:50
YuS_2, ReadCount ведь уже установлен в пару, зачем там цикл? Просто select -u:
gc input.log -r 2 | select -u
или, если сортировка принципиальна:
gc input.log -r 2 | sort | select -u
pwsh прекрасно поймет, что от него хотят.

Uragan66
27-06-2019, 14:10
Ребята, спасибо всем !
Но вопрос с кодировкой так и остался.
Входной файл/файлы в utf-8 и после скрипта, предложенного ув. Iska, с русскими символами на выходе беда, крякозябры выходят.
Сортировка в тексте не обязательна, главное удаление дублей парных строк.
С остальными скриптами не совсем разобрался, как их запускать.

greg zakharov
27-06-2019, 14:12
gc input.log -encoding utf8 ...
При выводе в файл оператор перенаправления (>) "пишет" в utf8 (если ничего не путаю), но лучше использовать командлет Out-File для последующей записи в файл (в нем можно указать конечную кодировку).
С остальными скриптами не совсем разобрался, как их запускать.Это - однострочники (для дзенствующих :) ), выполняются прямо в хосте pwsh.

YuS_2
27-06-2019, 15:47
ReadCount ведь уже установлен в пару, зачем там цикл? »
не помню, что-то там у меня сразу не пошло, вроде бы, строки перемешались в каком-то варианте, поэтому в цикл засунул, чтобы наверняка :)
если сортировка принципиальна: »
у сорт, кстати, тоже есть параметр -Unique, т.е. достаточно:
gc test.txt -r 2|sort -u

Uragan66
27-06-2019, 17:18
С кодировкой и выводом всё получилось.
Огромная благодарность всем за помощь!

greg zakharov
27-06-2019, 18:25
тоже есть параметр -UniqueАга, но в виду редкости его использования на практике, о нем легко позабыть.

Uragan66
27-06-2019, 21:57
По ходу работы со скриптом, выяснились некоторые нюансы. Для скрипта, предложенного ув. Iska:
Цитата Iska:
$aContent = Get-Content -Path 'C:\Мои проекты\0266\0001.txt'

@(for($i = 0; $i -le $aContent.Length - 1; $i = $i + 2) {
$aContent[$i] + "`r`n" + $aContent[$i + 1]
}) | Sort-Object -Unique »
требуются идеальные пары строк: название канала - ссылка. И соответственно чётное количество строк.
В противном случае строки могут перемешаться, и ссылки не будут соответствовать.
Бывает попадаются файлы с различного рода мусором между строк. К примеру:
#EXTINF:-1 ,Первый канал
http://gorod.tv/s/live/1/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Россия 1
http://gorod.tv/s/live/2/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Матч ТВ
http://gorod.tv/s/live/3/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,НТВ
http://gorod.tv/s/live/4/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Пятый канал
http://gorod.tv/s/live/5/46.252.219.59/1561632042766/0.m3u8
Здесь какая-то информация
http://gorod.tv/s/live/1/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Пятый канал
#EXTINF:-1 ,Россия К
http://gorod.tv/s/live/6/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 , Москва 24
http://91.231.219.145:80/tv_moscow_24/video.m3u8
#EXTINF:-1 , Санкт-Петербург
http://91.231.219.145:80/tv_sankt_peterburg/video.m3u8
https://smarttvnews.ru/wp-content/uploads/2018/11/BT-2016-logo_color7890.png
https://smarttvnews.ru/wp-content/uploads/2018/11/BT-2016-logo_color7890.png
#EXTINF:-1 ,Discovery
http://hls-v3-spbtv.msk.spbtv.com/for_spb/msk/ipv3/154.m3u8
#EXTINF:-1 ,NASA TV
http://hls-v3-spbtv.msk.spbtv.com/for_spb/msk/ipv3/17.m3u8
#EXTINF:-1 ,Россия 24
http://gorod.tv/s/live/7/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Карусель
http://gorod.tv/s/live/8/46.252.219.59/1561632042766/0.m3u8В примере вместо названия канала может быть что-то другое написано или канал может быть без ссылки. Или же прописаны ссылки на изображения.

Можно ли, перед выполнением основного кода скрипта, как-то удалить всё, что не соответствует парам канал - ссылка ? Регулярным выражением или дополнительным кодом в скрипт.
То есть первая строка название канала, обязательно начинающееся на #EXTINF, вторая строка - обязательно ссылка (могут быть http; https; rtmp; mms; rtmpe; udp).
Всё что не соответствует таким парам нужно удалить.

Uragan66
27-06-2019, 22:53
Составил такой RegExp:
^(#EXTINF.*[^\n\r]+\r?\nhttps?:\/\/[^\n\r]+\r?\n?|#EXTINF.*[^\n\r]+\r?\nrtmp:\/\/[^\n\r]+\r?\n?|#EXTINF.*[^\n\r]+\r?\nmms:\/\/[^\n\r]+\r?\n?|#EXTINF.*[^\n\r]+\r?\nrtmpe:\/\/[^\n\r]+\r?\n?|#EXTINF.*[^\n\r]+\r?\nudp:\/\/[^\n\r]+\r?\n?)
Нужные пары строк то ищет, но не получается удалить всё, кроме найденного.

greg zakharov
27-06-2019, 23:06
for ($i, $arr = 0, (gc input.log); $i -lt $arr.Length;) {
$a, $b = $arr[$i], $arr[$i + 1] # потенциальная пара
# критерий относительно которого формируется пара
if ($a.StartsWith('#') -and $b -match ':\/\/') {
# выводим пару в окно хоста
"$a`n$b"
# переходим на две позиции вперед
$i += 2
}
else {
# в противном случае переходим к следующему значению
++$i
continue
}
}
Должно вернуть:
#EXTINF:-1 ,Первый канал
http://gorod.tv/s/live/1/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Россия 1
http://gorod.tv/s/live/2/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Матч ТВ
http://gorod.tv/s/live/3/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,НТВ
http://gorod.tv/s/live/4/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Пятый канал
http://gorod.tv/s/live/5/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Россия К
http://gorod.tv/s/live/6/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 , Москва 24
http://91.231.219.145:80/tv_moscow_24/video.m3u8
#EXTINF:-1 , Санкт-Петербург
http://91.231.219.145:80/tv_sankt_peterburg/video.m3u8
#EXTINF:-1 ,Discovery
http://hls-v3-spbtv.msk.spbtv.com/for_spb/msk/ipv3/154.m3u8
#EXTINF:-1 ,NASA TV
http://hls-v3-spbtv.msk.spbtv.com/for_spb/msk/ipv3/17.m3u8
#EXTINF:-1 ,Россия 24
http://gorod.tv/s/live/7/46.252.219.59/1561632042766/0.m3u8
#EXTINF:-1 ,Карусель
http://gorod.tv/s/live/8/46.252.219.59/1561632042766/0.m3u8

Uragan66
28-06-2019, 07:58
greg zakharov, спасибо большое!
Но у меня почему-то не получается запустить скрипт, выдаёт ошибку компиляции:
http://images.vfl.ru/ii/1561697746/8e91e6d9/27032713.jpg

greg zakharov
28-06-2019, 08:59
Uragan66, ничего удивительного: вы пытаетесь запустить сценарий pwsh как WSH, - последнему ничего не известно о синтаксисе PowerShell.

Uragan66
28-06-2019, 09:07
greg zakharov, извините, думал, что скрипт нужно запускать как js.
Теперь разобрался... Большая благодарность за скрипт!

Uragan66
28-06-2019, 11:34
Немного подправил скрипт под свою задачу:
@(for($i, $arr = 0, (gc .\*.m3u -Encoding utf8); $i -lt $arr.Length;) {
$a, $b = $arr[$i], $arr[$i + 1] # потенциальная пара
# критерий относительно которого формируется пара
if ($a.StartsWith('#EXTINF') -and $b -match ':\/\/') {
# выводим пару в окно хоста
"$a`n$b"
# переходим на две позиции вперед
$i += 2
}
else {
# в противном случае переходим к следующему значению
++$i
continue
}
}) | Sort-Object -Unique | Out-File .\out.m3u -Encoding utf8
Отрабатывает отлично...
Ещё раз большое спасибо всем за помощь!

Uragan66
24-08-2019, 15:30
Здравствуйте!
Использую, подсказанный ув. greg zakharov, скрипт:
@(for($i, $arr = 0, (gc .\*.m3u -Encoding utf8); $i -lt $arr.Length;) {
$a, $b = $arr[$i], $arr[$i + 1] # потенциальная пара
# критерий относительно которого формируется пара
if ($a.StartsWith('#EXTINF') -and $b -match ':\/\/') {
# выводим пару в окно хоста
"$a`n$b"
# переходим на две позиции вперед
$i += 2
}
else {
# в противном случае переходим к следующему значению
++$i
continue
}
}) | Sort-Object -Unique | Out-File .\out.m3u -Encoding utf8
Скрипт работает отлично, ещё раз большое спасибо!
Но бывает возникает необходимость пускать на вход не один файл, а несколько.
Подскажите, пожалуйста, можно ли для обработки скриптом прописать не один файл, а папку с файлами одного расширения ?
Буду премного благодарен за ответы и подсказки.

DJ Mogarych
24-08-2019, 15:58
можно ли для обработки скриптом прописать не один файл, а папку с файлами одного расширения ? »
gc C:\temp\*.txt




© OSzone.net 2001-2012