Załóżmy, że mam plik (nazwij go sample.txt), który wygląda następująco:
Row1,10
Row2,20
Row3,30
Row4,40
Chcę mieć możliwość pracy ze strumieniem z tego pliku, który jest w zasadzie parą kombinacji wszystkich czterech wierszy (więc powinniśmy mieć w sumie 16). Na przykład szukam polecenia przesyłania strumieniowego (tzn. Wydajnego), którego wynikiem jest:
Row1,10 Row1,10
Row1,10 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row2,20 Row1,10
Row1,20 Row2,20
...
Row4,40 Row4,40
Mój przypadek użycia polega na tym, że chcę przesłać strumień danych wyjściowych do innego polecenia (takiego jak awk), aby obliczyć niektóre dane dotyczące tej kombinacji par.
Mam na to sposób w awk, ale martwię się, że użycie bloku END {} oznacza, że zasadniczo przechowuję cały plik w pamięci przed wyjściem. Przykładowy kod:
awk '{arr[$1]=$1} END{for (a in arr){ for (a2 in arr) { print arr[a] " " arr[a2]}}}' samples/rows.txt
Row3,30 Row3,30
Row3,30 Row4,40
Row3,30 Row1,10
Row3,30 Row2,20
Row4,40 Row3,30
Row4,40 Row4,40
Row4,40 Row1,10
Row4,40 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row1,10 Row1,10
Row1,10 Row2,20
Row2,20 Row3,30
Row2,20 Row4,40
Row2,20 Row1,10
Row2,20 Row2,20
Czy istnieje skuteczny sposób przesyłania strumieniowego, aby to zrobić bez konieczności przechowywania pliku w pamięci, a następnie wyprowadzania go do bloku END?
źródło
Odpowiedzi:
Oto jak to zrobić w awk, aby nie musiał przechowywać całego pliku w tablicy. Jest to zasadniczo ten sam algorytm co terdon.
Jeśli chcesz, możesz nawet nadać mu wiele nazw plików w wierszu poleceń i będzie przetwarzał każdy plik niezależnie, łącząc wyniki razem.
W moim systemie działa to około 2/3 czasu rozwiązania perla Terdona.
źródło
Nie jestem pewien, czy jest to lepsze niż robienie tego w pamięci, ale z tym,
sed
któryr
oddziela swój infile dla każdej linii w swoim infile i innym po drugiej stronie potoku na przemian zeH
starą przestrzenią z liniami wejściowymi ...WYNIK
Zrobiłem to w inny sposób. Przechowuje niektóre elementy w pamięci - przechowuje ciąg taki jak:
... dla każdej linii w pliku.
To jest bardzo szybkie. Jest
cat
to plik tyle razy, ile jest linii w pliku do|pipe
. Po drugiej stronie potoku dane wejściowe są scalane z samym plikiem tyle razy, ile jest linii w pliku.case
Rzeczy jest właśnie dla przenośności -yash
izsh
zarówno dodatek jeden element do rozłamu, podczasmksh
iposh
zarówno stracić.ksh
,dash
,busybox
, Ibash
wszystko podzielonego się dokładnie tak, jak wielu dziedzinach jak istnieją zera podany przezprintf
. Jak napisano powyżej, renderuje takie same wyniki dla każdej z wyżej wymienionych powłok na moim komputerze.Jeśli plik jest bardzo długi, mogą występować
$ARGMAX
problemy ze zbyt dużą liczbą argumentów, w którym to przypadku należy wprowadzićxargs
lub podobne.Biorąc pod uwagę to samo wejście, którego użyłem przed wyjściem jest identyczne. Ale gdybym miał zwiększyć ...
To generuje plik prawie identyczny z tym, którego użyłem wcześniej (bez wiersza) - ale w 1000 linii. Możesz sam przekonać się, jak szybko to jest:
Przy 1000 liniach występuje niewielka różnica w wydajności między powłokami -
bash
jest niezmiennie najwolniejsza - ale ponieważ jedyną pracą, którą wykonują, jest generowanie ciągu arg (1000 kopiifilename -
), efekt jest minimalny. Różnica w wydajności międzyzsh
- jak wyżej - ibash
wynosi tutaj setną sekundy.Oto kolejna wersja, która powinna działać dla pliku o dowolnej długości:
Tworzy miękkie łącze do pierwszego argumentu
/tmp
z pół losową nazwą, aby nie rozłączać się z dziwnymi nazwami plików. To ważne, ponieważcat
argony są podawane do niego za pośrednictwem rury za pośrednictwemxargs
.cat
„s wyjście jest zapisywany<&3
podczassed
p
rints każdy wiersz w pierwszej arg tyle razy, ile jest linii w tym pliku - a jej scenariusz jest także podawany do niego rurą. Ponowniepaste
łączy dane wejściowe, ale tym razem wymaga tylko dwóch argumentów-
dla standardowego wejścia i nazwy łącza/dev/fd/3
.To ostatnie -
/dev/fd/[num]
link - powinno działać na każdym systemie linux i wielu innych oprócz, ale jeśli nie tworzy nazwanego potokumkfifo
i używanie go zamiast tego powinno również działać.Ostatnią rzeczą, jaką robi, jest
rm
miękkie łącze, które tworzy przed wyjściem.Ta wersja jest jeszcze szybsza w moim systemie. Wydaje mi się, że dzieje się tak, ponieważ chociaż uruchamia więcej aplikacji, natychmiast przekazuje im swoje argumenty - a zanim najpierw ułożył je wszystkie w stos.
źródło
ctrl+v; ctrl+j
aby uzyskać nowe wiersze.. ./file; fn_name
w takim przypadku.Cóż, zawsze możesz to zrobić w swojej powłoce:
Jest znacznie wolniejszy niż twoje
awk
rozwiązanie (na moim komputerze zajęło to około 11 sekund na 1000 linii, w porównaniu do około 0,3 sekundyawk
), ale przynajmniej nie ma więcej niż kilku linii w pamięci.Pętla powyżej działa dla bardzo prostych danych, które masz w swoim przykładzie. Dusi się na odwrotnych ukośnikach i zjada spacje końcowe i wiodące. Bardziej niezawodna wersja tego samego jest:
Innym wyborem jest użycie
perl
zamiast tego:Powyższy skrypt odczyta każdy wiersz pliku wejściowego (
-ln
), zapisze go jako$l
,sample.txt
ponownie otworzy i wydrukuje każdy wiersz wraz z$l
. Wynikiem są wszystkie kombinacje par, podczas gdy tylko 2 linie są zawsze przechowywane w pamięci. W moim systemie zajęło to tylko około0.6
sekund na 1000 linii.źródło
echo
może to być problem. To, co napisałem (dodałemprintf
teraz), powinno działać z nimi wszystkimi, prawda? Co dowhile
pętli, dlaczego? Co jest nie tak zwhile read f; do ..; done < file
? Na pewno nie sugerujeszfor
pętli! Jaka jest inna alternatywa?Z
zsh
:$^a
na tablicy włącza interpretację nawiasów klamrowych (np. in{elt1,elt2}
) dla tablicy.źródło
Możesz skompilować ten kod c ++, aby uzyskać dość szybkie wyniki.
Wykonuje się w około 0,19 - 0,27 sekundy na pliku linii 1000.
Obecnie odczytuje
10000
wiersze do pamięci (aby przyspieszyć drukowanie do ekranu), co gdybyś miał1000
znaki w wierszu, zużyłoby mniej niż10mb
pamięć, co nie sądzę, że stanowiłoby problem. Możesz jednak całkowicie usunąć tę sekcję i po prostu wydrukować bezpośrednio na ekranie, jeśli spowoduje to problem.Możesz skompilować za pomocą
g++ -o "NAME" "NAME.cpp"
Where gdzie
NAME
jest nazwa pliku do zapisania iNAME.cpp
jest plikiem, w którym zapisany jest ten kodCTEST.cpp:
Demonstracja
źródło
Pole 2 jest puste i równe dla wszystkich elementów w pliku.txt, więc
join
połączy każdy element ze wszystkimi innymi: w rzeczywistości oblicza iloczyn kartezjański.źródło
Jedną z opcji w Pythonie jest mapowanie pamięci pliku i skorzystanie z faktu, że biblioteka wyrażeń regularnych Python może pracować bezpośrednio z plikami mapowanymi w pamięci. Chociaż wygląda to na uruchamianie zagnieżdżonych pętli nad plikiem, mapowanie pamięci zapewnia, że system operacyjny optymalnie wykorzystuje dostępną fizyczną pamięć RAM
Alternatywnie szybkie rozwiązanie w Pythonie, chociaż wydajność pamięci może nadal stanowić problem
źródło
W bash ksh powinien również działać, używając tylko wbudowanych powłok:
Zauważ, że chociaż przechowuje on cały plik w pamięci w zmiennej powłoki, potrzebuje tylko jednego dostępu do odczytu.
źródło
sed
rozwiązanie.Wyjaśnienie:
sed 'r file2' file1
- przeczytaj całą zawartość pliku file2 dla każdego wiersza pliku1.1~i
oznacza linię 1, następnie linię 1 + i, 1 + 2 * i, 1 + 3 * i itd. Dlatego1~$((line_num + 1)){h;d}
oznaczah
starą szpiczastą linię do bufora,d
usuwając przestrzeń wzoru i rozpoczynając nowy cykl.'G;s/(.*)\n(.*)/\2 \1/'
- dla wszystkich linii, z wyjątkiem wybranych w poprzednim kroku, wykonaj next:G
et linia z bufora wstrzymania i dołącz ją do bieżącej linii. Następnie zamień miejsca linii. Byłcurrent_line\nbuffer_line\n
, stał siębuffer_line\ncurrent_line\n
Wynik
źródło