2017-01-27 44 views
5

Próbuję zrobić narzędzie podobne do narzędzia, które używa Rebol 3 do przetwarzania większych plików tekstowych za pomocą potoków i narzędzi Bash. Mam problem z odczytaniem linii STDIN po linii w Rebol 3?Rebol 3: czytanie STDIN wydajnie linia po linii (do tworzenia narzędzia awk)

Na przykład ta komenda powłoki produkuje 3 linie:

$ (echo "first line" ; echo "second line" ; echo "third line") 
first line 
second line 
third line 

Ale wejście słowo w REBOL czyta wszystkie 3 linie w tym samym czasie. Spodziewam się, że zatrzyma się na linii nowej, ponieważ zatrzymuje się, jeśli interaktywnie używasz danych wejściowych.

r3 --do 'while [ x: input ] [ if empty? x [ break ] print x print "***" ]' 
abcdef 
abcdef 
*** 
blabla 
blabla 
*** 

Ale kiedy uruchomię to wszystko razem, odczyta cały sygnał wejściowy na raz. Mógłbym przeczytać go od razu i podzielić na linie, ale chcę, żeby działał w sposób "streamingowy", jak zwykle cat w wielu 1000-sekundach linii.

$ (echo "first line" ; echo "second line" ; echo "third line") \ 
    | r3 --do 'while [ x: input ] [ if empty? x [ break ] print x print "***" ]' 
first linesecond linethird line 
*** 

ja również spojrzał na źródło wejścia zrobić podobną funkcję. Mogłem odczytać postać na znak w pętli podczas i sprawdzić dla znaków nowej linii, ale to nie wydaje się skuteczne.

Odpowiedz

4

Wyjaśniałem to i wygląda na to, że działa dobrze nawet na dużych, 10000 liniach. Można go jednak napisać bardziej elegancko i poprawić.

Funkcja r3awk trwa STDIN i blok kodu, który to wykonuje jedną linię, wiążący zmienną linię do niego:

r3awk: func [ code /local a lines line partial ] [ 
    partial: copy "" 
    lines: read/lines/string system/ports/input 
    while [ not empty? lines ] [ 
     lines/1: rejoin [ partial lines/1 ] 
     partial: pull lines 
     foreach line lines [ 
      do bind code 'line 
     ] 
     if error? try [ lines: read/lines/string system/ports/input ] [ lines: copy [] ] 
    ] 
    line: partial 
    do bind code 'line 
]  

Działa to tak. odczyt/linie odczytuje liczbę znaków ze strumienia i zwraca blok linii. Za każdym razem, gdy jest wywoływana, odczytuje kolejną porcję takich znaków, więc wszystko jest zapakowane w pętlę while. Kod przetwarza (robi blok kodu) jako pętle while (nie na końcu).

Partia znaków nie kończy się na linii nowej, więc ostatnia linia to częściowa za każdym razem. I tak jest w pierwszym wierszu następnej partii, dlatego łączy je razem. Na końcu musi przetworzyć ostatnią (tym razem nie-częściową) linię. Spróbuj jest tam, ponieważ niektóre linie powodowały błędy kodowania utf.

Może być używany jak to w wierszu poleceń:

(echo "first line" ; echo "second line" ; echo "third line") | \ 
r3 --import utils.r --do 'r3awk [ parse line [ copy x to space (print x) ] ]' 
first 
second 
third 

Rzeczy do poprawy: uczynić funkcję generalnie lepsze, deduplikuj jakiś kod. Sprawdź, co się stanie, jeśli odczyt/linie zakończą się dokładnie na linii nowej linii.

+0

Dobre znaleźć o 'odczytu/lines' buforowanie na stdin. Jednak nie działa to w pełni na MacOS (OSX) :(Zamiast 'block!' Zwraca 'binary!' Z 34815 bajtów (dopóki STDIN nie jest wyczerpany) NB. W rzeczywistości '/ lines' (i '/ string') nic nie robią na MacOS :( – draegtun

3

Znalazłem ten sam problem z input kilka lat temu. Nie sądzę, że jest to planowana zmiana, ale raczej niekompletna implementacja (dotknij drewna!).

Oto funkcja obejścia, którą napisałem w tym czasie (co sprawdziło się dobrze na MacOS & Linux).

input-line: function [ 
    {Return next line (string!) from STDIN. Returns NONE when nothing left} 
    /part size [integer!] "Internal read/part (buffer) size" 
    ][ 
    buffer: {} ;; static 
    if none? part [size: 1024] 

    forever [ 
     if f: find buffer newline [ 
      remove f ;; chomp newline (NB. doesn't cover Windows CRLF?) 
      break 
     ] 

     if empty? data: read/part system/ports/input size [ 
      f: length? buffer 
      break 
     ] 

     append buffer to-string data 
    ] 

    unless all [empty? data empty? buffer] [take/part buffer f] 
] 

Przykład użycia:

while [not none? line: input-line] [ 
    ;; do something with LINE of data from STDIN 
]