Czy ktoś wie o nieliniowym narzędziu służącym do „binarnego” wyszukiwania / zastępowania ciągów w sposób zapewniający oszczędność pamięci? Zobacz też to pytanie .
Mam plik tekstowy + 2 GB, który chciałbym przetworzyć podobnie do tego, co wygląda na to, że:
sed -e 's/>\n/>/g'
Oznacza to, że chcę usunąć wszystkie nowe wiersze, które występują po >
, ale nigdzie indziej, więc to wyklucza tr -d
.
To polecenie (otrzymane z odpowiedzi na podobne pytanie ) kończy się niepowodzeniem couldn't re-allocate memory
:
sed --unbuffered ':a;N;$!ba;s/>\n/>/g'
Czy są jakieś inne metody bez uciekania się do C? Nienawidzę Perla, ale jestem gotów zrobić wyjątek w tym przypadku :-)
Nie wiem na pewno żadnego znaku, który nie występuje w danych, więc tymczasowe zastąpienie \n
go innym znakiem jest czymś, czego chciałbym uniknąć, jeśli to możliwe.
Jakieś dobre pomysły, ktoś?
źródło
--unbuffered
?--unbuffered
$!
zrobić?$!
jest. Spodziewam się, że będzie to wymagało dużo pamięci.sed
nie jest właściwym narzędziem w tym przypadku.Odpowiedzi:
To naprawdę jest banalne w Perlu, nie powinieneś tego nienawidzić!
Wyjaśnienie
-i
: edytuj plik na miejscu i utwórz kopię zapasową oryginału o nazwiefile.bak
. Jeśli nie chcesz kopii zapasowej, po prostu użyj jejperl -i -pe
.-pe
: odczytaj plik wejściowy linia po linii i wydrukuj każdą linię po zastosowaniu skryptu podanego jako-e
.s/>\n/>/
: podstawienie, tak jaksed
.A oto
awk
podejście:źródło
awk '{ORS=/>$/?"":"\n"}1'
':a;N;$!ba;s/>\n/>/g'
w swoim pytaniu, zrzekłeś się prawa do narzekań na czytelność! : Pfoo ? bar : baz
konstruktem, ale nie mogłem go uruchomić.perl
Rozwiązanie:Wyjaśnienie
s///
służy do podstawienia łańcucha.(?<=>)
jest wyglądający za wzór.\n
pasuje do nowej linii.Cały wzorzec znaczeń usuwa wszystkie znaki nowej linii, które miały
>
przed nim.źródło
s/>\n/>/
?s/>\K\n//
też działałbyCo powiesz na to:
W przypadku GNU sed możesz również spróbować dodać opcję
-u
(--unbuffered
) zgodnie z pytaniem. GNU sed jest również zadowolony z tego, że jest to prosta liniówka:źródło
\n
jeśli plik się kończy>\n
, ale prawdopodobnie i tak jest preferowane.}
musi być w osobnym wyrażeniu? czy to nie zadziała jako wyrażenie wielowierszowe?b loop\n}
lub,-e 'b loop' -e '}'
ale nie jakob loop;}
i na pewno nieb loop}
dlatego, że}
i;
są poprawne w nazwach etykiet (chociaż nikt przy zdrowych zmysłach nie użyłby go. A to oznacza, że GNU sed nie jest zgodny z POSIX) i}
polecenie należy rozdzielić zb
polecenia.sed
jest zadowolony ze wszystkich powyższych, nawet z--posix
! Standard zawiera również następujące wyrażenia nawiasów klamrowych -The list of sed functions shall be surrounded by braces and separated by <newline>s
. Czy to nie znaczy, że średników należy używać tylko poza nawiasami klamrowymi?>
. Oryginał nigdy go nie miał, zauważył to Stéphane.Powinieneś być w stanie używać
sed
zN
poleceniem, ale sztuczka polega na usunięciu jednej linii z obszaru wzorca za każdym razem, gdy dodajesz kolejny (tak, że obszar wzorca zawsze zawiera tylko 2 kolejne wiersze, zamiast próbować czytać w całości plik) - spróbujEDIT: Po ponownym przeczytaniu Pēteris Krumins' Famous sed jednej wkładki Poradnik wierzę lepszym
sed
rozwiązaniem byłobyktóra dołącza tylko następującą linię w przypadku, gdy jest już
>
dopasowana na końcu i powinna warunkowo zapętlić się z powrotem, aby obsłużyć przypadek kolejnych pasujących linii (jest to Krumin 39. Dołącz linię do następnej, jeśli kończy się odwrotnym ukośnikiem „\” dokładnie z wyjątkiem zamiany>
na\
jako znak łączenia oraz faktu, że znak łączenia jest zachowany na wyjściu.źródło
>
(dotyczy to również GNU)sed
nie zapewnia sposobu emitowania wyjścia bez końcowego nowego wiersza. Twoje podejście przy użyciuN
zasadniczo działa, ale zapisuje niekompletne linie w pamięci, a zatem może się nie powieść, jeśli linie staną się zbyt długie (implanty sed zwykle nie są zaprojektowane do obsługi bardzo długich linii).Zamiast tego możesz użyć awk.
Alternatywnym podejściem jest
tr
zamiana znaku nowej linii na „nudny”, często występujący znak. Przestrzeń może tu działać - wybierz znak, który ma tendencję do pojawiania się w każdej linii lub co najmniej dużej części linii w twoich danych.źródło
sed
nie działa bez bufora 2,5 gigabajta.tr
podejściu - mikeserv, opublikowałeś inne (prawidłowe, ale mniej ogólne) podejście, które również się stosujetr
.co z używaniem ed?
(przez http://wiki.bash-hackers.org/howto/edit-ed )
źródło
Skończyło się na użyciu gsar, jak opisano w tej odpowiedzi w następujący sposób:
źródło
Jest na to wiele sposobów i większość z nich jest naprawdę dobra, ale myślę, że ten jest moim ulubionym:
Lub nawet:
źródło
*
. Tak jak jest teraz, usunie wszelkie puste linie następujące po linii kończącej się na>
. … Hmm. Patrząc wstecz na pytanie, widzę, że jest to trochę niejednoznaczne. Pytanie brzmi: „Chcę usunąć wszystkie znaki nowej linii, które występują po>
…”. Interpretuję to, co oznacza, że>\n\n\n\n\nfoo
należy to zmienić\n\n\n\nfoo
, ale przypuszczam, żefoo
może to być pożądany wynik.printf '>\n>\n\n>>\n>\n>>>\n>\nf\n\nff\n>\n' | tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/'
- co>>>>>>>>>>f\n\nff\n\n
dla mnie daje pierwszą odpowiedź. Jestem jednak ciekawy tego, co robisz, aby to zepsuć, ponieważ chciałbym to naprawić. Co do drugiej kwestii - nie zgadzam się, że jest niejednoznaczna. PO nie prosi, aby usunąć cały>
poprzedzający się\n
ewline, ale zamiast usunąć wszystkie\n
ewlines następujące>
.>\n\n\n\n\n
za pierwszym znakiem jest tylko nowa linia>
; wszyscy inni podążają za nowymi liniami. Zauważ, że sugestia OP „właśnie tego chcę, jeśli tylko zadziała”sed -e 's/>\n/>/g'
, nie byłased -e 's/>\n*/>/g'
.s/>\n/>/
dnia>\n\n\n\n\n
nadal będzie coś, cos/>\n/>/
byłoby edycji.